Skip to content

Commit

Permalink
feat: Variable length returns (#5633)
Browse files Browse the repository at this point in the history
This PR adds variable length (different per function) returns for both
public and private functions. In private land and non-avm public
functions, the macros introduce an oracle call for the simulator to
store the returns in the packed values cache (this is not necessary for
avm functions since they do return actual values instead of some public
inputs). The macros previously serialized the returns to the public
inputs, now, the public inputs only contains the hash.

Two new structs, PackedReturns (for private) and FunctionReturns (for
public) are used as return values for the context functions that call
other external functions. They allow easy casting to other types, and
PackedReturns in particular also deals with the fact that in private you
get only the hash back from the call.

None of the new oracles are necessary in the AVM context.
  • Loading branch information
sirasistant committed Apr 10, 2024
1 parent 44e0d8a commit b4a6f17
Show file tree
Hide file tree
Showing 89 changed files with 1,029 additions and 1,025 deletions.
9 changes: 4 additions & 5 deletions l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ library Constants {
uint256 internal constant MAX_FIELD_VALUE = P - 1;

uint256 internal constant ARGS_LENGTH = 16;
uint256 internal constant RETURN_VALUES_LENGTH = 4;
uint256 internal constant MAX_NEW_NOTE_HASHES_PER_CALL = 16;
uint256 internal constant MAX_NEW_NULLIFIERS_PER_CALL = 16;
uint256 internal constant MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL = 4;
Expand Down Expand Up @@ -84,7 +83,7 @@ library Constants {
uint256 internal constant DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE =
0x85864497636cf755ae7bde03f267ce01a520981c21c3682aaf82a631;
uint256 internal constant DEPLOYER_CONTRACT_ADDRESS =
0x1b628eeb6349f2a4c000b703942eb8a625bfe5e6ee34ccc210748cf9ae05af98;
0x00eadf9f983d265e8f496dec4172e75b4a98fe00a99a1b80042a7257b63a3a56;
uint256 internal constant L1_TO_L2_MESSAGE_ORACLE_CALL_LENGTH = 17;
uint256 internal constant MAX_NOTE_FIELDS_LENGTH = 20;
uint256 internal constant GET_NOTE_ORACLE_RETURN_LENGTH = 23;
Expand All @@ -107,9 +106,9 @@ library Constants {
uint256 internal constant NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH = 4;
uint256 internal constant NULLIFIER_KEY_VALIDATION_REQUEST_CONTEXT_LENGTH = 5;
uint256 internal constant PARTIAL_STATE_REFERENCE_LENGTH = 6;
uint256 internal constant PRIVATE_CALL_STACK_ITEM_LENGTH = 210;
uint256 internal constant PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = 207;
uint256 internal constant PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH = 198;
uint256 internal constant PRIVATE_CALL_STACK_ITEM_LENGTH = 207;
uint256 internal constant PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = 204;
uint256 internal constant PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH = 195;
uint256 internal constant STATE_REFERENCE_LENGTH = 8;
uint256 internal constant TX_CONTEXT_DATA_LENGTH = 4;
uint256 internal constant TX_REQUEST_LENGTH = 8;
Expand Down
7 changes: 4 additions & 3 deletions noir-projects/aztec-nr/authwit/src/auth.nr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use dep::aztec::protocol_types::{
constants::{GENERATOR_INDEX__AUTHWIT_INNER, GENERATOR_INDEX__AUTHWIT_OUTER}, hash::pedersen_hash
};
use dep::aztec::{
prelude::Deserialize,
context::{
PrivateContext, PublicContext, Context, gas::GasOpts,
interface::{ContextInterface, PublicContextInterface}
Expand All @@ -17,7 +18,7 @@ global IS_VALID_SELECTOR = 0xabf64ad4; // 4 first bytes of keccak256("IS_VALID()
pub fn assert_current_call_valid_authwit(context: &mut PrivateContext, on_behalf_of: AztecAddress) {
let function_selector = FunctionSelector::from_signature("spend_private_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_private_function(on_behalf_of, function_selector, [inner_hash])[0];
let result: Field = context.call_private_function(on_behalf_of, function_selector, [inner_hash]).unpack_into();
assert(result == IS_VALID_SELECTOR, "Message not authorized by account");
}
// docs:end:assert_current_call_valid_authwit
Expand All @@ -32,13 +33,13 @@ pub fn assert_current_call_valid_authwit_public<TPublicContext>(
let inner_hash = compute_inner_authwit_hash(
[(*context).msg_sender().to_field(), (*context).selector().to_field(), (*context).get_args_hash()]
);
let result = PublicContextInterface::call_public_function(
let result: Field = PublicContextInterface::call_public_function(
context,
on_behalf_of,
function_selector,
[inner_hash],
GasOpts::default()
)[0];
).deserialize_into();
assert(result == IS_VALID_SELECTOR, "Message not authorized by account");
}
// docs:end:assert_current_call_valid_authwit_public
Expand Down
2 changes: 2 additions & 0 deletions noir-projects/aztec-nr/aztec/src/context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ mod gas;

use interface::ContextInterface;
use private_context::PrivateContext;
use private_context::PackedReturns;
use public_context::PublicContext;
use public_context::FunctionReturns;
use avm_context::AvmContext;

struct Context {
Expand Down
26 changes: 13 additions & 13 deletions noir-projects/aztec-nr/aztec/src/context/avm_context.nr
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use dep::protocol_types::{
address::{AztecAddress, EthAddress},
constants::{L1_TO_L2_MESSAGE_LENGTH, NESTED_CALL_L2_GAS_BUFFER, RETURN_VALUES_LENGTH},
header::Header
constants::{L1_TO_L2_MESSAGE_LENGTH, NESTED_CALL_L2_GAS_BUFFER}, header::Header
};
use dep::protocol_types::traits::Serialize;
use dep::protocol_types::abis::function_selector::FunctionSelector;
Expand All @@ -10,6 +9,7 @@ use crate::context::inputs::avm_context_inputs::AvmContextInputs;
use crate::context::interface::ContextInterface;
use crate::context::interface::PublicContextInterface;
use crate::context::gas::GasOpts;
use crate::context::public_context::FunctionReturns;

struct AvmContext {
inputs: AvmContextInputs,
Expand Down Expand Up @@ -132,52 +132,52 @@ impl PublicContextInterface for AvmContext {
send_l2_to_l1_msg(recipient, content);
}

fn call_public_function<ARGS_COUNT>(
fn call_public_function<ARGS_COUNT, RETURNS_COUNT>(
self: &mut Self,
contract_address: AztecAddress,
temporary_function_selector: FunctionSelector,
args: [Field; ARGS_COUNT],
gas_opts: GasOpts
) -> [Field; RETURN_VALUES_LENGTH] {
) -> FunctionReturns<RETURNS_COUNT> {
let results = call(
gas_for_call(gas_opts),
contract_address,
args,
temporary_function_selector.to_field()
);
let data_to_return: [Field; RETURN_VALUES_LENGTH] = results.0;
let data_to_return: [Field; RETURNS_COUNT] = results.0;
let success: u8 = results.1;
assert(success == 1, "Nested call failed!");

data_to_return
FunctionReturns::new(data_to_return)
}

fn static_call_public_function<ARGS_COUNT>(
fn static_call_public_function<ARGS_COUNT, RETURNS_COUNT>(
self: &mut Self,
contract_address: AztecAddress,
temporary_function_selector: FunctionSelector,
args: [Field; ARGS_COUNT],
gas_opts: GasOpts
) -> [Field; RETURN_VALUES_LENGTH] {
let (data_to_return, success): ([Field; RETURN_VALUES_LENGTH], u8) = call_static(
) -> FunctionReturns<RETURNS_COUNT> {
let (data_to_return, success): ([Field; RETURNS_COUNT], u8) = call_static(
gas_for_call(gas_opts),
contract_address,
args,
temporary_function_selector.to_field()
);

assert(success == 1, "Nested static call failed!");
data_to_return
FunctionReturns::new(data_to_return)
}

fn delegate_call_public_function<ARGS_COUNT>(
fn delegate_call_public_function<ARGS_COUNT, RETURNS_COUNT>(
self: &mut Self,
contract_address: AztecAddress,
function_selector: FunctionSelector,
args: [Field; ARGS_COUNT]
) -> [Field; RETURN_VALUES_LENGTH] {
) -> FunctionReturns<RETURNS_COUNT> {
assert(false, "'delegate_call_public_function' not implemented!");
[0; RETURN_VALUES_LENGTH]
FunctionReturns::new([0; RETURNS_COUNT])
}
}

Expand Down
18 changes: 8 additions & 10 deletions noir-projects/aztec-nr/aztec/src/context/interface.nr
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
use dep::protocol_types::{
abis::function_selector::FunctionSelector, address::{AztecAddress, EthAddress}, header::Header,
constants::RETURN_VALUES_LENGTH
};
use dep::protocol_types::{abis::function_selector::FunctionSelector, address::{AztecAddress, EthAddress}, header::Header};

use crate::context::gas::GasOpts;
use crate::context::public_context::FunctionReturns;

trait ContextInterface {
fn push_new_note_hash(&mut self, note_hash: Field);
Expand Down Expand Up @@ -32,25 +30,25 @@ trait PublicContextInterface {
fn consume_l1_to_l2_message(&mut self, content: Field, secret: Field, sender: EthAddress);
fn accumulate_encrypted_logs<N>(&mut self, log: [Field; N]);
fn accumulate_unencrypted_logs<T>(&mut self, log: T);
fn call_public_function<ARGS_COUNT>(
fn call_public_function<ARGS_COUNT, RETURNS_COUNT>(
self: &mut Self,
contract_address: AztecAddress,
function_selector: FunctionSelector,
args: [Field; ARGS_COUNT],
gas_opts: GasOpts
) -> [Field; RETURN_VALUES_LENGTH];
fn static_call_public_function<ARGS_COUNT>(
) -> FunctionReturns<RETURNS_COUNT>;
fn static_call_public_function<ARGS_COUNT, RETURNS_COUNT>(
self: &mut Self,
contract_address: AztecAddress,
function_selector: FunctionSelector,
args: [Field; ARGS_COUNT],
gas_opts: GasOpts
) -> [Field; RETURN_VALUES_LENGTH];
fn delegate_call_public_function<ARGS_COUNT>(
) -> FunctionReturns<RETURNS_COUNT>;
fn delegate_call_public_function<ARGS_COUNT, RETURNS_COUNT>(
self: &mut Self,
contract_address: AztecAddress,
function_selector: FunctionSelector,
args: [Field; ARGS_COUNT]
) -> [Field; RETURN_VALUES_LENGTH];
) -> FunctionReturns<RETURNS_COUNT>;
fn nullifier_exists(self, unsiloed_nullifier: Field, address: AztecAddress) -> bool;
}
73 changes: 53 additions & 20 deletions noir-projects/aztec-nr/aztec/src/context/private_context.nr
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::{
context::{inputs::PrivateContextInputs, interface::ContextInterface},
key::nullifier_key::validate_nullifier_key_against_address, messaging::process_l1_to_l2_message,
hash::hash_args_array,
hash::{hash_args_array, ArgsHasher},
oracle::{
arguments, call_private_function::call_private_function_internal,
arguments, returns, call_private_function::call_private_function_internal,
enqueue_public_function_call::enqueue_public_function_call_internal, context::get_portal_address,
header::get_header_at, nullifier_key::{get_nullifier_key_pair, NullifierKeyPair}
}
Expand All @@ -24,12 +24,11 @@ use dep::protocol_types::{
MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL,
MAX_PUBLIC_DATA_READS_PER_CALL, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL,
MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, MAX_NULLIFIER_READ_REQUESTS_PER_CALL,
MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL, MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL,
RETURN_VALUES_LENGTH
MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL, MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL
},
contrakt::{storage_read::StorageRead, storage_update_request::StorageUpdateRequest},
grumpkin_private_key::GrumpkinPrivateKey, header::Header,
messaging::l2_to_l1_message::L2ToL1Message, utils::reader::Reader, traits::is_empty
messaging::l2_to_l1_message::L2ToL1Message, utils::reader::Reader, traits::{is_empty, Deserialize}
};

// TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165)
Expand All @@ -44,7 +43,7 @@ struct PrivateContext {
min_revertible_side_effect_counter: u32,

args_hash : Field,
return_values : BoundedVec<Field, RETURN_VALUES_LENGTH>,
return_hash : Field,

max_block_number: MaxBlockNumber,

Expand Down Expand Up @@ -130,7 +129,7 @@ impl PrivateContext {
side_effect_counter,
min_revertible_side_effect_counter,
args_hash,
return_values: BoundedVec::new(),
return_hash: 0,
max_block_number: MaxBlockNumber::default(),
note_hash_read_requests: BoundedVec::new(),
nullifier_read_requests: BoundedVec::new(),
Expand All @@ -154,6 +153,11 @@ impl PrivateContext {
get_header_at(block_number, self)
}

pub fn set_return_hash(&mut self, returns_hasher: ArgsHasher) {
returns::pack_returns(returns_hasher.fields);
self.return_hash = returns_hasher.hash();
}

pub fn finish(self) -> PrivateCircuitPublicInputs {
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165)
let encrypted_logs_hash = 0;
Expand All @@ -164,7 +168,7 @@ impl PrivateContext {
let priv_circuit_pub_inputs = PrivateCircuitPublicInputs {
call_context: self.inputs.call_context,
args_hash: self.args_hash,
return_values: self.return_values.storage,
returns_hash: self.return_hash,
min_revertible_side_effect_counter: self.min_revertible_side_effect_counter,
max_block_number: self.max_block_number,
note_hash_read_requests: self.note_hash_read_requests.storage,
Expand Down Expand Up @@ -273,7 +277,7 @@ impl PrivateContext {
contract_address: AztecAddress,
function_selector: FunctionSelector,
args: [Field; ARGS_COUNT]
) -> [Field; RETURN_VALUES_LENGTH] {
) -> PackedReturns {
let args_hash = hash_args_array(args);
assert(args_hash == arguments::pack_arguments(args));
self.call_private_function_with_packed_args(contract_address, function_selector, args_hash, false, false)
Expand All @@ -284,7 +288,7 @@ impl PrivateContext {
contract_address: AztecAddress,
function_selector: FunctionSelector,
args: [Field; ARGS_COUNT]
) -> [Field; RETURN_VALUES_LENGTH] {
) -> PackedReturns {
let args_hash = hash_args_array(args);
assert(args_hash == arguments::pack_arguments(args));
self.call_private_function_with_packed_args(contract_address, function_selector, args_hash, true, false)
Expand All @@ -295,44 +299,44 @@ impl PrivateContext {
contract_address: AztecAddress,
function_selector: FunctionSelector,
args: [Field; ARGS_COUNT]
) -> [Field; RETURN_VALUES_LENGTH] {
) -> PackedReturns {
let args_hash = hash_args_array(args);
assert(args_hash == arguments::pack_arguments(args));
self.call_private_function_with_packed_args(contract_address, function_selector, args_hash, false, true)
}

pub fn call_private_function_no_args(
pub fn call_private_function_no_args<RETURNS_COUNT>(
&mut self,
contract_address: AztecAddress,
function_selector: FunctionSelector
) -> [Field; RETURN_VALUES_LENGTH] {
) -> PackedReturns {
self.call_private_function_with_packed_args(contract_address, function_selector, 0, false, false)
}

pub fn static_call_private_function_no_args(
pub fn static_call_private_function_no_args<RETURNS_COUNT>(
&mut self,
contract_address: AztecAddress,
function_selector: FunctionSelector
) -> [Field; RETURN_VALUES_LENGTH] {
) -> PackedReturns {
self.call_private_function_with_packed_args(contract_address, function_selector, 0, true, false)
}

pub fn delegate_call_private_function_no_args<ARGS_COUNT>(
&mut self,
contract_address: AztecAddress,
function_selector: FunctionSelector
) -> [Field; RETURN_VALUES_LENGTH] {
) -> PackedReturns {
self.call_private_function_with_packed_args(contract_address, function_selector, 0, false, true)
}

pub fn call_private_function_with_packed_args(
pub fn call_private_function_with_packed_args<RETURNS_COUNT>(
&mut self,
contract_address: AztecAddress,
function_selector: FunctionSelector,
args_hash: Field,
is_static_call: bool,
is_delegate_call: bool
) -> [Field; RETURN_VALUES_LENGTH] {
) -> PackedReturns {
let mut is_static_call = is_static_call | self.inputs.call_context.is_static_call;
let item = call_private_function_internal(
contract_address,
Expand Down Expand Up @@ -387,7 +391,7 @@ impl PrivateContext {

self.private_call_stack_hashes.push(item.hash());

item.public_inputs.return_values
PackedReturns::new(item.public_inputs.returns_hash)
}

pub fn call_public_function<ARGS_COUNT>(
Expand Down Expand Up @@ -475,7 +479,7 @@ impl PrivateContext {
public_inputs: PublicCircuitPublicInputs {
call_context: reader.read_struct(CallContext::deserialize),
args_hash: reader.read(),
return_values: [0; RETURN_VALUES_LENGTH],
returns_hash: 0,
nullifier_read_requests: [ReadRequest::empty(); MAX_NULLIFIER_READ_REQUESTS_PER_CALL],
nullifier_non_existent_read_requests: [ReadRequest::empty(); MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL],
contract_storage_update_requests: [StorageUpdateRequest::empty(); MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL],
Expand Down Expand Up @@ -526,3 +530,32 @@ impl PrivateContext {
self.public_call_stack_hashes.push(item.hash());
}
}

struct PackedReturns {
packed_returns: Field,
}

impl PackedReturns {
pub fn new(packed_returns: Field) -> Self {
PackedReturns { packed_returns }
}

pub fn assert_empty(self) {
assert_eq(self.packed_returns, 0);
}

pub fn raw(self) -> Field {
self.packed_returns
}

pub fn unpack<N>(self) -> [Field; N] {
let unpacked: [Field; N] = returns::unpack_returns(self.packed_returns);
assert_eq(self.packed_returns, hash_args_array(unpacked));
unpacked
}

pub fn unpack_into<T, N>(self) -> T where T: Deserialize<N> {
let unpacked: [Field; N] = self.unpack();
Deserialize::deserialize(unpacked)
}
}
Loading

0 comments on commit b4a6f17

Please sign in to comment.