Skip to content

Commit

Permalink
feat: add public call stack item
Browse files Browse the repository at this point in the history
  • Loading branch information
alexghr committed Mar 18, 2024
1 parent ec21ccb commit 9f1dd8f
Show file tree
Hide file tree
Showing 43 changed files with 272 additions and 149 deletions.
4 changes: 2 additions & 2 deletions l1-contracts/slither_output.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,9 +329,9 @@ src/core/messagebridge/Inbox.sol#L148-L153
Impact: Informational
Confidence: Medium
- [ ] ID-36
Variable [Constants.LOGS_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L130) is too similar to [Constants.NOTE_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L123)
Variable [Constants.LOGS_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L131) is too similar to [Constants.NOTE_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L124)

src/core/libraries/ConstantsGen.sol#L130
src/core/libraries/ConstantsGen.sol#L131


- [ ] ID-37
Expand Down
11 changes: 6 additions & 5 deletions l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,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 MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 15000;
uint256 internal constant MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 20000;
uint256 internal constant MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS = 500;
uint256 internal constant MAX_PACKED_BYTECODE_SIZE_PER_UNCONSTRAINED_FUNCTION_IN_FIELDS = 500;
uint256 internal constant REGISTERER_CONTRACT_CLASS_REGISTERED_MAGIC_VALUE =
Expand All @@ -97,7 +97,7 @@ library Constants {
uint256 internal constant MAX_NOTES_PER_PAGE = 10;
uint256 internal constant VIEW_NOTE_ORACLE_RETURN_LENGTH = 212;
uint256 internal constant AZTEC_ADDRESS_LENGTH = 1;
uint256 internal constant CALL_CONTEXT_LENGTH = 7;
uint256 internal constant CALL_CONTEXT_LENGTH = 6;
uint256 internal constant CONTENT_COMMITMENT_LENGTH = 7;
uint256 internal constant CONTRACT_INSTANCE_LENGTH = 6;
uint256 internal constant CONTRACT_STORAGE_READ_LENGTH = 2;
Expand All @@ -112,9 +112,10 @@ 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 = 213;
uint256 internal constant PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = 210;
uint256 internal constant PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH = 202;
uint256 internal constant PRIVATE_CALL_STACK_ITEM_LENGTH = 212;
uint256 internal constant PUBLIC_CALL_STACK_ITEM_LENGTH = 205;
uint256 internal constant PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = 209;
uint256 internal constant PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH = 201;
uint256 internal constant STATE_REFERENCE_LENGTH = 8;
uint256 internal constant TX_CONTEXT_DATA_LENGTH = 4;
uint256 internal constant TX_REQUEST_LENGTH = 8;
Expand Down
33 changes: 11 additions & 22 deletions noir-projects/aztec-nr/aztec/src/context/private_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ use dep::protocol_types::{
MAX_NEW_NOTE_HASHES_PER_CALL, MAX_NEW_L2_TO_L1_MSGS_PER_CALL, MAX_NEW_NULLIFIERS_PER_CALL,
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, NUM_FIELDS_PER_SHA256, RETURN_VALUES_LENGTH
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,
NUM_FIELDS_PER_SHA256, RETURN_VALUES_LENGTH
},
contrakt::{storage_read::StorageRead, storage_update_request::StorageUpdateRequest},
grumpkin_private_key::GrumpkinPrivateKey, hash::hash_args, header::Header,
Expand Down Expand Up @@ -332,7 +333,6 @@ impl PrivateContext {
is_delegate_call
);

assert_eq(item.public_inputs.call_context.side_effect_counter, self.side_effect_counter);
assert_eq(item.public_inputs.start_side_effect_counter, self.side_effect_counter);
self.side_effect_counter = item.public_inputs.end_side_effect_counter + 1;

Expand Down Expand Up @@ -441,14 +441,19 @@ impl PrivateContext {

let mut reader = Reader::new(fields);

// Note: Not using PublicCirclePublicInputs::deserialize here, because everything below args_hash is 0 and
// there is no more data in fields because there is only ENQUEUE_PUBLIC_FUNCTION_CALL_RETURN_SIZE fields!
// Note: _Not_ using PublicCirclePublicInputs::deserialize here
// the data returned by the oracle call is compressed and can't be used with ::deserialize
// read the relevant values manually and fill the rest with 0s
let item = PublicCallStackItem {
contract_address: AztecAddress::from_field(reader.read()),
function_data: reader.read_struct(FunctionData::deserialize),
public_inputs: PublicCircuitPublicInputs {
call_context: reader.read_struct(CallContext::deserialize),
args_hash: reader.read(),
// the counter that enqueued the call
start_side_effect_counter: reader.read() as u32,
// everything below is 0 (see comment above)
end_side_effect_counter: 0,
return_values: [0; RETURN_VALUES_LENGTH],
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],
Expand All @@ -458,8 +463,6 @@ impl PrivateContext {
new_note_hashes: [SideEffect::empty(); MAX_NEW_NOTE_HASHES_PER_CALL],
new_nullifiers: [SideEffectLinkedToNoteHash::empty(); MAX_NEW_NULLIFIERS_PER_CALL],
new_l2_to_l1_msgs: [L2ToL1Message::empty(); MAX_NEW_L2_TO_L1_MSGS_PER_CALL],
start_side_effect_counter: 0,
end_side_effect_counter: 0,
unencrypted_logs_hash: [0; NUM_FIELDS_PER_SHA256],
unencrypted_log_preimages_length: 0,
historical_header: Header::empty(),
Expand All @@ -473,7 +476,7 @@ impl PrivateContext {
assert(contract_address.eq(item.contract_address));
assert(function_selector.eq(item.function_data.selector));

assert_eq(item.public_inputs.call_context.side_effect_counter, self.side_effect_counter);
assert_eq(item.public_inputs.start_side_effect_counter, self.side_effect_counter);
// We increment the sideffect counter by one, to account for the call itself being a side effect.
self.side_effect_counter = self.side_effect_counter + 1;

Expand All @@ -483,20 +486,6 @@ impl PrivateContext {
assert(item.public_inputs.call_context.is_delegate_call == is_delegate_call);
assert(item.public_inputs.call_context.is_static_call == is_static_call);

if (is_delegate_call) {
// For delegate calls, we also constrain the execution context address for the nested call to be equal to our address.
assert(
item.public_inputs.call_context.storage_contract_address.eq(self.inputs.call_context.storage_contract_address)
);
assert(item.public_inputs.call_context.msg_sender.eq(self.inputs.call_context.msg_sender));
} else {
// For non-delegate calls, we also constrain the execution context address for the nested call to be equal to the address we called.
assert(item.public_inputs.call_context.storage_contract_address.eq(contract_address));
assert(
item.public_inputs.call_context.msg_sender.eq(self.inputs.call_context.storage_contract_address)
);
}

self.public_call_stack_hashes.push(item.hash());
}
}
27 changes: 20 additions & 7 deletions noir-projects/aztec-nr/aztec/src/context/public_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -279,17 +279,30 @@ impl PublicContext {
is_static_call: bool,
is_delegate_call: bool
) -> [Field; RETURN_VALUES_LENGTH] {
let side_effect_counter = self.side_effect_counter;
// TODO get next value from output of `call_public_function_internal`
self.side_effect_counter += 1;

call_public_function_internal(
let item = call_public_function_internal(
contract_address,
function_selector,
args_hash,
side_effect_counter,
self.side_effect_counter,
is_static_call,
is_delegate_call
)
);

assert_eq(item.public_inputs.start_side_effect_counter, self.side_effect_counter);
self.side_effect_counter = item.public_inputs.end_side_effect_counter + 1;

assert(contract_address.eq(item.contract_address));
assert(function_selector.eq(item.function_data.selector));

assert(args_hash == item.public_inputs.args_hash);

// Assert that the call context of the enqueued call generated by the oracle matches our request.
assert(item.public_inputs.call_context.is_delegate_call == is_delegate_call);
assert(item.public_inputs.call_context.is_static_call == is_static_call);

// this blows up contract deployments
// self.public_call_stack_hashes.push(item.hash());

item.public_inputs.return_values
}
}
15 changes: 10 additions & 5 deletions noir-projects/aztec-nr/aztec/src/oracle/public_call.nr
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use dep::protocol_types::{abis::function_selector::FunctionSelector, address::AztecAddress, constants::RETURN_VALUES_LENGTH};
use dep::protocol_types::{
abis::function_selector::FunctionSelector, address::AztecAddress,
abis::public_call_stack_item::PublicCallStackItem, constants::PUBLIC_CALL_STACK_ITEM_LENGTH
};

#[oracle(callPublicFunction)]
fn call_public_function_oracle(
Expand All @@ -8,7 +11,7 @@ fn call_public_function_oracle(
_side_effect_counter: u32,
_is_static_call: bool,
_is_delegate_call: bool
) -> [Field; RETURN_VALUES_LENGTH] {}
) -> [Field; PUBLIC_CALL_STACK_ITEM_LENGTH] {}

unconstrained pub fn call_public_function_internal(
contract_address: AztecAddress,
Expand All @@ -17,13 +20,15 @@ unconstrained pub fn call_public_function_internal(
side_effect_counter: u32,
is_static_call: bool,
is_delegate_call: bool
) -> [Field; RETURN_VALUES_LENGTH] {
call_public_function_oracle(
) -> PublicCallStackItem {
let fields = call_public_function_oracle(
contract_address,
function_selector,
args_hash,
side_effect_counter,
is_static_call,
is_delegate_call
)
);

PublicCallStackItem::deserialize(fields)
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ struct CallContext {

is_delegate_call : bool,
is_static_call : bool,

side_effect_counter : u32,
}
// docs:end:call-context

Expand All @@ -28,7 +26,6 @@ impl CallContext {
assert(self.function_selector.to_field() == 0);
assert(self.is_delegate_call == false);
assert(self.is_static_call == false);
assert(self.side_effect_counter == 0);
}
}

Expand All @@ -41,7 +38,6 @@ impl Eq for CallContext {
& call_context.function_selector.eq(self.function_selector)
& (call_context.is_delegate_call == self.is_delegate_call)
& (call_context.is_static_call == self.is_static_call)
& (call_context.side_effect_counter == self.side_effect_counter)
}
}

Expand All @@ -60,7 +56,6 @@ impl Serialize<CALL_CONTEXT_LENGTH> for CallContext {
self.function_selector.to_field(),
self.is_delegate_call as Field,
self.is_static_call as Field,
self.side_effect_counter as Field,
]
}
}
Expand All @@ -74,7 +69,6 @@ impl Deserialize<CALL_CONTEXT_LENGTH> for CallContext {
function_selector: FunctionSelector::from_field(serialized[3]),
is_delegate_call: serialized[4] as bool,
is_static_call: serialized[5] as bool,
side_effect_counter: serialized[6] as u32,
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,6 @@ fn empty_hash() {
let hash = inputs.hash();

// Value from private_circuit_public_inputs.test.ts "computes empty item hash" test
let test_data_empty_hash = 0x2b5ba01a6b73b68b4f44196e2dea49afd4076333e2dee8eddc9186e080f18201;
let test_data_empty_hash = 0x2745ec62624afeb19b86af3d440db1f8c3432e1d17a074c75cb8f44999fd3fae;
assert_eq(hash, test_data_empty_hash);
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use crate::abis::{function_data::FunctionData, public_circuit_public_inputs::PublicCircuitPublicInputs};
use crate::address::AztecAddress;
use crate::constants::GENERATOR_INDEX__CALL_STACK_ITEM;
use crate::traits::Hash;
use crate::constants::{GENERATOR_INDEX__CALL_STACK_ITEM, PUBLIC_CALL_STACK_ITEM_LENGTH};
use crate::traits::{Hash, Serialize, Deserialize};
use crate::utils::reader::Reader;

struct PublicCallStackItem {
contract_address: AztecAddress,
public_inputs: PublicCircuitPublicInputs,
function_data: FunctionData,
public_inputs: PublicCircuitPublicInputs,
// True if this call stack item represents a request to execute a function rather than a
// fulfilled execution. Used when enqueuing calls from private to public functions.
is_execution_request: bool,
Expand Down Expand Up @@ -45,6 +46,44 @@ impl PublicCallStackItem {
}
}

impl Serialize<PUBLIC_CALL_STACK_ITEM_LENGTH> for PublicCallStackItem {
fn serialize(self) -> [Field; PUBLIC_CALL_STACK_ITEM_LENGTH] {
let mut fields: BoundedVec<Field, PUBLIC_CALL_STACK_ITEM_LENGTH> = BoundedVec::new();

fields.push(self.contract_address.to_field());
fields.extend_from_array(self.function_data.serialize());
fields.extend_from_array(self.public_inputs.serialize());
fields.push(self.is_execution_request as Field);

assert_eq(fields.len(), PUBLIC_CALL_STACK_ITEM_LENGTH);

fields.storage
}
}

impl Deserialize<PUBLIC_CALL_STACK_ITEM_LENGTH> for PublicCallStackItem {
fn deserialize(serialized: [Field; PUBLIC_CALL_STACK_ITEM_LENGTH]) -> Self {
let mut reader = Reader::new(serialized);
let item = Self {
contract_address: reader.read_struct(AztecAddress::deserialize),
function_data: reader.read_struct(FunctionData::deserialize),
public_inputs: reader.read_struct(PublicCircuitPublicInputs::deserialize),
is_execution_request: reader.read() as bool,
};

item
}
}

impl Eq for PublicCallStackItem {
fn eq(self, other: Self) -> bool {
self.contract_address.eq(other.contract_address) &
self.function_data.eq(other.function_data) &
self.public_inputs.eq(other.public_inputs) &
self.is_execution_request.eq(other.is_execution_request)
}
}

mod tests {
use crate::{
abis::{
Expand All @@ -55,6 +94,23 @@ mod tests {
address::AztecAddress, constants::GENERATOR_INDEX__CALL_STACK_ITEM, traits::Hash
};

#[test]
fn serialize_smoke() {
let item: PublicCallStackItem = dep::std::unsafe::zeroed();
let serialized = item.serialize();
let deserialized = PublicCallStackItem::deserialize(serialized);
assert(item.eq(deserialized));
}

#[test]
fn serialize_exec_request_smoke() {
let mut item: PublicCallStackItem = dep::std::unsafe::zeroed();
item.is_execution_request = true;
let serialized = item.serialize();
let deserialized = PublicCallStackItem::deserialize(serialized);
assert(item.eq(deserialized));
}

#[test]
fn compute_call_stack_item_request_hash() {
let contract_address = AztecAddress::from_field(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,6 @@ fn empty_hash() {
let hash = inputs.hash();

// Value from public_circuit_public_inputs.test.ts "computes empty item hash" test
let test_data_empty_hash = 0x083ac560a513d670a7f50f0a3052d42cb9816b7b643e62025b8278652ad637ab;
let test_data_empty_hash = 0x19de99e09b94a6372bc831be78d7107c0eb9e2836a308f37dc47147a5e11bb2c;
assert_eq(hash, test_data_empty_hash);
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ global INITIAL_L2_BLOCK_NUM: Field = 1;
global BLOB_SIZE_IN_BYTES: Field = 126976;

// CONTRACT CLASS CONSTANTS
global MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS: u64 = 15000;
global MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS: u64 = 20000;
// Bytecode size for private functions is per function, not for the entire contract.
// Note that private functions bytecode includes a mix of acir and brillig.
global MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS: u64 = 500;
Expand Down Expand Up @@ -146,7 +146,7 @@ global VIEW_NOTE_ORACLE_RETURN_LENGTH: u64 = 212;

// LENGTH OF STRUCTS SERIALIZED TO FIELDS
global AZTEC_ADDRESS_LENGTH = 1;
global CALL_CONTEXT_LENGTH: u64 = 7;
global CALL_CONTEXT_LENGTH: u64 = 6;
global CONTENT_COMMITMENT_LENGTH: u64 = 7;
global CONTRACT_INSTANCE_LENGTH: u64 = 6;
global CONTRACT_STORAGE_READ_LENGTH: u64 = 2;
Expand All @@ -161,13 +161,14 @@ global L2_TO_L1_MESSAGE_LENGTH: u64 = 2;
global NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH = 4;
global NULLIFIER_KEY_VALIDATION_REQUEST_CONTEXT_LENGTH = 5;
global PARTIAL_STATE_REFERENCE_LENGTH: u64 = 6;
global PRIVATE_CALL_STACK_ITEM_LENGTH: u64 = 213;
global PRIVATE_CALL_STACK_ITEM_LENGTH: u64 = 212;
global PUBLIC_CALL_STACK_ITEM_LENGTH: u64 = 205;
// Change this ONLY if you have changed the PrivateCircuitPublicInputs structure.
// In other words, if the structure/size of the public inputs of a function call changes then we should change this
// constant as well PRIVATE_CALL_STACK_ITEM_LENGTH
global PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH: u64 = 210;
global PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH: u64 = 209;
// Change this ONLY if you have changed the PublicCircuitPublicInputs structure.
global PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH: u64 = 202;
global PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH: u64 = 201;
global STATE_REFERENCE_LENGTH: u64 = 8; // 2 for snap + 8 for partial
global TX_CONTEXT_DATA_LENGTH: u64 = 4;
global TX_REQUEST_LENGTH: u64 = 8; // 2 + TX_CONTEXT_DATA_LENGTH + FUNCTION_DATA_LENGTH
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ impl PrivateCircuitPublicInputsBuilder {
portal_contract_address,
function_selector: function_data.selector,
is_delegate_call: false,
is_static_call: false,
side_effect_counter: 0
is_static_call: false
};
public_inputs.call_context = call_context;
public_inputs.args_hash = args_hash;
Expand All @@ -92,7 +91,7 @@ impl PrivateCircuitPublicInputsBuilder {
private_call_stack_hashes: self.private_call_stack_hashes.storage,
public_call_stack_hashes: self.public_call_stack_hashes.storage,
new_l2_to_l1_msgs: self.new_l2_to_l1_msgs.storage,
start_side_effect_counter: self.call_context.side_effect_counter,
start_side_effect_counter: 1,
end_side_effect_counter: 10,
encrypted_logs_hash: self.encrypted_logs_hash,
unencrypted_logs_hash: self.unencrypted_logs_hash,
Expand Down
Loading

0 comments on commit 9f1dd8f

Please sign in to comment.