From fcac84451f657bb4a70c496538b443dda5bc961e Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Mon, 6 May 2024 13:28:18 -0300 Subject: [PATCH] feat(avm): Add TransactionFee opcode to simulator (#6210) Adds the TransactionFee opcode to the AVM simulator, which retrieves the environment's computed transactionFee. --- avm-transpiler/src/opcodes.rs | 2 + avm-transpiler/src/transpile.rs | 1 + .../barretenberg/vm/avm_trace/avm_opcode.hpp | 1 + .../public-vm/_nested-context.md | 1 + .../protocol-specs/public-vm/avm-circuit.md | 1 + .../docs/protocol-specs/public-vm/context.mdx | 44 ++++++++++--------- .../InstructionSet/InstructionSet.js | 19 ++++++++ .../aztec-nr/aztec/src/context/avm_context.nr | 6 ++- .../aztec-nr/aztec/src/context/interface.nr | 2 +- .../aztec/src/context/public_context.nr | 5 +-- .../contracts/avm_test_contract/src/main.nr | 5 +++ yarn-project/simulator/src/avm/avm_gas.ts | 1 + .../simulator/src/avm/avm_simulator.test.ts | 5 +++ .../avm/opcodes/environment_getters.test.ts | 11 ++++- .../src/avm/opcodes/environment_getters.ts | 9 ++++ .../serialization/bytecode_serialization.ts | 2 + .../instruction_serialization.ts | 1 + 17 files changed, 89 insertions(+), 27 deletions(-) diff --git a/avm-transpiler/src/opcodes.rs b/avm-transpiler/src/opcodes.rs index d74ce462db5..2b63c8e987e 100644 --- a/avm-transpiler/src/opcodes.rs +++ b/avm-transpiler/src/opcodes.rs @@ -24,6 +24,7 @@ pub enum AvmOpcode { SENDER, FEEPERL2GAS, FEEPERDAGAS, + TRANSACTIONFEE, CONTRACTCALLDEPTH, CHAINID, VERSION, @@ -100,6 +101,7 @@ impl AvmOpcode { AvmOpcode::SENDER => "SENDER", AvmOpcode::FEEPERL2GAS => "FEEPERL2GAS", AvmOpcode::FEEPERDAGAS => "FEEPERDAGAS", + AvmOpcode::TRANSACTIONFEE => "TRANSACTIONFEE", AvmOpcode::CONTRACTCALLDEPTH => "CONTRACTCALLDEPTH", // Execution Environment - Globals AvmOpcode::CHAINID => "CHAINID", diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs index 9dcdc031c91..ecbc8f16f0c 100644 --- a/avm-transpiler/src/transpile.rs +++ b/avm-transpiler/src/transpile.rs @@ -743,6 +743,7 @@ fn handle_getter_instruction( "avmOpcodeSender" => AvmOpcode::SENDER, "avmOpcodeFeePerL2Gas" => AvmOpcode::FEEPERL2GAS, "avmOpcodeFeePerDaGas" => AvmOpcode::FEEPERDAGAS, + "avmOpcodeTransactionFee" => AvmOpcode::TRANSACTIONFEE, "avmOpcodeChainId" => AvmOpcode::CHAINID, "avmOpcodeVersion" => AvmOpcode::VERSION, "avmOpcodeBlockNumber" => AvmOpcode::BLOCKNUMBER, diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_opcode.hpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_opcode.hpp index 9313cee1d77..2a4dd1138e9 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_opcode.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_opcode.hpp @@ -43,6 +43,7 @@ enum class OpCode : uint8_t { SENDER, FEEPERL2GAS, FEEPERDAGAS, + TRANSACTIONFEE, CONTRACTCALLDEPTH, // Execution Environment - Globals CHAINID, diff --git a/docs/docs/protocol-specs/public-vm/_nested-context.md b/docs/docs/protocol-specs/public-vm/_nested-context.md index cf1ec253bb0..2b24cff09f2 100644 --- a/docs/docs/protocol-specs/public-vm/_nested-context.md +++ b/docs/docs/protocol-specs/public-vm/_nested-context.md @@ -19,6 +19,7 @@ nestedExecutionEnvironment = ExecutionEnvironment { storageAddress: isDelegateCall ? context.storageAddress : M[addrOffset], feePerL2Gas: context.environment.feePerL2Gas, feePerDaGas: context.environment.feePerDaGas, + transactionFee: context.environment.transactionFee, contractCallDepth: context.contractCallDepth + 1, contractCallPointer: context.worldStateAccessTrace.contractCalls.length + 1, globals: context.globals, diff --git a/docs/docs/protocol-specs/public-vm/avm-circuit.md b/docs/docs/protocol-specs/public-vm/avm-circuit.md index 4eeddefa92a..146557a1af3 100644 --- a/docs/docs/protocol-specs/public-vm/avm-circuit.md +++ b/docs/docs/protocol-specs/public-vm/avm-circuit.md @@ -187,6 +187,7 @@ AvmSessionInputs { contractCallDepth: field, isStaticCall: boolean, isDelegateCall: boolean, + transactionFee: field, // Initializes Machine State l2GasLeft: field, daGasLeft: field, diff --git a/docs/docs/protocol-specs/public-vm/context.mdx b/docs/docs/protocol-specs/public-vm/context.mdx index f9a4a6b45e3..d770462befe 100644 --- a/docs/docs/protocol-specs/public-vm/context.mdx +++ b/docs/docs/protocol-specs/public-vm/context.mdx @@ -7,8 +7,9 @@ Many terms and definitions here are borrowed from the [Ethereum Yellow Paper](ht An **execution context** contains the information and state relevant to a contract call's execution. When a contract call is made, an execution context is [initialized](#context-initialization) before the contract code's execution begins. #### _AvmContext_ + | Field | Type | -| --- | --- | +| --------------------------------------------------------- | ----------------------- | | environment | `ExecutionEnvironment` | | [machineState](./state#machine-state) | `MachineState` | | [worldState](./state#avm-world-state) | `AvmWorldState` | @@ -21,30 +22,33 @@ An **execution context** contains the information and state relevant to a contra A context's **execution environment** remains constant throughout a contract call's execution. When a contract call initializes its execution context, it [fully specifies the execution environment](#context-initialization). ### _ExecutionEnvironment_ -| Field | Type | Description | -| --- | --- | --- | -| address | `AztecAddress` | | -| storageAddress | `AztecAddress` | | -| sender | `AztecAddress` | | -| portal | `EthAddress` | | -| feePerL2Gas | `field` | | -| feePerDaGas | `field` | | -| contractCallDepth | `field` | Depth of the current call (how many nested calls deep is it). | -| contractCallPointer | `field` | Uniquely identifies each contract call processed by an AVM session. An initial call is assigned pointer value of 1 (expanded on in the AVM circuit section's ["Call Pointer"](./avm-circuit#call-pointer) subsection). | -| globals | `PublicGlobalVariables` | | -| isStaticCall | `boolean` | | -| isDelegateCall | `boolean` | | -| calldata | `[field; ]` | | + +| Field | Type | Description | +| ------------------- | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| address | `AztecAddress` | | +| storageAddress | `AztecAddress` | | +| sender | `AztecAddress` | | +| portal | `EthAddress` | | +| feePerL2Gas | `field` | | +| feePerDaGas | `field` | | +| transactionFee | `field` | Computed transaction fee based on gas fees, inclusion fee, and gas usage. Zero in all phases but teardown. | +| contractCallDepth | `field` | Depth of the current call (how many nested calls deep is it). | +| contractCallPointer | `field` | Uniquely identifies each contract call processed by an AVM session. An initial call is assigned pointer value of 1 (expanded on in the AVM circuit section's ["Call Pointer"](./avm-circuit#call-pointer) subsection). | +| globals | `PublicGlobalVariables` | | +| isStaticCall | `boolean` | | +| isDelegateCall | `boolean` | | +| calldata | `[field; ]` | | ## Contract Call Results When a contract call halts, it sets the context's **contract call results** to communicate results to the caller. ### _ContractCallResults_ -| Field | Type | Description | -| --- | --- | --- | -| reverted | `boolean` | | -| output | `[field; ]` | | + +| Field | Type | Description | +| -------- | -------------------------- | ----------- | +| reverted | `boolean` | | +| output | `[field; ]` | | ## Context initialization @@ -117,4 +121,4 @@ INITIAL_CONTRACT_CALL_RESULTS = ContractCallResults { import NestedContext from "./_nested-context.md"; - \ No newline at end of file + diff --git a/docs/src/preprocess/InstructionSet/InstructionSet.js b/docs/src/preprocess/InstructionSet/InstructionSet.js index e9059b09657..a94e3b933ab 100644 --- a/docs/src/preprocess/InstructionSet/InstructionSet.js +++ b/docs/src/preprocess/InstructionSet/InstructionSet.js @@ -587,6 +587,25 @@ const INSTRUCTION_SET_RAW = [ "Tag checks": "", "Tag updates": "`T[dstOffset] = u32`", }, + { + id: "transactionfee", + Name: "`TRANSACTIONFEE`", + Category: "Execution Environment", + Flags: [{ name: "indirect", description: INDIRECT_FLAG_DESCRIPTION }], + Args: [ + { + name: "dstOffset", + description: + "memory offset specifying where to store operation's result", + }, + ], + Expression: "`M[dstOffset] = context.environment.transactionFee`", + Summary: + "Get the computed transaction fee during teardown phase, zero otherwise", + Details: "", + "Tag checks": "", + "Tag updates": "`T[dstOffset] = u32`", + }, { id: "contractcalldepth", Name: "`CONTRACTCALLDEPTH`", diff --git a/noir-projects/aztec-nr/aztec/src/context/avm_context.nr b/noir-projects/aztec-nr/aztec/src/context/avm_context.nr index 016e4860c77..30097e522f1 100644 --- a/noir-projects/aztec-nr/aztec/src/context/avm_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/avm_context.nr @@ -79,8 +79,7 @@ impl PublicContextInterface for AvmContext { } fn transaction_fee(self) -> Field { - assert(false, "'transaction_fee' not implemented!"); - 0 + transaction_fee() } fn nullifier_exists(self, unsiloed_nullifier: Field, address: AztecAddress) -> bool { @@ -219,6 +218,9 @@ fn fee_per_l2_gas() -> Field {} #[oracle(avmOpcodeFeePerDaGas)] fn fee_per_da_gas() -> Field {} +#[oracle(avmOpcodeTransactionFee)] +fn transaction_fee() -> Field {} + #[oracle(avmOpcodeChainId)] fn chain_id() -> Field {} diff --git a/noir-projects/aztec-nr/aztec/src/context/interface.nr b/noir-projects/aztec-nr/aztec/src/context/interface.nr index 5051d98511b..b0fa94a211e 100644 --- a/noir-projects/aztec-nr/aztec/src/context/interface.nr +++ b/noir-projects/aztec-nr/aztec/src/context/interface.nr @@ -30,6 +30,7 @@ trait PublicContextInterface { fn fee_recipient(self) -> AztecAddress; fn fee_per_da_gas(self) -> Field; fn fee_per_l2_gas(self) -> Field; + fn transaction_fee(self) -> Field; fn message_portal(&mut self, recipient: EthAddress, content: Field); fn consume_l1_to_l2_message(&mut self, content: Field, secret: Field, sender: EthAddress, leaf_index: Field); fn emit_unencrypted_log(&mut self, log: T); @@ -54,7 +55,6 @@ trait PublicContextInterface { args: [Field] ) -> FunctionReturns; fn nullifier_exists(self, unsiloed_nullifier: Field, address: AztecAddress) -> bool; - fn transaction_fee(self) -> Field; } struct PrivateCallInterface { diff --git a/noir-projects/aztec-nr/aztec/src/context/public_context.nr b/noir-projects/aztec-nr/aztec/src/context/public_context.nr index 0e7e9435105..a410a4accb1 100644 --- a/noir-projects/aztec-nr/aztec/src/context/public_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/public_context.nr @@ -72,7 +72,7 @@ impl PublicContext { unencrypted_logs_hashes: BoundedVec::new(), unencrypted_log_preimages_length: 0, historical_header: inputs.historical_header, - prover_address: AztecAddress::zero(), + prover_address: AztecAddress::zero() } } @@ -144,7 +144,6 @@ impl PublicContext { } pub fn finish(self) -> PublicCircuitPublicInputs { - // Compute the public call stack hashes let pub_circuit_pub_inputs = PublicCircuitPublicInputs { call_context: self.inputs.call_context, // Done @@ -358,7 +357,7 @@ fn emit_unencrypted_log_oracle( _contract_address: AztecAddress, _event_selector: Field, _message: T, - _counter: u32, + _counter: u32 ) -> Field {} struct FunctionReturns { diff --git a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr index 450a4ed01e3..03b9f8912da 100644 --- a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr @@ -217,6 +217,11 @@ contract AvmTest { context.fee_per_da_gas() } + #[aztec(public-vm)] + fn get_transaction_fee() -> pub Field { + context.transaction_fee() + } + #[aztec(public-vm)] fn get_chain_id() -> pub Field { context.chain_id() diff --git a/yarn-project/simulator/src/avm/avm_gas.ts b/yarn-project/simulator/src/avm/avm_gas.ts index 7b47e6c626a..8f140ed03e1 100644 --- a/yarn-project/simulator/src/avm/avm_gas.ts +++ b/yarn-project/simulator/src/avm/avm_gas.ts @@ -78,6 +78,7 @@ export const GasCosts: Record = { [Opcode.SENDER]: TemporaryDefaultGasCost, [Opcode.FEEPERL2GAS]: TemporaryDefaultGasCost, [Opcode.FEEPERDAGAS]: TemporaryDefaultGasCost, + [Opcode.TRANSACTIONFEE]: TemporaryDefaultGasCost, [Opcode.CONTRACTCALLDEPTH]: TemporaryDefaultGasCost, [Opcode.CHAINID]: TemporaryDefaultGasCost, [Opcode.VERSION]: TemporaryDefaultGasCost, diff --git a/yarn-project/simulator/src/avm/avm_simulator.test.ts b/yarn-project/simulator/src/avm/avm_simulator.test.ts index 4df5ccbbbf2..a625e63697f 100644 --- a/yarn-project/simulator/src/avm/avm_simulator.test.ts +++ b/yarn-project/simulator/src/avm/avm_simulator.test.ts @@ -199,6 +199,11 @@ describe('AVM simulator: transpiled Noir contracts', () => { await testEnvGetter('feePerDaGas', fee, 'get_fee_per_da_gas'); }); + it('getTransactionFee', async () => { + const fee = new Fr(1); + await testEnvGetter('transactionFee', fee, 'get_transaction_fee'); + }); + it('chainId', async () => { const chainId = new Fr(1); await testEnvGetter('chainId', chainId, 'get_chain_id', /*globalVar=*/ true); diff --git a/yarn-project/simulator/src/avm/opcodes/environment_getters.test.ts b/yarn-project/simulator/src/avm/opcodes/environment_getters.test.ts index 2fc5ffda5b0..00d23a0ae1d 100644 --- a/yarn-project/simulator/src/avm/opcodes/environment_getters.test.ts +++ b/yarn-project/simulator/src/avm/opcodes/environment_getters.test.ts @@ -10,16 +10,25 @@ import { Sender, StorageAddress, Timestamp, + TransactionFee, Version, } from './environment_getters.js'; -type EnvInstruction = typeof FeePerL2Gas | typeof FeePerDAGas | typeof Sender | typeof StorageAddress | typeof Address; +type EnvInstruction = + | typeof FeePerL2Gas + | typeof FeePerDAGas + | typeof Sender + | typeof StorageAddress + | typeof Address + | typeof TransactionFee; + describe.each([ [FeePerL2Gas, 'feePerL2Gas'], [FeePerDAGas, 'feePerDaGas'], [Sender, 'sender'], [StorageAddress, 'storageAddress'], [Address, 'address'], + [TransactionFee, 'transactionFee'], ])('Environment getters instructions', (clsValue: EnvInstruction, key: string) => { it(`${clsValue.name} should (de)serialize correctly`, () => { const buf = Buffer.from([ diff --git a/yarn-project/simulator/src/avm/opcodes/environment_getters.ts b/yarn-project/simulator/src/avm/opcodes/environment_getters.ts index e1182eff12d..2ecaacb067f 100644 --- a/yarn-project/simulator/src/avm/opcodes/environment_getters.ts +++ b/yarn-project/simulator/src/avm/opcodes/environment_getters.ts @@ -59,6 +59,15 @@ export class FeePerDAGas extends EnvironmentGetterInstruction { } } +export class TransactionFee extends EnvironmentGetterInstruction { + static type: string = 'TRANSACTIONFEE'; + static readonly opcode: Opcode = Opcode.TRANSACTIONFEE; + + protected getEnvironmentValue(env: AvmExecutionEnvironment) { + return env.transactionFee; + } +} + export class ChainId extends EnvironmentGetterInstruction { static type: string = 'CHAINID'; static readonly opcode: Opcode = Opcode.CHAINID; diff --git a/yarn-project/simulator/src/avm/serialization/bytecode_serialization.ts b/yarn-project/simulator/src/avm/serialization/bytecode_serialization.ts index 6345715bace..0d2866ee965 100644 --- a/yarn-project/simulator/src/avm/serialization/bytecode_serialization.ts +++ b/yarn-project/simulator/src/avm/serialization/bytecode_serialization.ts @@ -46,6 +46,7 @@ import { StorageAddress, Sub, Timestamp, + TransactionFee, Version, Xor, } from '../opcodes/index.js'; @@ -82,6 +83,7 @@ const INSTRUCTION_SET = () => [Sender.opcode, Sender], [FeePerL2Gas.opcode, FeePerL2Gas], [FeePerDAGas.opcode, FeePerDAGas], + [TransactionFee.opcode, TransactionFee], //[Contractcalldepth.opcode, Contractcalldepth], // Execution Environment - Globals [ChainId.opcode, ChainId], diff --git a/yarn-project/simulator/src/avm/serialization/instruction_serialization.ts b/yarn-project/simulator/src/avm/serialization/instruction_serialization.ts index d8bec39412c..569ad1d7eda 100644 --- a/yarn-project/simulator/src/avm/serialization/instruction_serialization.ts +++ b/yarn-project/simulator/src/avm/serialization/instruction_serialization.ts @@ -29,6 +29,7 @@ export enum Opcode { SENDER, FEEPERL2GAS, FEEPERDAGAS, + TRANSACTIONFEE, CONTRACTCALLDEPTH, CHAINID, VERSION,