Skip to content

Commit

Permalink
feat(avm): Add TransactionFee opcode to simulator (#6210)
Browse files Browse the repository at this point in the history
Adds the TransactionFee opcode to the AVM simulator, which retrieves the
environment's computed transactionFee.
  • Loading branch information
spalladino authored May 6, 2024
1 parent 9fd4f39 commit fcac844
Show file tree
Hide file tree
Showing 17 changed files with 89 additions and 27 deletions.
2 changes: 2 additions & 0 deletions avm-transpiler/src/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub enum AvmOpcode {
SENDER,
FEEPERL2GAS,
FEEPERDAGAS,
TRANSACTIONFEE,
CONTRACTCALLDEPTH,
CHAINID,
VERSION,
Expand Down Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions avm-transpiler/src/transpile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ enum class OpCode : uint8_t {
SENDER,
FEEPERL2GAS,
FEEPERDAGAS,
TRANSACTIONFEE,
CONTRACTCALLDEPTH,
// Execution Environment - Globals
CHAINID,
Expand Down
1 change: 1 addition & 0 deletions docs/docs/protocol-specs/public-vm/_nested-context.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions docs/docs/protocol-specs/public-vm/avm-circuit.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ AvmSessionInputs {
contractCallDepth: field,
isStaticCall: boolean,
isDelegateCall: boolean,
transactionFee: field,
// Initializes Machine State
l2GasLeft: field,
daGasLeft: field,
Expand Down
44 changes: 24 additions & 20 deletions docs/docs/protocol-specs/public-vm/context.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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` |
Expand All @@ -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; <calldata-length>]` | |

| 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; <calldata-length>]` | |

## 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; <output-length>]` | |

| Field | Type | Description |
| -------- | -------------------------- | ----------- |
| reverted | `boolean` | |
| output | `[field; <output-length>]` | |

## Context initialization

Expand Down Expand Up @@ -117,4 +121,4 @@ INITIAL_CONTRACT_CALL_RESULTS = ContractCallResults {
import NestedContext from "./_nested-context.md";

<NestedContext />
<NestedContext />
19 changes: 19 additions & 0 deletions docs/src/preprocess/InstructionSet/InstructionSet.js
Original file line number Diff line number Diff line change
Expand Up @@ -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`",
Expand Down
6 changes: 4 additions & 2 deletions noir-projects/aztec-nr/aztec/src/context/avm_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {}

Expand Down
2 changes: 1 addition & 1 deletion noir-projects/aztec-nr/aztec/src/context/interface.nr
Original file line number Diff line number Diff line change
Expand Up @@ -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<T,N,M>(&mut self, log: T);
Expand All @@ -54,7 +55,6 @@ trait PublicContextInterface {
args: [Field]
) -> FunctionReturns<RETURNS_COUNT>;
fn nullifier_exists(self, unsiloed_nullifier: Field, address: AztecAddress) -> bool;
fn transaction_fee(self) -> Field;
}

struct PrivateCallInterface<T> {
Expand Down
5 changes: 2 additions & 3 deletions noir-projects/aztec-nr/aztec/src/context/public_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -358,7 +357,7 @@ fn emit_unencrypted_log_oracle<T>(
_contract_address: AztecAddress,
_event_selector: Field,
_message: T,
_counter: u32,
_counter: u32
) -> Field {}

struct FunctionReturns<N> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
1 change: 1 addition & 0 deletions yarn-project/simulator/src/avm/avm_gas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export const GasCosts: Record<Opcode, Gas | typeof DynamicGasCost> = {
[Opcode.SENDER]: TemporaryDefaultGasCost,
[Opcode.FEEPERL2GAS]: TemporaryDefaultGasCost,
[Opcode.FEEPERDAGAS]: TemporaryDefaultGasCost,
[Opcode.TRANSACTIONFEE]: TemporaryDefaultGasCost,
[Opcode.CONTRACTCALLDEPTH]: TemporaryDefaultGasCost,
[Opcode.CHAINID]: TemporaryDefaultGasCost,
[Opcode.VERSION]: TemporaryDefaultGasCost,
Expand Down
5 changes: 5 additions & 0 deletions yarn-project/simulator/src/avm/avm_simulator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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([
Expand Down
9 changes: 9 additions & 0 deletions yarn-project/simulator/src/avm/opcodes/environment_getters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
StorageAddress,
Sub,
Timestamp,
TransactionFee,
Version,
Xor,
} from '../opcodes/index.js';
Expand Down Expand Up @@ -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],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export enum Opcode {
SENDER,
FEEPERL2GAS,
FEEPERDAGAS,
TRANSACTIONFEE,
CONTRACTCALLDEPTH,
CHAINID,
VERSION,
Expand Down

0 comments on commit fcac844

Please sign in to comment.