From ee234153655642ad062d84b12f420866fc3208e7 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Tue, 9 Apr 2024 05:55:57 -0300 Subject: [PATCH] feat(avm): Set gas allowance in public calls (#5567) Allows user to set gas allowance in public calls, defaulting to the gas left in the current frame. - Adds the `(L1|L2|DA)GASLEFT` instructions. - Adds a new `GasOpts` option to the `(Public|Avm)Context.call_public_function` so the user can specify the gas allocation for a nested public call (ignored in public context, only used in avm context). Defaults gas to the result of gasleft, minus a buffer for L2 compute. - Updates the noir interface codegen to handle this new argument. --- avm-transpiler/src/transpile.rs | 3 + .../src/core/libraries/ConstantsGen.sol | 1 + noir-projects/aztec-nr/authwit/src/auth.nr | 9 +- noir-projects/aztec-nr/aztec/src/context.nr | 1 + .../aztec-nr/aztec/src/context/avm_context.nr | 58 +- .../aztec-nr/aztec/src/context/gas.nr | 15 + .../aztec-nr/aztec/src/context/interface.nr | 8 +- .../aztec/src/context/public_context.nr | 11 +- .../contracts/avm_test_contract/src/main.nr | 67 +- .../benchmarking_contract/src/main.nr | 5 +- .../contracts/child_contract/src/main.nr | 17 +- .../src/interface/ContractInstanceDeployer.nr | 65 -- .../contracts/fpc_contract/src/interfaces.nr | 8 +- .../contracts/fpc_contract/src/main.nr | 8 +- .../lending_contract/src/interfaces.nr | 14 +- .../contracts/lending_contract/src/main.nr | 14 +- .../contracts/parent_contract/src/main.nr | 27 +- .../stateful_test_contract/src/main.nr | 10 +- .../contracts/test_contract/src/interface.nr | 581 +++++++++++++++--- .../src/interfaces.nr | 5 +- .../src/token_interface.nr | 11 +- .../uniswap_contract/src/interfaces.nr | 11 +- .../contracts/uniswap_contract/src/main.nr | 4 +- .../crates/types/src/constants.nr | 2 + yarn-project/circuits.js/src/constants.gen.ts | 1 + .../add_noir_compiler_commander_actions.ts | 5 +- yarn-project/noir-compiler/src/cli/codegen.ts | 4 +- .../src/contract-interface-gen/noir.ts | 20 +- .../scripts/generate-noir-interfaces.sh | 17 + .../simulator/src/avm/avm_simulator.test.ts | 20 +- .../simulator/src/avm/avm_simulator.ts | 14 +- .../src/avm/opcodes/context_getters.test.ts | 32 + .../src/avm/opcodes/context_getters.ts | 32 + .../src/avm/opcodes/environment_getters.ts | 85 ++- .../src/avm/opcodes/instruction_impl.ts | 24 + .../serialization/bytecode_serialization.ts | 7 +- 36 files changed, 909 insertions(+), 307 deletions(-) create mode 100644 noir-projects/aztec-nr/aztec/src/context/gas.nr delete mode 100644 noir-projects/noir-contracts/contracts/contract_instance_deployer_contract/src/interface/ContractInstanceDeployer.nr create mode 100755 yarn-project/noir-contracts.js/scripts/generate-noir-interfaces.sh create mode 100644 yarn-project/simulator/src/avm/opcodes/context_getters.test.ts create mode 100644 yarn-project/simulator/src/avm/opcodes/context_getters.ts diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs index 1b97449d81c..f13514c74ed 100644 --- a/avm-transpiler/src/transpile.rs +++ b/avm-transpiler/src/transpile.rs @@ -795,6 +795,9 @@ fn handle_getter_instruction( "avmOpcodeVersion" => AvmOpcode::VERSION, "avmOpcodeBlockNumber" => AvmOpcode::BLOCKNUMBER, "avmOpcodeTimestamp" => AvmOpcode::TIMESTAMP, + "avmOpcodeL1GasLeft" => AvmOpcode::L1GASLEFT, + "avmOpcodeL2GasLeft" => AvmOpcode::L2GASLEFT, + "avmOpcodeDaGasLeft" => AvmOpcode::DAGASLEFT, // "callStackDepth" => AvmOpcode::CallStackDepth, _ => panic!( "Transpiler doesn't know how to process ForeignCall function {:?}", diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index bac287e95a5..072da6e1efe 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -69,6 +69,7 @@ library Constants { uint256 internal constant INITIALIZATION_SLOT_SEPARATOR = 1000_000_000; uint256 internal constant INITIAL_L2_BLOCK_NUM = 1; uint256 internal constant BLOB_SIZE_IN_BYTES = 126976; + uint256 internal constant NESTED_CALL_L2_GAS_BUFFER = 20000; uint256 internal constant MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 15000; uint256 internal constant MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS = 3000; uint256 internal constant MAX_PACKED_BYTECODE_SIZE_PER_UNCONSTRAINED_FUNCTION_IN_FIELDS = 3000; diff --git a/noir-projects/aztec-nr/authwit/src/auth.nr b/noir-projects/aztec-nr/authwit/src/auth.nr index c1c126be9a2..4e97aebe2b3 100644 --- a/noir-projects/aztec-nr/authwit/src/auth.nr +++ b/noir-projects/aztec-nr/authwit/src/auth.nr @@ -2,7 +2,7 @@ use dep::aztec::protocol_types::{ abis::function_selector::FunctionSelector, address::AztecAddress, constants::{GENERATOR_INDEX__AUTHWIT_INNER, GENERATOR_INDEX__AUTHWIT_OUTER}, hash::pedersen_hash }; -use dep::aztec::{context::{PrivateContext, PublicContext, Context}, hash::hash_args_array}; +use dep::aztec::{context::{PrivateContext, PublicContext, Context, gas::GasOpts}, hash::hash_args_array}; global IS_VALID_SELECTOR = 0xabf64ad4; // 4 first bytes of keccak256("IS_VALID()") @@ -21,7 +21,12 @@ pub fn assert_current_call_valid_authwit(context: &mut PrivateContext, on_behalf pub fn assert_current_call_valid_authwit_public(context: &mut PublicContext, on_behalf_of: AztecAddress) { let function_selector = FunctionSelector::from_signature("spend_public_authwit(Field)"); let inner_hash = compute_inner_authwit_hash([context.msg_sender().to_field(), context.selector().to_field(), context.args_hash]); - let result = context.call_public_function(on_behalf_of, function_selector, [inner_hash])[0]; + let result = context.call_public_function( + on_behalf_of, + function_selector, + [inner_hash], + GasOpts::default() + )[0]; assert(result == IS_VALID_SELECTOR, "Message not authorized by account"); } // docs:end:assert_current_call_valid_authwit_public diff --git a/noir-projects/aztec-nr/aztec/src/context.nr b/noir-projects/aztec-nr/aztec/src/context.nr index ce4c7eea3c2..eb4e66512c5 100644 --- a/noir-projects/aztec-nr/aztec/src/context.nr +++ b/noir-projects/aztec-nr/aztec/src/context.nr @@ -5,6 +5,7 @@ mod private_context; mod public_context; mod avm_context; mod interface; +mod gas; use interface::ContextInterface; use private_context::PrivateContext; 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 1be10fa0f40..8f7a0193349 100644 --- a/noir-projects/aztec-nr/aztec/src/context/avm_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/avm_context.nr @@ -1,11 +1,15 @@ -use dep::protocol_types::{address::{AztecAddress, EthAddress}, constants::L1_TO_L2_MESSAGE_LENGTH, header::Header}; +use dep::protocol_types::{ + address::{AztecAddress, EthAddress}, + constants::{L1_TO_L2_MESSAGE_LENGTH, NESTED_CALL_L2_GAS_BUFFER, RETURN_VALUES_LENGTH}, + header::Header +}; use dep::protocol_types::traits::Serialize; use dep::protocol_types::abis::function_selector::FunctionSelector; use dep::protocol_types::abis::public_circuit_public_inputs::PublicCircuitPublicInputs; -use dep::protocol_types::constants::RETURN_VALUES_LENGTH; use crate::context::inputs::avm_context_inputs::AvmContextInputs; use crate::context::interface::ContextInterface; use crate::context::interface::PublicContextInterface; +use crate::context::gas::GasOpts; struct AvmContext { inputs: AvmContextInputs, @@ -54,22 +58,32 @@ impl AvmContext { fn call_public_function_raw( self: &mut Self, - gas: [Field; 3], + gas: GasOpts, contract_address: AztecAddress, temporary_function_selector: Field, args: [Field; ARGS_COUNT] ) -> ([Field; RET_COUNT], u8) { - call(gas, contract_address, args, temporary_function_selector) + call( + gas_for_call(gas), + contract_address, + args, + temporary_function_selector + ) } fn static_call_public_function_raw( self: &mut Self, - gas: [Field; 3], + gas: GasOpts, contract_address: AztecAddress, temporary_function_selector: Field, args: [Field; ARGS_COUNT] ) -> ([Field; RET_COUNT], u8) { - call_static(gas, contract_address, args, temporary_function_selector) + call_static( + gas_for_call(gas), + contract_address, + args, + temporary_function_selector + ) } } @@ -121,12 +135,11 @@ impl PublicContextInterface for AvmContext { self: &mut Self, contract_address: AztecAddress, temporary_function_selector: FunctionSelector, - args: [Field; ARGS_COUNT] + args: [Field; ARGS_COUNT], + gas_opts: GasOpts ) -> [Field; RETURN_VALUES_LENGTH] { - let gas = [/*l1_gas*/10000, /*l2_gas*/10000, /*da_gas*/10000]; - let results = call( - gas, + gas_for_call(gas_opts), contract_address, args, temporary_function_selector.to_field() @@ -142,12 +155,11 @@ impl PublicContextInterface for AvmContext { self: &mut Self, contract_address: AztecAddress, temporary_function_selector: FunctionSelector, - args: [Field; ARGS_COUNT] + args: [Field; ARGS_COUNT], + gas_opts: GasOpts ) -> [Field; RETURN_VALUES_LENGTH] { - let gas = [/*l1_gas*/10000, /*l2_gas*/10000, /*da_gas*/10000]; - let (data_to_return, success): ([Field; RETURN_VALUES_LENGTH], u8) = call_static( - gas, + gas_for_call(gas_opts), contract_address, args, temporary_function_selector.to_field() @@ -203,6 +215,15 @@ impl ContextInterface for AvmContext { } } +// Helper functions +fn gas_for_call(user_gas: GasOpts) -> [Field; 3] { + [ + user_gas.l1_gas.unwrap_or_else(|| l1_gas_left()), + user_gas.l2_gas.unwrap_or_else(|| l2_gas_left() - NESTED_CALL_L2_GAS_BUFFER), + user_gas.da_gas.unwrap_or_else(|| da_gas_left()) + ] +} + // AVM oracles (opcodes) follow, do not use directly. #[oracle(avmOpcodeAddress)] fn address() -> AztecAddress {} @@ -240,6 +261,15 @@ fn block_number() -> Field {} #[oracle(avmOpcodeTimestamp)] fn timestamp() -> u64 {} +#[oracle(avmOpcodeL1GasLeft)] +fn l1_gas_left() -> Field {} + +#[oracle(avmOpcodeL2GasLeft)] +fn l2_gas_left() -> Field {} + +#[oracle(avmOpcodeDaGasLeft)] +fn da_gas_left() -> Field {} + #[oracle(avmOpcodeNoteHashExists)] fn note_hash_exists(note_hash: Field, leaf_index: Field) -> u8 {} diff --git a/noir-projects/aztec-nr/aztec/src/context/gas.nr b/noir-projects/aztec-nr/aztec/src/context/gas.nr new file mode 100644 index 00000000000..346eb089496 --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/context/gas.nr @@ -0,0 +1,15 @@ +struct GasOpts { + l1_gas: Option, + l2_gas: Option, + da_gas: Option, +} + +impl GasOpts { + pub fn default() -> Self { + GasOpts { l1_gas: Option::none(), l2_gas: Option::none(), da_gas: Option::none() } + } + + pub fn new(l1_gas: Field, l2_gas: Field, da_gas: Field) -> Self { + GasOpts { l1_gas: Option::some(l1_gas), l2_gas: Option::some(l2_gas), da_gas: Option::some(da_gas) } + } +} diff --git a/noir-projects/aztec-nr/aztec/src/context/interface.nr b/noir-projects/aztec-nr/aztec/src/context/interface.nr index 29260eb5018..b6973e9e244 100644 --- a/noir-projects/aztec-nr/aztec/src/context/interface.nr +++ b/noir-projects/aztec-nr/aztec/src/context/interface.nr @@ -3,6 +3,8 @@ use dep::protocol_types::{ constants::RETURN_VALUES_LENGTH }; +use crate::context::gas::GasOpts; + trait ContextInterface { fn push_new_note_hash(&mut self, note_hash: Field); fn push_new_nullifier(&mut self, nullifier: Field, nullified_commitment: Field); @@ -34,13 +36,15 @@ trait PublicContextInterface { self: &mut Self, contract_address: AztecAddress, function_selector: FunctionSelector, - args: [Field; ARGS_COUNT] + args: [Field; ARGS_COUNT], + gas_opts: GasOpts ) -> [Field; RETURN_VALUES_LENGTH]; fn static_call_public_function( self: &mut Self, contract_address: AztecAddress, function_selector: FunctionSelector, - args: [Field; ARGS_COUNT] + args: [Field; ARGS_COUNT], + gas_opts: GasOpts ) -> [Field; RETURN_VALUES_LENGTH]; fn delegate_call_public_function( self: &mut Self, 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 c789301005b..46fbdc13ef9 100644 --- a/noir-projects/aztec-nr/aztec/src/context/public_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/public_context.nr @@ -1,5 +1,8 @@ use crate::{ - context::{inputs::PublicContextInputs, interface::ContextInterface, interface::PublicContextInterface}, + context::{ + inputs::PublicContextInputs, interface::ContextInterface, interface::PublicContextInterface, + gas::GasOpts +}, messaging::process_l1_to_l2_message, oracle::{arguments, public_call::call_public_function_internal}, hash::hash_args_array }; @@ -268,7 +271,8 @@ impl PublicContextInterface for PublicContext { self: &mut Self, contract_address: AztecAddress, function_selector: FunctionSelector, - args: [Field; ARGS_COUNT] + args: [Field; ARGS_COUNT], + _gas: GasOpts ) -> [Field; RETURN_VALUES_LENGTH] { let args_hash = hash_args_array(args); assert(args_hash == arguments::pack_arguments(args)); @@ -279,7 +283,8 @@ impl PublicContextInterface for PublicContext { self: &mut Self, contract_address: AztecAddress, function_selector: FunctionSelector, - args: [Field; ARGS_COUNT] + args: [Field; ARGS_COUNT], + _gas: GasOpts ) -> [Field; RETURN_VALUES_LENGTH] { let args_hash = hash_args_array(args); assert(args_hash == arguments::pack_arguments(args)); 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 317d500e4c9..b8a2f65888d 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 @@ -30,6 +30,7 @@ contract AvmTest { address::{AztecAddress, EthAddress}, constants::L1_TO_L2_MESSAGE_LENGTH, contract_instance::ContractInstance }; + use dep::aztec::context::gas::GasOpts; use dep::aztec::oracle::get_contract_instance::{get_contract_instance_avm, get_contract_instance_internal_avm}; use dep::aztec::protocol_types::abis::function_selector::FunctionSelector; use dep::aztec::protocol_types::traits::ToField; @@ -340,12 +341,16 @@ contract AvmTest { #[aztec(public-vm)] fn raw_nested_call_to_add(arg_a: Field, arg_b: Field) -> pub Field { let selector = FunctionSelector::from_signature("add_args_return(Field,Field)").to_field(); - let gas = [/*l1_gas*/10000, /*l2_gas*/10000, /*da_gas*/10000]; // Nested call - let results = context.call_public_function_raw(gas, context.this_address(), selector, [arg_a, arg_b]); + let results = context.call_public_function_raw( + GasOpts::default(), + context.this_address(), + selector, + [arg_a, arg_b] + ); let data_to_return: [Field; 1] = results.0; - // this explicit size ^ is necessary to ensure that ret_size is compile-time + // this explicit size is necessary to ensure that ret_size is compile-time // (ensure the data_to_return is in a HeapArray not a HeapVector) let success: u8 = results.1; @@ -355,14 +360,46 @@ contract AvmTest { add_result } + // Directly call the external call opcode to initiate a nested call to the add function with user-specified gas + #[aztec(public-vm)] + fn raw_nested_call_to_add_with_gas( + arg_a: Field, + arg_b: Field, + l1_gas: Field, + l2_gas: Field, + da_gas: Field + ) -> pub Field { + let selector = FunctionSelector::from_signature("add_args_return(Field,Field)").to_field(); + + // Nested call + let results = context.call_public_function_raw( + GasOpts::new(l1_gas, l2_gas, da_gas), + context.this_address(), + selector, + [arg_a, arg_b] + ); + let data_to_return: [Field; 1] = results.0; + // this explicit size is necessary to ensure that ret_size is compile-time + // (ensure the data_to_return is in a HeapArray not a HeapVector) + let success: u8 = results.1; + + let add_result = data_to_return[0]; + add_result + } + // Use the `call_public_function` wrapper to initiate a nested call to the add function #[aztec(public-vm)] fn nested_call_to_add(arg_a: Field, arg_b: Field) -> pub Field { let selector = FunctionSelector::from_signature("add_args_return(Field,Field)"); // Nested call using standard context interface function - let data_to_return: [Field; RETURN_VALUES_LENGTH] = context.call_public_function(context.this_address(), selector, [arg_a, arg_b]); - // this explicit size ^ is necessary to ensure that ret_size is compile-time + let data_to_return: [Field; RETURN_VALUES_LENGTH] = context.call_public_function( + context.this_address(), + selector, + [arg_a, arg_b], + GasOpts::default() + ); + // this explicit size in the return variable is necessary to ensure that ret_size is compile-time // (ensure the data_to_return is in a HeapArray not a HeapVector) let add_result = data_to_return[0]; @@ -373,9 +410,13 @@ contract AvmTest { #[aztec(public-vm)] fn raw_nested_static_call_to_add(arg_a: Field, arg_b: Field) -> pub (Field, u8) { let selector = FunctionSelector::from_signature("add_args_return(Field,Field)").to_field(); - let gas = [/*l1_gas*/10000, /*l2_gas*/10000, /*da_gas*/10000]; - let (result_data, success): ([Field; 1], u8) = context.static_call_public_function_raw(gas, context.this_address(), selector, [arg_a, arg_b]); + let (result_data, success): ([Field; 1], u8) = context.static_call_public_function_raw( + GasOpts::default(), + context.this_address(), + selector, + [arg_a, arg_b] + ); (result_data[0], success) } @@ -384,10 +425,9 @@ contract AvmTest { #[aztec(public-vm)] fn raw_nested_static_call_to_set_storage() -> pub u8 { let selector = FunctionSelector::from_signature("set_storage_single(Field)").to_field(); - let gas = [/*l1_gas*/10000, /*l2_gas*/10000, /*da_gas*/10000]; let calldata: [Field; 1] = [20]; - let (_data_to_return, success): ([Field; 0], u8) = context.static_call_public_function_raw(gas, context.this_address(), selector, calldata); + let (_data_to_return, success): ([Field; 0], u8) = context.static_call_public_function_raw(GasOpts::default(), context.this_address(), selector, calldata); success } @@ -397,7 +437,12 @@ contract AvmTest { fn nested_static_call_to_add(arg_a: Field, arg_b: Field) -> pub Field { let selector = FunctionSelector::from_signature("add_args_return(Field,Field)"); - let result_data: [Field; RETURN_VALUES_LENGTH] = context.static_call_public_function(context.this_address(), selector, [arg_a, arg_b]); + let result_data: [Field; RETURN_VALUES_LENGTH] = context.static_call_public_function( + context.this_address(), + selector, + [arg_a, arg_b], + GasOpts::default() + ); result_data[0] } @@ -408,6 +453,6 @@ contract AvmTest { let selector = FunctionSelector::from_signature("set_storage_single(Field)"); let calldata: [Field; 1] = [20]; - let _resultData: [Field; RETURN_VALUES_LENGTH] = context.static_call_public_function(context.this_address(), selector, calldata); + let _resultData: [Field; RETURN_VALUES_LENGTH] = context.static_call_public_function(context.this_address(), selector, calldata, GasOpts::default()); } } diff --git a/noir-projects/noir-contracts/contracts/benchmarking_contract/src/main.nr b/noir-projects/noir-contracts/contracts/benchmarking_contract/src/main.nr index 5af1844db39..f909e0290ee 100644 --- a/noir-projects/noir-contracts/contracts/benchmarking_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/benchmarking_contract/src/main.nr @@ -10,7 +10,7 @@ contract Benchmarking { }; use dep::value_note::{utils::{increment, decrement}, value_note::ValueNote}; - use dep::aztec::{context::Context}; + use dep::aztec::{context::{Context, gas::GasOpts}}; #[aztec(storage)] struct Storage { @@ -47,7 +47,8 @@ contract Benchmarking { let _callStackItem1 = context.call_public_function( context.this_address(), FunctionSelector::from_signature("broadcast((Field))"), - [owner.to_field()] + [owner.to_field()], + GasOpts::default() ); } diff --git a/noir-projects/noir-contracts/contracts/child_contract/src/main.nr b/noir-projects/noir-contracts/contracts/child_contract/src/main.nr index b8a7da92d7a..3d8cb921c37 100644 --- a/noir-projects/noir-contracts/contracts/child_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/child_contract/src/main.nr @@ -3,7 +3,8 @@ contract Child { use dep::aztec::prelude::{AztecAddress, FunctionSelector, PublicMutable, PrivateSet, PrivateContext, emit_unencrypted_log}; use dep::aztec::{ - context::{PublicContext, Context}, protocol_types::{abis::{call_context::CallContext}}, + context::{PublicContext, Context, gas::GasOpts}, + protocol_types::{abis::{call_context::CallContext}}, note::{note_getter_options::NoteGetterOptions, note_header::NoteHeader} }; use dep::value_note::value_note::ValueNote; @@ -91,7 +92,12 @@ contract Child { #[aztec(public)] fn set_value_twice_with_nested_first() { let pub_set_value_selector = FunctionSelector::from_signature("pub_set_value(Field)"); - let _ret = context.call_public_function(context.this_address(), pub_set_value_selector, [10]); + let _ret = context.call_public_function( + context.this_address(), + pub_set_value_selector, + [10], + GasOpts::default() + ); storage.current_value.write(20); emit_unencrypted_log(&mut context, 20); @@ -103,6 +109,11 @@ contract Child { emit_unencrypted_log(&mut context, 20); let pub_set_value_selector = FunctionSelector::from_signature("pub_set_value(Field)"); - let _ret = context.call_public_function(context.this_address(), pub_set_value_selector, [10]); + let _ret = context.call_public_function( + context.this_address(), + pub_set_value_selector, + [10], + GasOpts::default() + ); } } diff --git a/noir-projects/noir-contracts/contracts/contract_instance_deployer_contract/src/interface/ContractInstanceDeployer.nr b/noir-projects/noir-contracts/contracts/contract_instance_deployer_contract/src/interface/ContractInstanceDeployer.nr deleted file mode 100644 index 810cdccc248..00000000000 --- a/noir-projects/noir-contracts/contracts/contract_instance_deployer_contract/src/interface/ContractInstanceDeployer.nr +++ /dev/null @@ -1,65 +0,0 @@ -/* Autogenerated file, do not edit! */ - -use dep::std; -use dep::aztec::context::{PrivateContext, PublicContext}; -use dep::aztec::protocol_types::{address::AztecAddress, abis::function_selector::FunctionSelector, constants::RETURN_VALUES_LENGTH}; - -struct ContractClassIdDeployStruct { - inner: Field, -} - -struct PortalContractAddressDeployStruct { - inner: Field, -} - -struct PublicKeysHashDeployStruct { - inner: Field, -} - -// Interface for calling ContractInstanceDeployer functions from a private context -struct ContractInstanceDeployerPrivateContextInterface { - address: AztecAddress, -} - -impl ContractInstanceDeployerPrivateContextInterface { - pub fn at(address: AztecAddress) -> Self { - Self { address } - } - - pub fn deploy( - self, - context: &mut PrivateContext, - salt: Field, - contract_class_id: ContractClassIdDeployStruct, - initialization_hash: Field, - portal_contract_address: PortalContractAddressDeployStruct, - public_keys_hash: PublicKeysHashDeployStruct, - universal_deploy: bool - ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 6]; - serialized_args[0] = salt; - serialized_args[1] = contract_class_id.inner; - serialized_args[2] = initialization_hash; - serialized_args[3] = portal_contract_address.inner; - serialized_args[4] = public_keys_hash.inner; - serialized_args[5] = universal_deploy as Field; - - context.call_private_function( - self.address, - FunctionSelector::from_field(0x883355ab), - serialized_args - ) - } -} - -// Interface for calling ContractInstanceDeployer functions from a public context -struct ContractInstanceDeployerPublicContextInterface { - address: AztecAddress, -} - -impl ContractInstanceDeployerPublicContextInterface { - pub fn at(address: AztecAddress) -> Self { - Self { address } - } -} - diff --git a/noir-projects/noir-contracts/contracts/fpc_contract/src/interfaces.nr b/noir-projects/noir-contracts/contracts/fpc_contract/src/interfaces.nr index dd18df1295f..fc98c231693 100644 --- a/noir-projects/noir-contracts/contracts/fpc_contract/src/interfaces.nr +++ b/noir-projects/noir-contracts/contracts/fpc_contract/src/interfaces.nr @@ -1,5 +1,5 @@ use dep::aztec::prelude::{AztecAddress, EthAddress, FunctionSelector, PrivateContext}; -use dep::aztec::context::PublicContext; +use dep::aztec::context::{PublicContext, gas::GasOpts}; use dep::aztec::protocol_types::constants::RETURN_VALUES_LENGTH; @@ -23,7 +23,8 @@ impl Token { let _ = context.call_public_function( self.address, FunctionSelector::from_signature("transfer_public((Field),(Field),Field,Field)"), - [from.to_field(), to.to_field(), amount, nonce] + [from.to_field(), to.to_field(), amount, nonce], + GasOpts::default() ); } @@ -38,7 +39,8 @@ impl Token { let _ = context.call_public_function( self.address, FunctionSelector::from_signature("shield((Field),Field,Field,Field)"), - [from.to_field(), amount, secret_hash, nonce] + [from.to_field(), amount, secret_hash, nonce], + GasOpts::default() ); } diff --git a/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr b/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr index deb23fa21d9..6d70815c6e5 100644 --- a/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr @@ -3,7 +3,7 @@ mod interfaces; contract FPC { use dep::aztec::protocol_types::{abis::function_selector::FunctionSelector, address::AztecAddress, traits::is_empty}; use dep::aztec::state_vars::SharedImmutable; - + use dep::aztec::context::gas::GasOpts; use crate::interfaces::Token; #[aztec(storage)] @@ -65,7 +65,8 @@ contract FPC { let refund = context.call_public_function( storage.gas_token_address.read_public(), FunctionSelector::from_signature("pay_fee(Field)"), - [amount] + [amount], + GasOpts::default() )[0]; // Just do public refunds for the present @@ -78,7 +79,8 @@ contract FPC { let refund = context.call_public_function( storage.gas_token_address.read_public(), FunctionSelector::from_signature("pay_fee(Field)"), - [amount] + [amount], + GasOpts::default() )[0]; Token::at(asset).shield(&mut context, context.this_address(), refund, secret_hash, 0); diff --git a/noir-projects/noir-contracts/contracts/lending_contract/src/interfaces.nr b/noir-projects/noir-contracts/contracts/lending_contract/src/interfaces.nr index f86f484562f..3357c89a023 100644 --- a/noir-projects/noir-contracts/contracts/lending_contract/src/interfaces.nr +++ b/noir-projects/noir-contracts/contracts/lending_contract/src/interfaces.nr @@ -1,6 +1,6 @@ use dep::aztec::prelude::{FunctionSelector, AztecAddress, PrivateContext}; -use dep::aztec::context::PublicContext; +use dep::aztec::context::{PublicContext, gas::GasOpts}; use dep::aztec::protocol_types::constants::RETURN_VALUES_LENGTH; use crate::asset::Asset; @@ -18,7 +18,8 @@ impl PriceFeed { let return_values = context.call_public_function( self.address, FunctionSelector::from_signature("get_price(Field)"), - [0] + [0], + GasOpts::default() ); U128::from_integer(return_values[0]) @@ -45,7 +46,8 @@ impl Token { let _ = context.call_public_function( self.address, FunctionSelector::from_signature("transfer_public((Field),(Field),Field,Field)"), - [from.to_field(), to.to_field(), amount, nonce] + [from.to_field(), to.to_field(), amount, nonce], + GasOpts::default() ); } @@ -53,7 +55,8 @@ impl Token { let _ = context.call_public_function( self.address, FunctionSelector::from_signature("mint_public((Field),Field)"), - [to.to_field(), amount] + [to.to_field(), amount], + GasOpts::default() ); } @@ -67,7 +70,8 @@ impl Token { let _ = context.call_public_function( self.address, FunctionSelector::from_signature("burn_public((Field),Field,Field)"), - [from.to_field(), amount, nonce] + [from.to_field(), amount, nonce], + GasOpts::default() ); } diff --git a/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr b/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr index 42bcdca5b9f..80675129a15 100644 --- a/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr @@ -12,7 +12,7 @@ mod interfaces; // - Liquidations contract Lending { use dep::aztec::prelude::{FunctionSelector, AztecAddress, PrivateContext, Map, PublicMutable}; - use dep::aztec::context::{PublicContext, Context}; + use dep::aztec::context::{PublicContext, Context, gas::GasOpts}; use crate::asset::Asset; use crate::interest_math::compute_multiplier; @@ -126,7 +126,8 @@ contract Lending { let _ = context.call_public_function( context.this_address(), selector, - [on_behalf_of, amount, collateral_asset.to_field()] + [on_behalf_of, amount, collateral_asset.to_field()], + GasOpts::default() ); } @@ -160,7 +161,8 @@ contract Lending { let _ = context.call_public_function( context.this_address(), selector, - [context.msg_sender().to_field(), to.to_field(), amount] + [context.msg_sender().to_field(), to.to_field(), amount], + GasOpts::default() ); } @@ -218,7 +220,8 @@ contract Lending { let _ = context.call_public_function( context.this_address(), selector, - [context.msg_sender().to_field(), to.to_field(), amount] + [context.msg_sender().to_field(), to.to_field(), amount], + GasOpts::default() ); } @@ -281,7 +284,8 @@ contract Lending { let _ = context.call_public_function( context.this_address(), selector, - [owner.to_field(), amount, stable_coin.to_field()] + [owner.to_field(), amount, stable_coin.to_field()], + GasOpts::default() ); } diff --git a/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr b/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr index 472159dfe2c..ec699ebf649 100644 --- a/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr @@ -1,6 +1,7 @@ // A contract used along with `Child` contract to test nested calls. contract Parent { use dep::aztec::prelude::{AztecAddress, FunctionSelector}; + use dep::aztec::context::gas::GasOpts; // Private function to call another private function in the target_contract using the provided selector #[aztec(private)] @@ -19,7 +20,12 @@ contract Parent { target_selector: FunctionSelector, init_value: Field ) -> Field { - let return_values = context.call_public_function(target_contract, target_selector, [init_value]); + let return_values = context.call_public_function( + target_contract, + target_selector, + [init_value], + GasOpts::default() + ); return_values[0] } @@ -31,8 +37,18 @@ contract Parent { target_selector: FunctionSelector, init_value: Field ) -> Field { - let return_value = context.call_public_function(target_contract, target_selector, [init_value])[0]; - let return_values = context.call_public_function(target_contract, target_selector, [return_value]); + let return_value = context.call_public_function( + target_contract, + target_selector, + [init_value], + GasOpts::default() + )[0]; + let return_values = context.call_public_function( + target_contract, + target_selector, + [return_value], + GasOpts::default() + ); return_values[0] } @@ -186,7 +202,7 @@ contract Parent { target_selector: FunctionSelector, args: [Field; 1] ) -> Field { - let return_values = context.static_call_public_function(target_contract, target_selector, args); + let return_values = context.static_call_public_function(target_contract, target_selector, args, GasOpts::default()); return_values[0] } @@ -204,7 +220,8 @@ contract Parent { let return_values = context.static_call_public_function( this_address, pub_entry_point_selector, - [target_contract.to_field(), target_selector.to_field(), args[0]] + [target_contract.to_field(), target_selector.to_field(), args[0]], + GasOpts::default() ); return_values[0] } diff --git a/noir-projects/noir-contracts/contracts/stateful_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/stateful_test_contract/src/main.nr index 31ae33d367d..555b8be512c 100644 --- a/noir-projects/noir-contracts/contracts/stateful_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/stateful_test_contract/src/main.nr @@ -3,7 +3,8 @@ contract StatefulTest { use dep::aztec::prelude::{PrivateContext, NoteHeader, Map, PublicMutable, PrivateSet, AztecAddress, FunctionSelector}; use dep::value_note::{balance_utils, utils::{increment, decrement}, value_note::{VALUE_NOTE_LEN, ValueNote}}; use dep::aztec::{ - deploy::deploy_contract as aztec_deploy_contract, context::{PublicContext, Context}, + deploy::deploy_contract as aztec_deploy_contract, + context::{PublicContext, Context, gas::GasOpts}, oracle::get_contract_instance::get_contract_instance, initializer::assert_is_initialized_private }; @@ -24,7 +25,12 @@ contract StatefulTest { #[aztec(initializer)] fn public_constructor(owner: AztecAddress, value: Field) { let selector = FunctionSelector::from_signature("increment_public_value_no_init_check((Field),Field)"); - let _res = context.call_public_function(context.this_address(), selector, [owner.to_field(), value]); + let _res = context.call_public_function( + context.this_address(), + selector, + [owner.to_field(), value], + GasOpts::default() + ); } #[aztec(private)] diff --git a/noir-projects/noir-contracts/contracts/test_contract/src/interface.nr b/noir-projects/noir-contracts/contracts/test_contract/src/interface.nr index f46f123d5c4..6eb4edbe314 100644 --- a/noir-projects/noir-contracts/contracts/test_contract/src/interface.nr +++ b/noir-projects/noir-contracts/contracts/test_contract/src/interface.nr @@ -1,9 +1,29 @@ /* Autogenerated file, do not edit! */ use dep::std; -use dep::aztec::context::{PrivateContext, PublicContext}; +use dep::aztec::context::{PrivateContext, PublicContext, gas::GasOpts}; use dep::aztec::protocol_types::{address::AztecAddress, abis::function_selector::FunctionSelector, constants::RETURN_VALUES_LENGTH}; +struct OwnerCallCreateNoteStruct { + inner: Field, +} + +struct CoinbaseAssertPublicGlobalVarsStruct { + inner: Field, +} + +struct FeeRecipientAssertPublicGlobalVarsStruct { + inner: Field, +} + +struct AztecAddressGetPortalContractAddressStruct { + inner: Field, +} + +struct AddressGetPublicKeyStruct { + inner: Field, +} + struct AStructTestCodeGenStruct { amount: Field, secret_hash: Field, @@ -26,23 +46,27 @@ struct ManyNotesADeepStructTestCodeGenStruct { secret_hash: Field, } -struct AddressGetPublicKeyStruct { +struct RecipientCreateL2ToL1MessageArbitraryRecipientPublicStruct { inner: Field, } -struct AztecAddressGetPortalContractAddressStruct { +struct ToConsumeMintPublicMessageStruct { inner: Field, } -struct CancellerConsumeMintPrivateMessageStruct { +struct SenderConsumeMessageFromArbitrarySenderPublicStruct { inner: Field, } -struct ToConsumeMintPublicMessageStruct { +struct SenderConsumeMessageFromArbitrarySenderPrivateStruct { + inner: Field, +} + +struct RecipientCreateL2ToL1MessageArbitraryRecipientPrivateStruct { inner: Field, } -struct CancellerConsumeMintPublicMessageStruct { +struct TargetDeployContractStruct { inner: Field, } @@ -56,6 +80,168 @@ impl TestPrivateContextInterface { Self { address } } + pub fn call_create_note( + self, + context: &mut PrivateContext, + value: Field, + owner: OwnerCallCreateNoteStruct, + storage_slot: Field + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialized_args = [0; 3]; + serialized_args[0] = value; + serialized_args[1] = owner.inner; + serialized_args[2] = storage_slot; + + context.call_private_function( + self.address, + FunctionSelector::from_field(0x946991ff), + serialized_args + ) + } + + pub fn get_this_portal_address(self, context: &mut PrivateContext) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialized_args = [0; 0]; + + context.call_private_function( + self.address, + FunctionSelector::from_field(0xc71384f5), + serialized_args + ) + } + + pub fn set_constant( + self, + context: &mut PrivateContext, + value: Field + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialized_args = [0; 1]; + serialized_args[0] = value; + + context.call_private_function( + self.address, + FunctionSelector::from_field(0x1b3b9e18), + serialized_args + ) + } + + pub fn assert_public_global_vars( + self, + context: &mut PrivateContext, + chain_id: Field, + version: Field, + block_number: Field, + timestamp: u64, + coinbase: CoinbaseAssertPublicGlobalVarsStruct, + fee_recipient: FeeRecipientAssertPublicGlobalVarsStruct + ) { + let mut serialized_args = [0; 6]; + serialized_args[0] = chain_id; + serialized_args[1] = version; + serialized_args[2] = block_number; + serialized_args[3] = timestamp as Field; + serialized_args[4] = coinbase.inner; + serialized_args[5] = fee_recipient.inner; + + context.call_public_function( + self.address, + FunctionSelector::from_field(0x9e88565a), + serialized_args + ) + } + + pub fn call_destroy_note( + self, + context: &mut PrivateContext, + storage_slot: Field + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialized_args = [0; 1]; + serialized_args[0] = storage_slot; + + context.call_private_function( + self.address, + FunctionSelector::from_field(0xf52a62f7), + serialized_args + ) + } + + pub fn consume_mint_private_message( + self, + context: &mut PrivateContext, + secret_hash_for_redeeming_minted_notes: Field, + amount: Field, + secret_for_L1_to_L2_message_consumption: Field + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialized_args = [0; 3]; + serialized_args[0] = secret_hash_for_redeeming_minted_notes; + serialized_args[1] = amount; + serialized_args[2] = secret_for_L1_to_L2_message_consumption; + + context.call_private_function( + self.address, + FunctionSelector::from_field(0xa0fdbaa9), + serialized_args + ) + } + + pub fn get_this_address(self, context: &mut PrivateContext) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialized_args = [0; 0]; + + context.call_private_function( + self.address, + FunctionSelector::from_field(0x95a7b2ae), + serialized_args + ) + } + + pub fn get_portal_contract_address( + self, + context: &mut PrivateContext, + aztec_address: AztecAddressGetPortalContractAddressStruct + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialized_args = [0; 1]; + serialized_args[0] = aztec_address.inner; + + context.call_private_function( + self.address, + FunctionSelector::from_field(0x30e5344b), + serialized_args + ) + } + + pub fn emit_array_as_unencrypted_log( + self, + context: &mut PrivateContext, + fields: [Field; 5] + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialized_args = [0; 5]; + serialized_args[0] = fields[0]; + serialized_args[1] = fields[1]; + serialized_args[2] = fields[2]; + serialized_args[3] = fields[3]; + serialized_args[4] = fields[4]; + + context.call_private_function( + self.address, + FunctionSelector::from_field(0xe25cbdd3), + serialized_args + ) + } + + pub fn get_public_key( + self, + context: &mut PrivateContext, + address: AddressGetPublicKeyStruct + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialized_args = [0; 1]; + serialized_args[0] = address.inner; + + context.call_private_function( + self.address, + FunctionSelector::from_field(0x501e4f48), + serialized_args + ) + } + pub fn test_code_gen( self, context: &mut PrivateContext, @@ -92,29 +278,111 @@ impl TestPrivateContextInterface { ) } - pub fn get_public_key( + pub fn assert_private_global_vars( self, context: &mut PrivateContext, - address: AddressGetPublicKeyStruct + chain_id: Field, + version: Field + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialized_args = [0; 2]; + serialized_args[0] = chain_id; + serialized_args[1] = version; + + context.call_private_function( + self.address, + FunctionSelector::from_field(0x7a8e9b66), + serialized_args + ) + } + + pub fn emit_nullifier( + self, + context: &mut PrivateContext, + nullifier: Field ) -> [Field; RETURN_VALUES_LENGTH] { let mut serialized_args = [0; 1]; - serialized_args[0] = address.inner; + serialized_args[0] = nullifier; context.call_private_function( self.address, - FunctionSelector::from_field(0x501e4f48), + FunctionSelector::from_field(0x82a8b183), serialized_args ) } - pub fn create_nullifier_public(self, context: &mut PrivateContext, amount: Field, secret_hash: Field) { + pub fn call_get_notes_many( + self, + context: &mut PrivateContext, + storage_slot: Field, + active_or_nullified: bool + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialized_args = [0; 2]; + serialized_args[0] = storage_slot; + serialized_args[1] = active_or_nullified as Field; + + context.call_private_function( + self.address, + FunctionSelector::from_field(0xcfcadbce), + serialized_args + ) + } + + pub fn assert_header_private( + self, + context: &mut PrivateContext, + header_hash: Field + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialized_args = [0; 1]; + serialized_args[0] = header_hash; + + context.call_private_function( + self.address, + FunctionSelector::from_field(0x4e45eb9e), + serialized_args + ) + } + + pub fn create_l2_to_l1_message_public( + self, + context: &mut PrivateContext, + amount: Field, + secret_hash: Field + ) { let mut serialized_args = [0; 2]; serialized_args[0] = amount; serialized_args[1] = secret_hash; context.call_public_function( self.address, - FunctionSelector::from_field(0xdf02db8d), + FunctionSelector::from_field(0x9749ca06), + serialized_args + ) + } + + pub fn create_l2_to_l1_message_arbitrary_recipient_public( + self, + context: &mut PrivateContext, + content: Field, + recipient: RecipientCreateL2ToL1MessageArbitraryRecipientPublicStruct + ) { + let mut serialized_args = [0; 2]; + serialized_args[0] = content; + serialized_args[1] = recipient.inner; + + context.call_public_function( + self.address, + FunctionSelector::from_field(0x2fb25188), + serialized_args + ) + } + + pub fn is_time_equal(self, context: &mut PrivateContext, time: u64) { + let mut serialized_args = [0; 1]; + serialized_args[0] = time as Field; + + context.call_public_function( + self.address, + FunctionSelector::from_field(0xb5bb17fa), serialized_args ) } @@ -129,148 +397,174 @@ impl TestPrivateContextInterface { ) } - pub fn get_portal_contract_address( + pub fn consume_mint_public_message( self, context: &mut PrivateContext, - aztec_address: AztecAddressGetPortalContractAddressStruct + to: ToConsumeMintPublicMessageStruct, + amount: Field, + secret: Field + ) { + let mut serialized_args = [0; 3]; + serialized_args[0] = to.inner; + serialized_args[1] = amount; + serialized_args[2] = secret; + + context.call_public_function( + self.address, + FunctionSelector::from_field(0xa0f84219), + serialized_args + ) + } + + pub fn request_max_block_number( + self, + context: &mut PrivateContext, + max_block_number: u32, + enqueue_public_call: bool ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 1]; - serialized_args[0] = aztec_address.inner; + let mut serialized_args = [0; 2]; + serialized_args[0] = max_block_number as Field; + serialized_args[1] = enqueue_public_call as Field; context.call_private_function( self.address, - FunctionSelector::from_field(0x30e5344b), + FunctionSelector::from_field(0x6db24b2e), serialized_args ) } - pub fn emit_unencrypted(self, context: &mut PrivateContext, value: Field) { - let mut serialized_args = [0; 1]; - serialized_args[0] = value; + pub fn consume_message_from_arbitrary_sender_public( + self, + context: &mut PrivateContext, + content: Field, + secret: Field, + sender: SenderConsumeMessageFromArbitrarySenderPublicStruct + ) { + let mut serialized_args = [0; 3]; + serialized_args[0] = content; + serialized_args[1] = secret; + serialized_args[2] = sender.inner; context.call_public_function( self.address, - FunctionSelector::from_field(0x817a64cb), + FunctionSelector::from_field(0x42ca6d60), serialized_args ) } - pub fn get_this_portal_address(self, context: &mut PrivateContext) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 0]; + pub fn emit_unencrypted(self, context: &mut PrivateContext, value: Field) { + let mut serialized_args = [0; 1]; + serialized_args[0] = value; - context.call_private_function( + context.call_public_function( self.address, - FunctionSelector::from_field(0xc71384f5), + FunctionSelector::from_field(0x817a64cb), serialized_args ) } - pub fn get_this_address(self, context: &mut PrivateContext) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 0]; + pub fn consume_message_from_arbitrary_sender_private( + self, + context: &mut PrivateContext, + content: Field, + secret: Field, + sender: SenderConsumeMessageFromArbitrarySenderPrivateStruct + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialized_args = [0; 3]; + serialized_args[0] = content; + serialized_args[1] = secret; + serialized_args[2] = sender.inner; context.call_private_function( self.address, - FunctionSelector::from_field(0x95a7b2ae), + FunctionSelector::from_field(0x2847cb26), serialized_args ) } - pub fn is_time_equal(self, context: &mut PrivateContext, time: Field) { - let mut serialized_args = [0; 1]; - serialized_args[0] = time; + pub fn create_nullifier_public(self, context: &mut PrivateContext, amount: Field, secret_hash: Field) { + let mut serialized_args = [0; 2]; + serialized_args[0] = amount; + serialized_args[1] = secret_hash; context.call_public_function( self.address, - FunctionSelector::from_field(0x61fa2bda), + FunctionSelector::from_field(0xdf02db8d), serialized_args ) } - pub fn consume_mint_private_message( + pub fn create_l2_to_l1_message_arbitrary_recipient_private( self, context: &mut PrivateContext, - secret_hash_for_redeeming_minted_notes: Field, - amount: Field, - canceller: CancellerConsumeMintPrivateMessageStruct, - secret_for_L1_to_L2_message_consumption: Field + content: Field, + recipient: RecipientCreateL2ToL1MessageArbitraryRecipientPrivateStruct ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 4]; - serialized_args[0] = secret_hash_for_redeeming_minted_notes; - serialized_args[1] = amount; - serialized_args[2] = canceller.inner; - serialized_args[3] = secret_for_L1_to_L2_message_consumption; + let mut serialized_args = [0; 2]; + serialized_args[0] = content; + serialized_args[1] = recipient.inner; context.call_private_function( self.address, - FunctionSelector::from_field(0x260712c7), + FunctionSelector::from_field(0xaccc5d5d), serialized_args ) } - pub fn emit_nullifier( + pub fn deploy_contract( self, context: &mut PrivateContext, - nullifier: Field + target: TargetDeployContractStruct ) -> [Field; RETURN_VALUES_LENGTH] { let mut serialized_args = [0; 1]; - serialized_args[0] = nullifier; + serialized_args[0] = target.inner; context.call_private_function( self.address, - FunctionSelector::from_field(0x82a8b183), + FunctionSelector::from_field(0x5acec588), serialized_args ) } - pub fn create_l2_to_l1_message_public( + pub fn call_get_notes( self, context: &mut PrivateContext, - amount: Field, - secret_hash: Field - ) { + storage_slot: Field, + active_or_nullified: bool + ) -> [Field; RETURN_VALUES_LENGTH] { let mut serialized_args = [0; 2]; - serialized_args[0] = amount; - serialized_args[1] = secret_hash; + serialized_args[0] = storage_slot; + serialized_args[1] = active_or_nullified as Field; - context.call_public_function( + context.call_private_function( self.address, - FunctionSelector::from_field(0x9749ca06), + FunctionSelector::from_field(0x11eeb3ea), serialized_args ) } - pub fn consume_mint_public_message( + pub fn consume_note_from_secret( self, context: &mut PrivateContext, - to: ToConsumeMintPublicMessageStruct, - amount: Field, - canceller: CancellerConsumeMintPublicMessageStruct, secret: Field - ) { - let mut serialized_args = [0; 4]; - serialized_args[0] = to.inner; - serialized_args[1] = amount; - serialized_args[2] = canceller.inner; - serialized_args[3] = secret; + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialized_args = [0; 1]; + serialized_args[0] = secret; - context.call_public_function( + context.call_private_function( self.address, - FunctionSelector::from_field(0x04ee3bb0), + FunctionSelector::from_field(0x754932c8), serialized_args ) } - pub fn set_constant( - self, - context: &mut PrivateContext, - value: Field - ) -> [Field; RETURN_VALUES_LENGTH] { + pub fn assert_header_public(self, context: &mut PrivateContext, header_hash: Field) { let mut serialized_args = [0; 1]; - serialized_args[0] = value; + serialized_args[0] = header_hash; - context.call_private_function( + context.call_public_function( self.address, - FunctionSelector::from_field(0x1b3b9e18), + FunctionSelector::from_field(0x86e38c61), serialized_args ) } @@ -286,7 +580,33 @@ impl TestPublicContextInterface { Self { address } } - pub fn create_nullifier_public( + pub fn assert_public_global_vars( + self, + context: &mut PublicContext, + chain_id: Field, + version: Field, + block_number: Field, + timestamp: u64, + coinbase: CoinbaseAssertPublicGlobalVarsStruct, + fee_recipient: FeeRecipientAssertPublicGlobalVarsStruct + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialized_args = [0; 6]; + serialized_args[0] = chain_id; + serialized_args[1] = version; + serialized_args[2] = block_number; + serialized_args[3] = timestamp as Field; + serialized_args[4] = coinbase.inner; + serialized_args[5] = fee_recipient.inner; + + context.call_public_function( + self.address, + FunctionSelector::from_field(0x9e88565a), + serialized_args, + GasOpts::default() + ) + } + + pub fn create_l2_to_l1_message_public( self, context: &mut PublicContext, amount: Field, @@ -298,38 +618,99 @@ impl TestPublicContextInterface { context.call_public_function( self.address, - FunctionSelector::from_field(0xdf02db8d), - serialized_args + FunctionSelector::from_field(0x9749ca06), + serialized_args, + GasOpts::default() ) } - pub fn emit_unencrypted( + pub fn create_l2_to_l1_message_arbitrary_recipient_public( self, context: &mut PublicContext, - value: Field + content: Field, + recipient: RecipientCreateL2ToL1MessageArbitraryRecipientPublicStruct ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialized_args = [0; 2]; + serialized_args[0] = content; + serialized_args[1] = recipient.inner; + + context.call_public_function( + self.address, + FunctionSelector::from_field(0x2fb25188), + serialized_args, + GasOpts::default() + ) + } + + pub fn is_time_equal(self, context: &mut PublicContext, time: u64) -> [Field; RETURN_VALUES_LENGTH] { let mut serialized_args = [0; 1]; - serialized_args[0] = value; + serialized_args[0] = time as Field; context.call_public_function( self.address, - FunctionSelector::from_field(0x817a64cb), - serialized_args + FunctionSelector::from_field(0xb5bb17fa), + serialized_args, + GasOpts::default() + ) + } + + pub fn consume_mint_public_message( + self, + context: &mut PublicContext, + to: ToConsumeMintPublicMessageStruct, + amount: Field, + secret: Field + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialized_args = [0; 3]; + serialized_args[0] = to.inner; + serialized_args[1] = amount; + serialized_args[2] = secret; + + context.call_public_function( + self.address, + FunctionSelector::from_field(0xa0f84219), + serialized_args, + GasOpts::default() ) } - pub fn is_time_equal(self, context: &mut PublicContext, time: Field) -> [Field; RETURN_VALUES_LENGTH] { + pub fn consume_message_from_arbitrary_sender_public( + self, + context: &mut PublicContext, + content: Field, + secret: Field, + sender: SenderConsumeMessageFromArbitrarySenderPublicStruct + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialized_args = [0; 3]; + serialized_args[0] = content; + serialized_args[1] = secret; + serialized_args[2] = sender.inner; + + context.call_public_function( + self.address, + FunctionSelector::from_field(0x42ca6d60), + serialized_args, + GasOpts::default() + ) + } + + pub fn emit_unencrypted( + self, + context: &mut PublicContext, + value: Field + ) -> [Field; RETURN_VALUES_LENGTH] { let mut serialized_args = [0; 1]; - serialized_args[0] = time; + serialized_args[0] = value; context.call_public_function( self.address, - FunctionSelector::from_field(0x61fa2bda), - serialized_args + FunctionSelector::from_field(0x817a64cb), + serialized_args, + GasOpts::default() ) } - pub fn create_l2_to_l1_message_public( + pub fn create_nullifier_public( self, context: &mut PublicContext, amount: Field, @@ -341,29 +722,25 @@ impl TestPublicContextInterface { context.call_public_function( self.address, - FunctionSelector::from_field(0x9749ca06), - serialized_args + FunctionSelector::from_field(0xdf02db8d), + serialized_args, + GasOpts::default() ) } - pub fn consume_mint_public_message( + pub fn assert_header_public( self, context: &mut PublicContext, - to: ToConsumeMintPublicMessageStruct, - amount: Field, - canceller: CancellerConsumeMintPublicMessageStruct, - secret: Field + header_hash: Field ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 4]; - serialized_args[0] = to.inner; - serialized_args[1] = amount; - serialized_args[2] = canceller.inner; - serialized_args[3] = secret; + let mut serialized_args = [0; 1]; + serialized_args[0] = header_hash; context.call_public_function( self.address, - FunctionSelector::from_field(0x04ee3bb0), - serialized_args + FunctionSelector::from_field(0x86e38c61), + serialized_args, + GasOpts::default() ) } } diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/interfaces.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/interfaces.nr index 9cb8253c927..9ef4f1439b7 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/interfaces.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/interfaces.nr @@ -1,6 +1,6 @@ use dep::aztec::protocol_types::{abis::function_selector::FunctionSelector, address::AztecAddress}; -use dep::aztec::context::{PrivateContext, PublicContext, Context}; +use dep::aztec::context::{PrivateContext, PublicContext, Context, gas::GasOpts}; struct SlowMap { address: AztecAddress, @@ -22,7 +22,8 @@ impl SlowMap { let _return_values = context.call_public_function( self.address, FunctionSelector::from_signature("read_at_pub(Field)"), - [index] + [index], + GasOpts::default() ); _return_values[0] } diff --git a/noir-projects/noir-contracts/contracts/token_bridge_contract/src/token_interface.nr b/noir-projects/noir-contracts/contracts/token_bridge_contract/src/token_interface.nr index 684f19ebd44..be491ea1412 100644 --- a/noir-projects/noir-contracts/contracts/token_bridge_contract/src/token_interface.nr +++ b/noir-projects/noir-contracts/contracts/token_bridge_contract/src/token_interface.nr @@ -1,6 +1,6 @@ // docs:start:token_bridge_token_interface use dep::aztec::prelude::{FunctionSelector, AztecAddress, EthAddress, PrivateContext}; -use dep::aztec::context::{PublicContext, Context}; +use dep::aztec::context::{PublicContext, Context, gas::GasOpts}; struct Token { address: AztecAddress, @@ -15,7 +15,8 @@ impl Token { let _return_values = context.call_public_function( self.address, FunctionSelector::from_signature("mint_public((Field),Field)"), - [to.to_field(), amount] + [to.to_field(), amount], + GasOpts::default() ); } @@ -30,7 +31,8 @@ impl Token { let _return_values = context.call_public_function( self.address, FunctionSelector::from_signature("burn_public((Field),Field,Field)"), - [from.to_field(), amount, nonce] + [from.to_field(), amount, nonce], + GasOpts::default() ); } // docs:end:public_burn_interface @@ -39,7 +41,8 @@ impl Token { let _return_values = context.call_public_function( self.address, FunctionSelector::from_signature("mint_private(Field,Field)"), - [amount, secret_hash] + [amount, secret_hash], + GasOpts::default() ); } diff --git a/noir-projects/noir-contracts/contracts/uniswap_contract/src/interfaces.nr b/noir-projects/noir-contracts/contracts/uniswap_contract/src/interfaces.nr index 81bf1824bfb..20b9b4af2af 100644 --- a/noir-projects/noir-contracts/contracts/uniswap_contract/src/interfaces.nr +++ b/noir-projects/noir-contracts/contracts/uniswap_contract/src/interfaces.nr @@ -1,6 +1,6 @@ // docs:start:interfaces use dep::aztec::prelude::{FunctionSelector, AztecAddress, EthAddress, PrivateContext}; -use dep::aztec::context::PublicContext; +use dep::aztec::context::{PublicContext, gas::GasOpts}; struct Token { address: AztecAddress, @@ -22,7 +22,8 @@ impl Token { let _ = context.call_public_function( self.address, FunctionSelector::from_signature("transfer_public((Field),(Field),Field,Field)"), - [from.to_field(), to.to_field(), amount, nonce] + [from.to_field(), to.to_field(), amount, nonce], + GasOpts::default() ); } @@ -55,7 +56,8 @@ impl TokenBridge { let return_values = context.call_public_function( self.address, FunctionSelector::from_signature("get_token()"), - [] + [], + GasOpts::default() ); AztecAddress::from_field(return_values[0]) } @@ -71,7 +73,8 @@ impl TokenBridge { let _ = context.call_public_function( self.address, FunctionSelector::from_signature("exit_to_l1_public((Field),Field,(Field),Field)"), - [recipient.to_field(), amount, caller_on_l1.to_field(), nonce] + [recipient.to_field(), amount, caller_on_l1.to_field(), nonce], + GasOpts::default() ); } } diff --git a/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr b/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr index b6d2d97a97e..7a7c68c5fe3 100644 --- a/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr @@ -8,6 +8,7 @@ mod util; contract Uniswap { use dep::aztec::prelude::{FunctionSelector, AztecAddress, EthAddress, Map, PublicMutable}; use dep::aztec::oracle::context::get_portal_address; + use dep::aztec::context::gas::GasOpts; use dep::authwit::auth::{ IS_VALID_SELECTOR, assert_current_call_valid_authwit_public, compute_call_authwit_hash, @@ -65,7 +66,8 @@ contract Uniswap { let _void = context.call_public_function( context.this_address(), FunctionSelector::from_signature("_approve_bridge_and_exit_input_asset_to_L1((Field),(Field),Field)"), - [input_asset.to_field(), input_asset_bridge.to_field(), input_amount] + [input_asset.to_field(), input_asset_bridge.to_field(), input_amount], + GasOpts::default() ); // Create swap message and send to Outbox for Uniswap Portal diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index c95d6654430..f00c50ed762 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -93,6 +93,8 @@ global INITIALIZATION_SLOT_SEPARATOR: Field = 1000_000_000; global INITIAL_L2_BLOCK_NUM: Field = 1; // 126976 = 31 * 4096; global BLOB_SIZE_IN_BYTES: Field = 126976; +// How much gas is subtracted from L2GASLEFT when making a nested public call by default in the AVM +global NESTED_CALL_L2_GAS_BUFFER = 20000; // CONTRACT CLASS CONSTANTS global MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS: u64 = 15000; diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index d48c426d72b..9ee6bd1a3eb 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -55,6 +55,7 @@ export const ARGS_HASH_CHUNK_COUNT = 64; export const INITIALIZATION_SLOT_SEPARATOR = 1000_000_000; export const INITIAL_L2_BLOCK_NUM = 1; export const BLOB_SIZE_IN_BYTES = 126976; +export const NESTED_CALL_L2_GAS_BUFFER = 20000; export const MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 15000; export const MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS = 3000; export const MAX_PACKED_BYTECODE_SIZE_PER_UNCONSTRAINED_FUNCTION_IN_FIELDS = 3000; diff --git a/yarn-project/noir-compiler/src/cli/add_noir_compiler_commander_actions.ts b/yarn-project/noir-compiler/src/cli/add_noir_compiler_commander_actions.ts index 4ac4ce2b4c3..02fa9eb6c0b 100644 --- a/yarn-project/noir-compiler/src/cli/add_noir_compiler_commander_actions.ts +++ b/yarn-project/noir-compiler/src/cli/add_noir_compiler_commander_actions.ts @@ -14,12 +14,13 @@ export function addCodegenCommanderAction(program: Command, _: LogFn = () => {}) .option('-o, --outdir ', 'Output folder for the generated code.') .option('--ts', 'Generate TypeScript wrapper.') .option('--nr', 'Generate Noir interface.') + .option('--force', 'Force code generation even when the contract has not changed.') .description('Validates and generates an Aztec Contract ABI from Noir ABI.') - .action(async (noirAbiPath: string, { outdir, ts, nr }) => { + .action(async (noirAbiPath: string, { outdir, ts, nr, force }) => { if (ts && nr) { throw new Error('--ts and --nr are mutually exclusive.'); } const { generateCode } = await import('./codegen.js'); - generateCode(outdir || dirname(noirAbiPath), noirAbiPath, { ts, nr }); + generateCode(outdir || dirname(noirAbiPath), noirAbiPath, { ts, nr, force }); }); } diff --git a/yarn-project/noir-compiler/src/cli/codegen.ts b/yarn-project/noir-compiler/src/cli/codegen.ts index 1f2656e8ebb..47aaeaa390b 100644 --- a/yarn-project/noir-compiler/src/cli/codegen.ts +++ b/yarn-project/noir-compiler/src/cli/codegen.ts @@ -12,7 +12,7 @@ const cacheFilePath = './codegenCache.json'; let cache: Record = {}; /** Generate code options */ -type GenerateCodeOptions = { /** Typescript */ ts?: boolean; /** Noir */ nr?: boolean }; +type GenerateCodeOptions = { /** Typescript */ ts?: boolean; /** Noir */ nr?: boolean; force?: boolean }; /** * Generates Noir interface or Typescript interface for a folder or single file from a Noir compilation artifact. @@ -42,7 +42,7 @@ function generateFromNoirAbi(outputPath: string, noirAbiPath: string, opts: Gene const contractName = path.basename(noirAbiPath); const currentHash = generateFileHash(noirAbiPath); - if (isCacheValid(contractName, currentHash)) { + if (isCacheValid(contractName, currentHash) && !opts.force) { console.log(`${contractName} has not changed. Skipping generation.`); return; } diff --git a/yarn-project/noir-compiler/src/contract-interface-gen/noir.ts b/yarn-project/noir-compiler/src/contract-interface-gen/noir.ts index 3a2f1d92e2e..ede698561bb 100644 --- a/yarn-project/noir-compiler/src/contract-interface-gen/noir.ts +++ b/yarn-project/noir-compiler/src/contract-interface-gen/noir.ts @@ -27,10 +27,20 @@ function isPrivateCall(functionType: FunctionType) { * @param functionType - Type of the function. * @returns A code string. */ -function generateCallStatement(selector: FunctionSelector, functionType: FunctionType) { +function generateCallStatement( + selector: FunctionSelector, + functionType: FunctionType, + callingContext: 'private' | 'public', +) { const callMethod = isPrivateCall(functionType) ? 'call_private_function' : 'call_public_function'; + const args = [ + 'self.address', + `FunctionSelector::from_field(${selector.toString()})`, + 'serialized_args', + ...(callingContext === 'private' ? [] : ['GasOpts::default()']), + ]; return ` - context.${callMethod}(self.address, FunctionSelector::from_field(0x${selector.toString()}), serialized_args)`; + context.${callMethod}(${args.join(', ')})`; } /** @@ -142,8 +152,8 @@ function generateFunctionInterface(functionData: FunctionArtifact, kind: 'privat const { name, parameters } = functionData; const selector = FunctionSelector.fromNameAndParameters(name, parameters); const serialization = generateSerialization(parameters); - const contextType = kind === 'private' ? '&mut PrivateContext' : 'PublicContext'; - const callStatement = generateCallStatement(selector, functionData.functionType); + const contextType = kind === 'private' ? '&mut PrivateContext' : '&mut PublicContext'; + const callStatement = generateCallStatement(selector, functionData.functionType, kind); const allParams = ['self', `context: ${contextType}`, ...parameters.map(p => generateParameter(p, functionData))]; const isPrivate = isPrivateCall(functionData.functionType); const isSync = (isPrivate && kind === 'private') || (!isPrivate && kind === 'public'); @@ -165,7 +175,7 @@ ${callStatement} */ function generateStaticImports() { return `use dep::std; -use dep::aztec::context::{ PrivateContext, PublicContext }; +use dep::aztec::context::{ PrivateContext, PublicContext, gas::GasOpts }; use dep::aztec::protocol_types::{ address::AztecAddress, abis::function_selector::FunctionSelector, diff --git a/yarn-project/noir-contracts.js/scripts/generate-noir-interfaces.sh b/yarn-project/noir-contracts.js/scripts/generate-noir-interfaces.sh new file mode 100755 index 00000000000..249d168242f --- /dev/null +++ b/yarn-project/noir-contracts.js/scripts/generate-noir-interfaces.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -euo pipefail + +CONTRACTS=( + test_contract-Test +) + +NOIR_CONTRACTS_DIR="../../noir-projects/noir-contracts" + +for contract in $CONTRACTS; do + echo "Generating Noir interface for $contract" + OUT_DIR="$NOIR_CONTRACTS_DIR/contracts/${contract%%-*}/src" + node --no-warnings ../noir-compiler/dest/cli.js codegen -o $OUT_DIR --nr --force $NOIR_CONTRACTS_DIR/target/$contract.json + mv $OUT_DIR/${contract#*-}.nr $OUT_DIR/interface.nr +done + +echo "Done updating Noir interfaces" \ No newline at end of file diff --git a/yarn-project/simulator/src/avm/avm_simulator.test.ts b/yarn-project/simulator/src/avm/avm_simulator.test.ts index b5247881e7e..198fdf2fd07 100644 --- a/yarn-project/simulator/src/avm/avm_simulator.test.ts +++ b/yarn-project/simulator/src/avm/avm_simulator.test.ts @@ -461,7 +461,7 @@ describe('AVM simulator: transpiled Noir contracts', () => { }); describe('Nested external calls', () => { - it(`Nested call`, async () => { + it(`Nested call succeeds`, async () => { const calldata: Fr[] = [new Fr(1), new Fr(2)]; const callBytecode = getAvmTestContractBytecode('raw_nested_call_to_add'); const addBytecode = getAvmTestContractBytecode('add_args_return'); @@ -477,6 +477,24 @@ describe('AVM simulator: transpiled Noir contracts', () => { expect(results.output).toEqual([new Fr(3)]); }); + it(`Nested call with not enough gas`, async () => { + const gas = [/*l1=*/ 10000, /*l2=*/ 20, /*da=*/ 10000].map(g => new Fr(g)); + const calldata: Fr[] = [new Fr(1), new Fr(2), ...gas]; + const callBytecode = getAvmTestContractBytecode('raw_nested_call_to_add_with_gas'); + const addBytecode = getAvmTestContractBytecode('add_args_return'); + const context = initContext({ env: initExecutionEnvironment({ calldata }) }); + jest + .spyOn(context.persistableState.hostStorage.contractsDb, 'getBytecode') + .mockReturnValueOnce(Promise.resolve(addBytecode)); + + const results = await new AvmSimulator(context).executeBytecode(callBytecode); + + // Outer frame should not revert, but inner should, so the forwarded return value is 0 + expect(results.revertReason).toBeUndefined(); + expect(results.reverted).toBe(false); + expect(results.output).toEqual([new Fr(0)]); + }); + it(`Nested call through the old interface`, async () => { const calldata: Fr[] = [new Fr(1), new Fr(2)]; const callBytecode = getAvmTestContractBytecode('nested_call_to_add'); diff --git a/yarn-project/simulator/src/avm/avm_simulator.ts b/yarn-project/simulator/src/avm/avm_simulator.ts index b2221f51b05..6cf9e5bb078 100644 --- a/yarn-project/simulator/src/avm/avm_simulator.ts +++ b/yarn-project/simulator/src/avm/avm_simulator.ts @@ -50,30 +50,32 @@ export class AvmSimulator { */ public async executeInstructions(instructions: Instruction[]): Promise { assert(instructions.length > 0); + const { machineState } = this.context; try { // Execute instruction pointed to by the current program counter // continuing until the machine state signifies a halt - while (!this.context.machineState.halted) { - const instruction = instructions[this.context.machineState.pc]; + while (!machineState.halted) { + const instruction = instructions[machineState.pc]; assert( !!instruction, 'AVM attempted to execute non-existent instruction. This should never happen (invalid bytecode or AVM simulator bug)!', ); - this.log.debug(`@${this.context.machineState.pc} ${instruction.toString()}`); + const gasLeft = `l1=${machineState.l1GasLeft} l2=${machineState.l2GasLeft} da=${machineState.daGasLeft}`; + this.log.debug(`@${machineState.pc} (${gasLeft}) ${instruction.toString()}`); // Execute the instruction. // Normal returns and reverts will return normally here. // "Exceptional halts" will throw. await instruction.execute(this.context); - if (this.context.machineState.pc >= instructions.length) { + if (machineState.pc >= instructions.length) { this.log('Passed end of program!'); - throw new InvalidProgramCounterError(this.context.machineState.pc, /*max=*/ instructions.length); + throw new InvalidProgramCounterError(machineState.pc, /*max=*/ instructions.length); } } // Return results for processing by calling context - const results = this.context.machineState.getResults(); + const results = machineState.getResults(); this.log(`Context execution results: ${results.toString()}`); return results; } catch (e) { diff --git a/yarn-project/simulator/src/avm/opcodes/context_getters.test.ts b/yarn-project/simulator/src/avm/opcodes/context_getters.test.ts new file mode 100644 index 00000000000..8cbc26e7dcd --- /dev/null +++ b/yarn-project/simulator/src/avm/opcodes/context_getters.test.ts @@ -0,0 +1,32 @@ +import { initContext, initMachineState } from '../fixtures/index.js'; +import { DAGasLeft, L1GasLeft, L2GasLeft } from './context_getters.js'; + +describe.each([ + [L1GasLeft, 'l1GasLeft'], + [L2GasLeft, 'l2GasLeft'], + [DAGasLeft, 'daGasLeft'], +] as const)('Context getter instructions for machine state', (clsValue, key) => { + it(`${clsValue.name} should (de)serialize correctly`, () => { + const buf = Buffer.from([ + clsValue.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + const inst = new clsValue(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); + + expect(clsValue.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); + }); + + it(`${clsValue.name} should read '${key}' correctly`, async () => { + const value = 123456; + const instruction = new clsValue(/*indirect=*/ 0, /*dstOffset=*/ 0); + const context = initContext({ machineState: initMachineState({ [key]: value }) }); + + await instruction.execute(context); + + const actual = context.machineState.memory.get(0).toNumber(); + const expected = key === 'l2GasLeft' ? value - 110 : value; // l2gascost decreases when it's executed + expect(actual).toEqual(expected); + }); +}); diff --git a/yarn-project/simulator/src/avm/opcodes/context_getters.ts b/yarn-project/simulator/src/avm/opcodes/context_getters.ts new file mode 100644 index 00000000000..e07948e4657 --- /dev/null +++ b/yarn-project/simulator/src/avm/opcodes/context_getters.ts @@ -0,0 +1,32 @@ +import type { AvmContext } from '../avm_context.js'; +import { Field, type MemoryValue } from '../avm_memory_types.js'; +import { Opcode } from '../serialization/instruction_serialization.js'; +import { GetterInstruction } from './instruction_impl.js'; + +export class L2GasLeft extends GetterInstruction { + static type: string = 'L2GASLEFT'; + static readonly opcode: Opcode = Opcode.L2GASLEFT; + + // TODO(@spalladino) Yellow paper specifies that the value should be an Uint32, not a Field. + protected getValue(context: AvmContext): MemoryValue { + return new Field(context.machineState.l2GasLeft); + } +} + +export class L1GasLeft extends GetterInstruction { + static type: string = 'L1GASLEFT'; + static readonly opcode: Opcode = Opcode.L1GASLEFT; + + protected getValue(context: AvmContext): MemoryValue { + return new Field(context.machineState.l1GasLeft); + } +} + +export class DAGasLeft extends GetterInstruction { + static type: string = 'DAGASLEFT'; + static readonly opcode: Opcode = Opcode.DAGASLEFT; + + protected getValue(context: AvmContext): MemoryValue { + return new Field(context.machineState.daGasLeft); + } +} diff --git a/yarn-project/simulator/src/avm/opcodes/environment_getters.ts b/yarn-project/simulator/src/avm/opcodes/environment_getters.ts index aa9b848a239..9f179841fcb 100644 --- a/yarn-project/simulator/src/avm/opcodes/environment_getters.ts +++ b/yarn-project/simulator/src/avm/opcodes/environment_getters.ts @@ -2,142 +2,127 @@ import { type Fr } from '@aztec/circuits.js'; import type { AvmContext } from '../avm_context.js'; import type { AvmExecutionEnvironment } from '../avm_execution_environment.js'; -import { Field } from '../avm_memory_types.js'; -import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; -import { Instruction } from './instruction.js'; +import { Field, type MemoryValue } from '../avm_memory_types.js'; +import { Opcode } from '../serialization/instruction_serialization.js'; +import { GetterInstruction } from './instruction_impl.js'; -abstract class GetterInstruction extends Instruction { - // Informs (de)serialization. See Instruction.deserialize. - static readonly wireFormat: OperandType[] = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT32]; - - constructor(protected indirect: number, protected dstOffset: number) { - super(); - } - - public async execute(context: AvmContext): Promise { - const memoryOperations = { writes: 1, indirect: this.indirect }; - const memory = context.machineState.memory.track(this.type); - context.machineState.consumeGas(this.gasCost(memoryOperations)); - - const res = new Field(this.getIt(context.environment)); - memory.set(this.dstOffset, res); - - memory.assert(memoryOperations); - context.machineState.incrementPc(); +abstract class EnvironmentGetterInstruction extends GetterInstruction { + protected getValue(context: AvmContext): MemoryValue { + return new Field(this.getEnvironmentValue(context.environment)); } - protected abstract getIt(env: AvmExecutionEnvironment): Fr | number | bigint; + protected abstract getEnvironmentValue(env: AvmExecutionEnvironment): Fr | number | bigint; } -export class Address extends GetterInstruction { +export class Address extends EnvironmentGetterInstruction { static type: string = 'ADDRESS'; static readonly opcode: Opcode = Opcode.ADDRESS; - protected getIt(env: AvmExecutionEnvironment) { + protected getEnvironmentValue(env: AvmExecutionEnvironment) { return env.address; } } -export class StorageAddress extends GetterInstruction { +export class StorageAddress extends EnvironmentGetterInstruction { static type: string = 'STORAGEADDRESS'; static readonly opcode: Opcode = Opcode.STORAGEADDRESS; - protected getIt(env: AvmExecutionEnvironment) { + protected getEnvironmentValue(env: AvmExecutionEnvironment) { return env.storageAddress; } } -export class Sender extends GetterInstruction { +export class Sender extends EnvironmentGetterInstruction { static type: string = 'SENDER'; static readonly opcode: Opcode = Opcode.SENDER; - protected getIt(env: AvmExecutionEnvironment) { + protected getEnvironmentValue(env: AvmExecutionEnvironment) { return env.sender; } } -export class Origin extends GetterInstruction { +export class Origin extends EnvironmentGetterInstruction { static type: string = 'ORIGIN'; static readonly opcode: Opcode = Opcode.ORIGIN; - protected getIt(env: AvmExecutionEnvironment) { + protected getEnvironmentValue(env: AvmExecutionEnvironment) { return env.origin; } } -export class FeePerL1Gas extends GetterInstruction { +export class FeePerL1Gas extends EnvironmentGetterInstruction { static type: string = 'FEEPERL1GAS'; static readonly opcode: Opcode = Opcode.FEEPERL1GAS; - protected getIt(env: AvmExecutionEnvironment) { + protected getEnvironmentValue(env: AvmExecutionEnvironment) { return env.feePerL1Gas; } } -export class FeePerL2Gas extends GetterInstruction { +export class FeePerL2Gas extends EnvironmentGetterInstruction { static type: string = 'FEEPERL2GAS'; static readonly opcode: Opcode = Opcode.FEEPERL2GAS; - protected getIt(env: AvmExecutionEnvironment) { + protected getEnvironmentValue(env: AvmExecutionEnvironment) { return env.feePerL2Gas; } } -export class FeePerDAGas extends GetterInstruction { +export class FeePerDAGas extends EnvironmentGetterInstruction { static type: string = 'FEEPERDAGAS'; static readonly opcode: Opcode = Opcode.FEEPERDAGAS; - protected getIt(env: AvmExecutionEnvironment) { + protected getEnvironmentValue(env: AvmExecutionEnvironment) { return env.feePerDaGas; } } -export class Portal extends GetterInstruction { +export class Portal extends EnvironmentGetterInstruction { static type: string = 'PORTAL'; static readonly opcode: Opcode = Opcode.PORTAL; - protected getIt(env: AvmExecutionEnvironment) { + protected getEnvironmentValue(env: AvmExecutionEnvironment) { return env.portal.toField(); } } -export class ChainId extends GetterInstruction { +export class ChainId extends EnvironmentGetterInstruction { static type: string = 'CHAINID'; static readonly opcode: Opcode = Opcode.CHAINID; - protected getIt(env: AvmExecutionEnvironment) { + protected getEnvironmentValue(env: AvmExecutionEnvironment) { return env.globals.chainId; } } -export class Version extends GetterInstruction { +export class Version extends EnvironmentGetterInstruction { static type: string = 'VERSION'; static readonly opcode: Opcode = Opcode.VERSION; - protected getIt(env: AvmExecutionEnvironment) { + protected getEnvironmentValue(env: AvmExecutionEnvironment) { return env.globals.version; } } -export class BlockNumber extends GetterInstruction { +export class BlockNumber extends EnvironmentGetterInstruction { static type: string = 'BLOCKNUMBER'; static readonly opcode: Opcode = Opcode.BLOCKNUMBER; - protected getIt(env: AvmExecutionEnvironment) { + protected getEnvironmentValue(env: AvmExecutionEnvironment) { return env.globals.blockNumber; } } -export class Timestamp extends GetterInstruction { +export class Timestamp extends EnvironmentGetterInstruction { static type: string = 'TIMESTAMP'; static readonly opcode: Opcode = Opcode.TIMESTAMP; - protected getIt(env: AvmExecutionEnvironment) { + protected getEnvironmentValue(env: AvmExecutionEnvironment) { return env.globals.timestamp; } } -// export class Coinbase extends GetterInstruction { +// export class Coinbase extends EnvironmentGetterInstruction { // static type: string = 'COINBASE'; // static numberOfOperands = 1; @@ -155,7 +140,7 @@ export class Timestamp extends GetterInstruction { // } // // TODO: are these even needed within the block? (both block gas limit variables - why does the execution env care?) -// export class BlockL1GasLimit extends GetterInstruction { +// export class BlockL1GasLimit extends EnvironmentGetterInstruction { // static type: string = 'BLOCKL1GASLIMIT'; // static numberOfOperands = 1; @@ -172,7 +157,7 @@ export class Timestamp extends GetterInstruction { // } // } -// export class BlockL2GasLimit extends GetterInstruction { +// export class BlockL2GasLimit extends EnvironmentGetterInstruction { // static type: string = 'BLOCKL2GASLIMIT'; // static numberOfOperands = 1; @@ -189,7 +174,7 @@ export class Timestamp extends GetterInstruction { // } // } -// export class BlockDAGasLimit extends GetterInstruction { +// export class BlockDAGasLimit extends EnvironmentGetterInstruction { // static type: string = 'BLOCKDAGASLIMIT'; // static numberOfOperands = 1; diff --git a/yarn-project/simulator/src/avm/opcodes/instruction_impl.ts b/yarn-project/simulator/src/avm/opcodes/instruction_impl.ts index c31eec89e4b..18ad99bf749 100644 --- a/yarn-project/simulator/src/avm/opcodes/instruction_impl.ts +++ b/yarn-project/simulator/src/avm/opcodes/instruction_impl.ts @@ -1,3 +1,5 @@ +import { type AvmContext } from '../avm_context.js'; +import { type MemoryValue } from '../avm_memory_types.js'; import { OperandType } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; @@ -56,3 +58,25 @@ export abstract class ThreeOperandInstruction extends Instruction { super(); } } + +export abstract class GetterInstruction extends Instruction { + // Informs (de)serialization. See Instruction.deserialize. + static readonly wireFormat: OperandType[] = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT32]; + + constructor(protected indirect: number, protected dstOffset: number) { + super(); + } + + public async execute(context: AvmContext): Promise { + const memoryOperations = { writes: 1, indirect: this.indirect }; + const memory = context.machineState.memory.track(this.type); + context.machineState.consumeGas(this.gasCost(memoryOperations)); + + memory.set(this.dstOffset, this.getValue(context)); + + memory.assert(memoryOperations); + context.machineState.incrementPc(); + } + + protected abstract getValue(env: AvmContext): MemoryValue; +} diff --git a/yarn-project/simulator/src/avm/serialization/bytecode_serialization.ts b/yarn-project/simulator/src/avm/serialization/bytecode_serialization.ts index 80c3d15f139..29d7357a904 100644 --- a/yarn-project/simulator/src/avm/serialization/bytecode_serialization.ts +++ b/yarn-project/simulator/src/avm/serialization/bytecode_serialization.ts @@ -1,3 +1,4 @@ +import { DAGasLeft, L1GasLeft, L2GasLeft } from '../opcodes/context_getters.js'; import { Keccak, Pedersen, Poseidon2, Sha256 } from '../opcodes/hashing.js'; import { Add, @@ -102,9 +103,9 @@ const INSTRUCTION_SET = () => // Machine State // Machine State - Gas - //[L1gasleft.opcode, L1gasleft], - //[L2gasleft.opcode, L2gasleft], - //[Dagasleft.opcode, Dagasleft], + [L1GasLeft.opcode, L1GasLeft], + [L2GasLeft.opcode, L2GasLeft], + [DAGasLeft.opcode, DAGasLeft], // Machine State - Internal Control Flow [Jump.opcode, Jump], [JumpI.opcode, JumpI],