From 9c4cd8ce991af4ac52176f39966434df10c5492c Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Fri, 3 May 2024 18:26:05 -0300 Subject: [PATCH 1/3] feat(avm): Add TransactionFee opcode to simulator 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 | 3 ++ 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 + 14 files changed, 80 insertions(+), 21 deletions(-) diff --git a/avm-transpiler/src/opcodes.rs b/avm-transpiler/src/opcodes.rs index d74ce462db50..2b63c8e987ea 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 9dcdc031c914..ecbc8f16f0c7 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 9313cee1d777..2a4dd1138e94 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 cf1ec253bb03..2b24cff09f22 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 4eeddefa92a0..146557a1af38 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 f9a4a6b45e3a..d770462befe2 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 e9059b096577..a94e3b933ab4 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 016e4860c77c..d98ce6a1b2d6 100644 --- a/noir-projects/aztec-nr/aztec/src/context/avm_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/avm_context.nr @@ -219,6 +219,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/yarn-project/simulator/src/avm/avm_gas.ts b/yarn-project/simulator/src/avm/avm_gas.ts index 7b47e6c626a9..8f140ed03e1f 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 4df5ccbbbf27..a625e63697fc 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 2fc5ffda5b0e..00d23a0ae1de 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 e1182eff12dc..2ecaacb067f7 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 6345715bacee..0d2866ee9657 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 d8bec39412c5..569ad1d7edaa 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, From 98b536c06ddf245fd69d7995f63192d18096e186 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Fri, 3 May 2024 18:53:26 -0300 Subject: [PATCH 2/3] Add tx_fee to public_context --- noir-projects/aztec-nr/aztec/src/context/avm_context.nr | 3 +-- noir-projects/aztec-nr/aztec/src/context/interface.nr | 1 + noir-projects/aztec-nr/aztec/src/context/public_context.nr | 5 ++--- .../noir-contracts/contracts/avm_test_contract/src/main.nr | 5 +++++ 4 files changed, 9 insertions(+), 5 deletions(-) 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 d98ce6a1b2d6..30097e522f12 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 { diff --git a/noir-projects/aztec-nr/aztec/src/context/interface.nr b/noir-projects/aztec-nr/aztec/src/context/interface.nr index 5051d98511b7..9ae267fd9831 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); 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 0e7e9435105d..a410a4accb17 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 450a4ed01e34..03b9f8912dab 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() From 9a44961a0b9e6504d2c36d31a5b5f8a43bb34177 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Mon, 6 May 2024 09:30:04 -0300 Subject: [PATCH 3/3] Fix bad merge --- noir-projects/aztec-nr/aztec/src/context/interface.nr | 1 - 1 file changed, 1 deletion(-) diff --git a/noir-projects/aztec-nr/aztec/src/context/interface.nr b/noir-projects/aztec-nr/aztec/src/context/interface.nr index 9ae267fd9831..b0fa94a211ec 100644 --- a/noir-projects/aztec-nr/aztec/src/context/interface.nr +++ b/noir-projects/aztec-nr/aztec/src/context/interface.nr @@ -55,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 {