diff --git a/noir-projects/aztec-nr/aztec/src/context.nr b/noir-projects/aztec-nr/aztec/src/context.nr index 9f5ae7efb82..4c87c33339d 100644 --- a/noir-projects/aztec-nr/aztec/src/context.nr +++ b/noir-projects/aztec-nr/aztec/src/context.nr @@ -3,13 +3,13 @@ mod inputs; mod private_context; mod public_context; +mod avm_context; mod interface; -mod avm; -use private_context::PrivateContext; use interface::ContextInterface; +use private_context::PrivateContext; use public_context::PublicContext; -use avm::AVMContext; +use avm_context::AVMContext; struct Context { private: Option<&mut PrivateContext>, diff --git a/noir-projects/aztec-nr/aztec/src/context/avm.nr b/noir-projects/aztec-nr/aztec/src/context/avm.nr deleted file mode 100644 index 96902336a98..00000000000 --- a/noir-projects/aztec-nr/aztec/src/context/avm.nr +++ /dev/null @@ -1,172 +0,0 @@ -use dep::protocol_types::{address::{AztecAddress, EthAddress}, constants::L1_TO_L2_MESSAGE_LENGTH}; -use dep::protocol_types::traits::{Serialize}; -use dep::protocol_types::abis::function_selector::FunctionSelector; - -// Getters that will be converted by the transpiler into their -// own opcodes -struct AVMContext {} - -impl AVMContext { - // Empty new function enables retaining context. syntax - pub fn new() -> Self { - Self {} - } - - // OPCODES - #[oracle(avmOpcodeAddress)] - pub fn address(self) -> AztecAddress {} - - #[oracle(avmOpcodeStorageAddress)] - pub fn storage_address(self) -> AztecAddress {} - - #[oracle(avmOpcodeOrigin)] - pub fn origin(self) -> AztecAddress {} - - #[oracle(avmOpcodeSender)] - pub fn sender(self) -> AztecAddress {} - - #[oracle(avmOpcodePortal)] - pub fn portal(self) -> EthAddress {} - - #[oracle(avmOpcodeFeePerL1Gas)] - pub fn fee_per_l1_gas(self) -> Field {} - - #[oracle(avmOpcodeFeePerL2Gas)] - pub fn fee_per_l2_gas(self) -> Field {} - - #[oracle(avmOpcodeFeePerDaGas)] - pub fn fee_per_da_gas(self) -> Field {} - - #[oracle(avmOpcodeChainId)] - pub fn chain_id(self) -> Field {} - - #[oracle(avmOpcodeVersion)] - pub fn version(self) -> Field {} - - #[oracle(avmOpcodeBlockNumber)] - pub fn block_number(self) -> Field {} - - #[oracle(avmOpcodeTimestamp)] - pub fn timestamp(self) -> Field {} - - // #[oracle(avmOpcodeContractCallDepth)] - // pub fn contract_call_depth(self) -> Field {} - - #[oracle(avmOpcodeNoteHashExists)] - pub fn note_hash_exists(self, note_hash: Field, leaf_index: Field) -> u8 {} - - #[oracle(avmOpcodeEmitNoteHash)] - pub fn emit_note_hash(self, note_hash: Field) {} - - #[oracle(avmOpcodeNullifierExists)] - pub fn nullifier_exists(self, nullifier: Field) -> u8 {} - - #[oracle(avmOpcodeEmitNullifier)] - pub fn emit_nullifier(self, nullifier: Field) {} - - /** - * Emit a log with the given event selector and message. - * - * @param event_selector The event selector for the log. - * @param message The message to emit in the log. - * Should be automatically convertible to [Field; N]. For example str works with - * one char per field. Otherwise you can use CompressedString. - */ - #[oracle(amvOpcodeEmitUnencryptedLog)] - pub fn emit_unencrypted_log(self, event_selector: Field, message: T) {} - - #[oracle(avmOpcodeL1ToL2MsgExists)] - pub fn l1_to_l2_msg_exists(self, msg_hash: Field, msg_leaf_index: Field) -> u8 {} - - #[oracle(avmOpcodeSendL2ToL1Msg)] - pub fn send_l2_to_l1_msg(self, recipient: EthAddress, content: Field) {} - - #[oracle(avmOpcodeCall)] - pub fn call( - self, - gas: [Field; 3], // gas allocation: [l1Gas, l2Gas, daGas] - address: AztecAddress, - args: [Field; ARGS_COUNT], - temporary_function_selector: Field - ) -> ([Field; RET_SIZE], u8) {} - // ^ return data ^ success - - #[oracle(avmOpcodeStaticCall)] - pub fn call_static( - self, - gas: [Field; 3], // gas allocation: [l1Gas, l2Gas, daGas] - address: AztecAddress, - args: [Field; ARGS_COUNT], - // TODO(5110): consider passing in calldata directly - temporary_function_selector: Field - ) -> ([Field; RET_SIZE], u8) {} - // ^ return data ^ success - - //////////////////////////////////////////////////////////////////////////////// - // The functions below allow interface-equivalence with current public/private - //////////////////////////////////////////////////////////////////////////////// - pub fn this_address(self) -> AztecAddress { - self.address() - } - - #[oracle(avmOpcodeSendL2ToL1Msg)] - pub fn message_portal(&mut self, recipient: EthAddress, content: Field) {} - - pub fn consume_l1_to_l2_message( - &mut self, - _msg_key: Field, - _content: Field, - _secret: Field, - _sender: EthAddress - ) { - assert(false, "Not implemented!"); - } - - #[oracle(avmOpcodeEmitNoteHash)] - pub fn push_new_note_hash(self: &mut Self, note_hash: Field) {} - - pub fn push_new_nullifier(self: &mut Self, nullifier: Field, _nullified_commitment: Field) { - // Cannot nullify pending commitments in AVM, so `nullified_commitment` is not used - self.emit_nullifier(nullifier); - } - - pub fn call_public_function( - self: Self, - contract_address: AztecAddress, - temporary_function_selector: FunctionSelector, - args: [Field; ARGS_COUNT] - ) -> [Field; RET_SIZE] { - let gas = [/*l1Gas*/42, /*l2Gas*/24, /*daGas*/420]; - - let results = self.call( - gas, - contract_address, - args, - temporary_function_selector.to_field() - ); - let returnData: [Field; RET_SIZE] = results.0; - let success: u8 = results.1; - assert(success == 1, "Nested call failed!"); - - returnData - } - - pub fn static_call_public_function( - self: Self, - contract_address: AztecAddress, - temporary_function_selector: FunctionSelector, - args: [Field; ARGS_COUNT] - ) -> [Field; RET_SIZE] { - let gas = [/*l1Gas*/42, /*l2Gas*/24, /*daGas*/420]; - - let (returnData, success): ([Field; RET_SIZE], u8) = self.call_static( - gas, - contract_address, - args, - temporary_function_selector.to_field() - ); - - assert(success == 1, "Nested static call failed!"); - returnData - } -} diff --git a/noir-projects/aztec-nr/aztec/src/context/avm_context.nr b/noir-projects/aztec-nr/aztec/src/context/avm_context.nr new file mode 100644 index 00000000000..765b1036016 --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/context/avm_context.nr @@ -0,0 +1,289 @@ +use dep::protocol_types::{address::{AztecAddress, EthAddress}, constants::L1_TO_L2_MESSAGE_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::PublicContextInputs; +use crate::context::interface::ContextInterface; +use crate::context::interface::PublicContextInterface; + +struct AVMContext {} + +impl AVMContext { + pub fn new() -> Self { + AVMContext {} + } + + pub fn origin(self) -> AztecAddress { + origin() + } + pub fn storage_address(self) -> AztecAddress { + storage_address() + } + pub fn fee_per_l1_gas(self) -> Field { + fee_per_l1_gas() + } + pub fn fee_per_l2_gas(self) -> Field { + fee_per_l2_gas() + } + pub fn fee_per_da_gas(self) -> Field { + fee_per_da_gas() + } + /** + * Emit a log with the given event selector and message. + * + * @param event_selector The event selector for the log. + * @param message The message to emit in the log. + * Should be automatically convertible to [Field; N]. For example str works with + * one char per field. Otherwise you can use CompressedString. + */ + pub fn accumulate_unencrypted_logs(&mut self, event_selector: Field, log: T) { + emit_unencrypted_log(event_selector, log); + } + pub fn note_hash_exists(self, note_hash: Field, leaf_index: Field) -> bool { + note_hash_exists(note_hash, leaf_index) == 1 + } + pub fn l1_to_l2_msg_exists(self, msg_hash: Field, msg_leaf_index: Field) -> bool { + l1_to_l2_msg_exists(msg_hash, msg_leaf_index) == 1 + } + + fn call_public_function_raw( + self: &mut Self, + gas: [Field; 3], + contract_address: AztecAddress, + temporary_function_selector: Field, + args: [Field; ARGS_COUNT] + ) -> ([Field; RET_COUNT], u8) { + call(gas, contract_address, args, temporary_function_selector) + } + + fn static_call_public_function_raw( + self: &mut Self, + gas: [Field; 3], + contract_address: AztecAddress, + temporary_function_selector: Field, + args: [Field; ARGS_COUNT] + ) -> ([Field; RET_COUNT], u8) { + call_static(gas, contract_address, args, temporary_function_selector) + } +} + +impl PublicContextInterface for AVMContext { + fn block_number(self) -> Field { + block_number() + } + + fn timestamp(self) -> Field { + timestamp() + } + + fn coinbase(self) -> EthAddress { + assert(false, "'coinbase' not implemented!"); + EthAddress::zero() + } + + fn fee_recipient(self) -> AztecAddress { + assert(false, "'fee_recipient' not implemented!"); + AztecAddress::zero() + } + + fn push_nullifier_read_request(&mut self, nullifier: Field) { + assert(false, "'push_nullifier_read_request' not implemented!"); + } + + fn push_nullifier_non_existent_read_request(&mut self, nullifier: Field) { + assert(false, "'push_nullifier_non_existent_read_request' not implemented!"); + } + + fn accumulate_encrypted_logs(&mut self, log: [Field; N]) { + assert(false, "'accumulate_encrypted_logs' not implemented!"); + } + + fn accumulate_unencrypted_logs(&mut self, log: T) { + let event_selector = 0; + self.accumulate_unencrypted_logs(event_selector, log); + } + + fn consume_l1_to_l2_message(&mut self, content: Field, secret: Field, sender: EthAddress) { + assert(false, "'consume_l1_to_l2_message' not implemented!"); + } + + fn message_portal(&mut self, recipient: EthAddress, content: Field) { + send_l2_to_l1_msg(recipient, content); + } + + fn call_public_function( + self: &mut Self, + contract_address: AztecAddress, + temporary_function_selector: FunctionSelector, + args: [Field; ARGS_COUNT] + ) -> [Field; RETURN_VALUES_LENGTH] { + let gas = [/*l1Gas*/42, /*l2Gas*/24, /*daGas*/420]; + + let results = call( + gas, + contract_address, + args, + temporary_function_selector.to_field() + ); + let returnData: [Field; RETURN_VALUES_LENGTH] = results.0; + let success: u8 = results.1; + assert(success == 1, "Nested call failed!"); + + returnData + } + + fn static_call_public_function( + self: &mut Self, + contract_address: AztecAddress, + temporary_function_selector: FunctionSelector, + args: [Field; ARGS_COUNT] + ) -> [Field; RETURN_VALUES_LENGTH] { + let gas = [/*l1Gas*/42, /*l2Gas*/24, /*daGas*/420]; + + let (returnData, success): ([Field; RETURN_VALUES_LENGTH], u8) = call_static( + gas, + contract_address, + args, + temporary_function_selector.to_field() + ); + + assert(success == 1, "Nested static call failed!"); + returnData + } + + fn delegate_call_public_function( + self: &mut Self, + contract_address: AztecAddress, + function_selector: FunctionSelector, + args: [Field; ARGS_COUNT] + ) -> [Field; RETURN_VALUES_LENGTH] { + assert(false, "'delegate_call_public_function' not implemented!"); + [0; RETURN_VALUES_LENGTH] + } +} + +impl ContextInterface for AVMContext { + fn push_new_note_hash(&mut self, note_hash: Field) { + emit_note_hash(note_hash); + } + fn push_new_nullifier(&mut self, nullifier: Field, _nullified_commitment: Field) { + // Cannot nullify pending commitments in AVM, so `nullified_commitment` is not used + emit_nullifier(nullifier); + } + fn msg_sender(self) -> AztecAddress { + sender() + } + fn this_address(self) -> AztecAddress { + address() + } + fn this_portal_address(self) -> EthAddress { + portal() + } + fn chain_id(self) -> Field { + chain_id() + } + fn version(self) -> Field { + version() + } + fn selector(self) -> FunctionSelector { + assert(false, "'selector' not implemented!"); + FunctionSelector::zero() + } + fn get_header(self) -> Header { + assert(false, "'get_header' not implemented!"); + Header::empty() + } + fn get_args_hash(self) -> Field { + assert(false, "'get_args_hash' not implemented!"); + 0 + } + fn nullifier_exists(self, nullifier: Field) -> bool { + nullifier_exists(nullifier) == 1 + } + fn nullifier_valid_inclusion(self, nullifier: Field) -> bool { + self.nullifier_exists(nullifier) == false + } + + fn nullifier_valid_non_inclusion(self, nullifier: Field) -> bool { + self.nullifier_exists(nullifier) == true + } +} + +// AVM oracles (opcodes) follow, do not use directly. +#[oracle(avmOpcodeAddress)] +fn address() -> AztecAddress {} + +#[oracle(avmOpcodeStorageAddress)] +fn storage_address() -> AztecAddress {} + +#[oracle(avmOpcodeOrigin)] +fn origin() -> AztecAddress {} + +#[oracle(avmOpcodeSender)] +fn sender() -> AztecAddress {} + +#[oracle(avmOpcodePortal)] +fn portal() -> EthAddress {} + +#[oracle(avmOpcodeFeePerL1Gas)] +fn fee_per_l1_gas() -> Field {} + +#[oracle(avmOpcodeFeePerL2Gas)] +fn fee_per_l2_gas() -> Field {} + +#[oracle(avmOpcodeFeePerDaGas)] +fn fee_per_da_gas() -> Field {} + +#[oracle(avmOpcodeChainId)] +fn chain_id() -> Field {} + +#[oracle(avmOpcodeVersion)] +fn version() -> Field {} + +#[oracle(avmOpcodeBlockNumber)] +fn block_number() -> Field {} + +#[oracle(avmOpcodeTimestamp)] +fn timestamp() -> Field {} + +#[oracle(avmOpcodeNoteHashExists)] +fn note_hash_exists(note_hash: Field, leaf_index: Field) -> u8 {} + +#[oracle(avmOpcodeEmitNoteHash)] +fn emit_note_hash(note_hash: Field) {} + +#[oracle(avmOpcodeNullifierExists)] +fn nullifier_exists(nullifier: Field) -> u8 {} + +#[oracle(avmOpcodeEmitNullifier)] +fn emit_nullifier(nullifier: Field) {} + +#[oracle(amvOpcodeEmitUnencryptedLog)] +fn emit_unencrypted_log(event_selector: Field, message: T) {} + +#[oracle(avmOpcodeL1ToL2MsgExists)] +fn l1_to_l2_msg_exists(msg_hash: Field, msg_leaf_index: Field) -> u8 {} + +#[oracle(avmOpcodeSendL2ToL1Msg)] +fn send_l2_to_l1_msg(recipient: EthAddress, content: Field) {} + +#[oracle(avmOpcodeCall)] +fn call( + gas: [Field; 3], // gas allocation: [l1Gas, l2Gas, daGas] + address: AztecAddress, + args: [Field; ARGS_COUNT], + // TODO(5110): consider passing in calldata directly + temporary_function_selector: Field +) -> ([Field; RET_SIZE], u8) {} +// ^ return data ^ success + +#[oracle(avmOpcodeStaticCall)] +fn call_static( + gas: [Field; 3], // gas allocation: [l1Gas, l2Gas, daGas] + address: AztecAddress, + args: [Field; ARGS_COUNT], + // TODO(5110): consider passing in calldata directly + temporary_function_selector: Field +) -> ([Field; RET_SIZE], u8) {} +// ^ return data ^ success \ No newline at end of file diff --git a/noir-projects/aztec-nr/aztec/src/context/interface.nr b/noir-projects/aztec-nr/aztec/src/context/interface.nr index 19a34c90b0b..37bb18868d7 100644 --- a/noir-projects/aztec-nr/aztec/src/context/interface.nr +++ b/noir-projects/aztec-nr/aztec/src/context/interface.nr @@ -1,4 +1,7 @@ -use dep::protocol_types::{abis::function_selector::FunctionSelector, address::{AztecAddress, EthAddress}, header::Header}; +use dep::protocol_types::{ + abis::function_selector::FunctionSelector, address::{AztecAddress, EthAddress}, header::Header, + constants::RETURN_VALUES_LENGTH +}; trait ContextInterface { fn push_new_note_hash(&mut self, note_hash: Field); @@ -11,4 +14,42 @@ trait ContextInterface { fn selector(self) -> FunctionSelector; fn get_args_hash(self) -> Field; fn get_header(self) -> Header; + // nullifiers + fn nullifier_exists(self, nullifier: Field) -> bool; + fn nullifier_valid_inclusion(self, nullifier: Field) -> bool; + fn nullifier_valid_non_inclusion(self, nullifier: Field) -> bool; +} + +// TEMPORARY: This trait is to promote sharing of the current public context +// and the upcoming AVMContext. This will be removed once the AVMContext is the default. +// If you are adding something here, then you should also implement it in the AVMContext. +trait PublicContextInterface { + fn block_number(self) -> Field; + fn timestamp(self) -> Field; + fn coinbase(self) -> EthAddress; + fn fee_recipient(self) -> AztecAddress; + fn push_nullifier_read_request(&mut self, nullifier: Field); + fn push_nullifier_non_existent_read_request(&mut self, nullifier: Field); + fn message_portal(&mut self, recipient: EthAddress, content: Field); + fn consume_l1_to_l2_message(&mut self, content: Field, secret: Field, sender: EthAddress); + fn accumulate_encrypted_logs(&mut self, log: [Field; N]); + fn accumulate_unencrypted_logs(&mut self, log: T); + fn call_public_function( + self: &mut Self, + contract_address: AztecAddress, + function_selector: FunctionSelector, + args: [Field; ARGS_COUNT] + ) -> [Field; RETURN_VALUES_LENGTH]; + fn static_call_public_function( + self: &mut Self, + contract_address: AztecAddress, + function_selector: FunctionSelector, + args: [Field; ARGS_COUNT] + ) -> [Field; RETURN_VALUES_LENGTH]; + fn delegate_call_public_function( + self: &mut Self, + contract_address: AztecAddress, + function_selector: FunctionSelector, + args: [Field; ARGS_COUNT] + ) -> [Field; RETURN_VALUES_LENGTH]; } diff --git a/noir-projects/aztec-nr/aztec/src/context/private_context.nr b/noir-projects/aztec-nr/aztec/src/context/private_context.nr index 99f5580d620..a515688484e 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -1,6 +1,8 @@ use crate::{ context::{inputs::PrivateContextInputs, interface::ContextInterface}, key::nullifier_key::validate_nullifier_key_against_address, messaging::process_l1_to_l2_message, + history::nullifier_inclusion::nullifier_valid_inclusion, + history::nullifier_non_inclusion::nullifier_valid_non_inclusion, oracle::{ arguments, call_private_function::call_private_function_internal, enqueue_public_function_call::enqueue_public_function_call_internal, context::get_portal_address, @@ -23,8 +25,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, @@ -113,6 +116,26 @@ impl ContextInterface for PrivateContext { self.new_nullifiers.push(side_effect); self.side_effect_counter = self.side_effect_counter + 1; } + + fn nullifier_exists(self, nullifier: Field) -> bool { + let header = self.get_header(); + let not_in = nullifier_valid_inclusion(nullifier, header); + let is_in = nullifier_valid_non_inclusion(nullifier, header); + // We want to ensure that the user could not just make the nullifier inclusion + // fail with a bad path, therefore it must either be in or not + let xor = (not_in | is_in) - (not_in & is_in); + xor == false + } + + fn nullifier_valid_inclusion(self, nullifier: Field) -> bool { + let header = self.get_header(); + nullifier_valid_inclusion(nullifier, header) + } + + fn nullifier_valid_non_inclusion(self, nullifier: Field) -> bool { + let header = self.get_header(); + nullifier_valid_non_inclusion(nullifier, header) + } } impl PrivateContext { @@ -150,6 +173,24 @@ impl PrivateContext { get_header_at(block_number, self) } + pub fn nullifier_valid_inclusion_at( + self, + nullifier: Field, + block_number: u32 // The block at which we'll prove that the nullifier exists in the nullifier tree + ) -> bool { + let header = self.get_header_at(block_number); + nullifier_valid_inclusion(nullifier, header) + } + + pub fn nullifier_valid_non_inclusion_at( + self, + nullifier: Field, + block_number: u32 // The block at which we'll prove that the nullifier does not exist in the nullifier tree + ) -> bool { + let header = self.get_header_at(block_number); + nullifier_valid_non_inclusion(nullifier, header) + } + pub fn finish(self) -> PrivateCircuitPublicInputs { // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) let encrypted_logs_hash = [0; NUM_FIELDS_PER_SHA256]; 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 cc5dc60d90e..8400212b1d4 100644 --- a/noir-projects/aztec-nr/aztec/src/context/public_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/public_context.nr @@ -1,6 +1,9 @@ use crate::{ - context::{inputs::PublicContextInputs, interface::ContextInterface}, - messaging::process_l1_to_l2_message, oracle::{arguments, public_call::call_public_function_internal} + context::{inputs::PublicContextInputs, interface::{ContextInterface, PublicContextInterface}}, + messaging::process_l1_to_l2_message, + oracle::{arguments, public_call::call_public_function_internal}, + history::nullifier_inclusion::nullifier_valid_inclusion, + history::nullifier_non_inclusion::nullifier_valid_non_inclusion }; use dep::protocol_types::{ abis::{ @@ -49,6 +52,106 @@ struct PublicContext { prover_address: AztecAddress, } +impl PublicContext { + pub fn new(inputs: PublicContextInputs, args_hash: Field) -> PublicContext { + PublicContext { + inputs, + side_effect_counter: inputs.start_side_effect_counter, + args_hash, + return_values: BoundedVec::new(), + nullifier_read_requests: BoundedVec::new(), + nullifier_non_existent_read_requests: BoundedVec::new(), + contract_storage_update_requests: BoundedVec::new(), + contract_storage_reads: BoundedVec::new(), + public_call_stack_hashes: BoundedVec::new(), + new_note_hashes: BoundedVec::new(), + new_nullifiers: BoundedVec::new(), + new_l2_to_l1_msgs: BoundedVec::new(), + unencrypted_logs_hash: BoundedVec::new(), + unencrypted_logs_preimages_length: 0, + historical_header: inputs.historical_header, + prover_address: AztecAddress::zero() // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) + // encrypted_logs_preimages: Vec::new(), + // unencrypted_logs_preimages: Vec::new(), + } + } + + pub fn call_public_function_no_args( + self: &mut Self, + contract_address: AztecAddress, + function_selector: FunctionSelector + ) -> [Field; RETURN_VALUES_LENGTH] { + self.call_public_function_with_packed_args(contract_address, function_selector, 0, false, false) + } + + pub fn static_call_public_function_no_args( + self: &mut Self, + contract_address: AztecAddress, + function_selector: FunctionSelector + ) -> [Field; RETURN_VALUES_LENGTH] { + self.call_public_function_with_packed_args(contract_address, function_selector, 0, true, false) + } + + pub fn delegate_call_public_function_no_args( + self: &mut Self, + contract_address: AztecAddress, + function_selector: FunctionSelector + ) -> [Field; RETURN_VALUES_LENGTH] { + self.call_public_function_with_packed_args(contract_address, function_selector, 0, false, true) + } + + pub fn call_public_function_with_packed_args( + self: &mut Self, + contract_address: AztecAddress, + function_selector: FunctionSelector, + args_hash: Field, + 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( + contract_address, + function_selector, + args_hash, + side_effect_counter, + is_static_call, + is_delegate_call + ) + } + + pub fn finish(self) -> PublicCircuitPublicInputs { + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) + let unencrypted_logs_hash = [0; NUM_FIELDS_PER_SHA256]; + let unencrypted_log_preimages_length = 0; + + // Compute the public call stack hashes + let pub_circuit_pub_inputs = PublicCircuitPublicInputs { + call_context: self.inputs.call_context, // Done + args_hash: self.args_hash, // Done + nullifier_read_requests: self.nullifier_read_requests.storage, + nullifier_non_existent_read_requests: self.nullifier_non_existent_read_requests.storage, + contract_storage_update_requests: self.contract_storage_update_requests.storage, + contract_storage_reads: self.contract_storage_reads.storage, + return_values: self.return_values.storage, + new_note_hashes: self.new_note_hashes.storage, + new_nullifiers: self.new_nullifiers.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.inputs.start_side_effect_counter, + end_side_effect_counter: self.side_effect_counter, + unencrypted_logs_hash, + unencrypted_log_preimages_length, + historical_header: self.inputs.historical_header, + prover_address: self.prover_address, + reverted: false + }; + pub_circuit_pub_inputs + } +} + impl ContextInterface for PublicContext { fn msg_sender(self) -> AztecAddress { self.inputs.call_context.msg_sender @@ -98,95 +201,60 @@ impl ContextInterface for PublicContext { self.side_effect_counter = self.side_effect_counter + 1; } -} + fn nullifier_exists(self, nullifier: Field) -> bool { + let header = self.get_header(); + let not_in = nullifier_valid_inclusion(nullifier, header); + let is_in = nullifier_valid_non_inclusion(nullifier, header); + // We want to ensure that the user could not just make the nullifier inclusion + // fail with a bad path, therefore it must either be in or not + let xor = (not_in | is_in) - (not_in & is_in); + xor == false + } -impl PublicContext { - pub fn new(inputs: PublicContextInputs, args_hash: Field) -> PublicContext { - PublicContext { - inputs, - side_effect_counter: inputs.start_side_effect_counter, - args_hash, - return_values: BoundedVec::new(), - nullifier_read_requests: BoundedVec::new(), - nullifier_non_existent_read_requests: BoundedVec::new(), - contract_storage_update_requests: BoundedVec::new(), - contract_storage_reads: BoundedVec::new(), - public_call_stack_hashes: BoundedVec::new(), - new_note_hashes: BoundedVec::new(), - new_nullifiers: BoundedVec::new(), - new_l2_to_l1_msgs: BoundedVec::new(), - unencrypted_logs_hash: BoundedVec::new(), - unencrypted_logs_preimages_length: 0, - historical_header: inputs.historical_header, - prover_address: AztecAddress::zero() // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) - // encrypted_logs_preimages: Vec::new(), - // unencrypted_logs_preimages: Vec::new(), - } + fn nullifier_valid_inclusion(self, nullifier: Field) -> bool { + nullifier_valid_inclusion(nullifier, self.get_header()) } - pub fn block_number(self) -> Field { + fn nullifier_valid_non_inclusion(self, nullifier: Field) -> bool { + nullifier_valid_non_inclusion(nullifier, self.get_header()) + } +} + +impl PublicContextInterface for PublicContext { + fn block_number(self) -> Field { self.inputs.public_global_variables.block_number } - pub fn timestamp(self) -> Field { + fn timestamp(self) -> Field { self.inputs.public_global_variables.timestamp } - pub fn coinbase(self) -> EthAddress { + fn coinbase(self) -> EthAddress { self.inputs.public_global_variables.coinbase } - pub fn fee_recipient(self) -> AztecAddress { + fn fee_recipient(self) -> AztecAddress { self.inputs.public_global_variables.fee_recipient } - pub fn finish(self) -> PublicCircuitPublicInputs { - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) - let unencrypted_logs_hash = [0; NUM_FIELDS_PER_SHA256]; - let unencrypted_log_preimages_length = 0; - - // Compute the public call stack hashes - let pub_circuit_pub_inputs = PublicCircuitPublicInputs { - call_context: self.inputs.call_context, // Done - args_hash: self.args_hash, // Done - nullifier_read_requests: self.nullifier_read_requests.storage, - nullifier_non_existent_read_requests: self.nullifier_non_existent_read_requests.storage, - contract_storage_update_requests: self.contract_storage_update_requests.storage, - contract_storage_reads: self.contract_storage_reads.storage, - return_values: self.return_values.storage, - new_note_hashes: self.new_note_hashes.storage, - new_nullifiers: self.new_nullifiers.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.inputs.start_side_effect_counter, - end_side_effect_counter: self.side_effect_counter, - unencrypted_logs_hash, - unencrypted_log_preimages_length, - historical_header: self.inputs.historical_header, - prover_address: self.prover_address, - reverted: false - }; - pub_circuit_pub_inputs - } - - pub fn push_nullifier_read_request(&mut self, nullifier: Field) { + fn push_nullifier_read_request(&mut self, nullifier: Field) { let request = ReadRequest { value: nullifier, counter: self.side_effect_counter }; self.nullifier_read_requests.push(request); self.side_effect_counter = self.side_effect_counter + 1; } - pub fn push_nullifier_non_existent_read_request(&mut self, nullifier: Field) { + fn push_nullifier_non_existent_read_request(&mut self, nullifier: Field) { let request = ReadRequest { value: nullifier, counter: self.side_effect_counter }; self.nullifier_non_existent_read_requests.push(request); self.side_effect_counter = self.side_effect_counter + 1; } - pub fn message_portal(&mut self, recipient: EthAddress, content: Field) { + fn message_portal(&mut self, recipient: EthAddress, content: Field) { let message = L2ToL1Message { recipient, content }; self.new_l2_to_l1_msgs.push(message); } - pub fn consume_l1_to_l2_message(&mut self, content: Field, secret: Field, sender: EthAddress) { + fn consume_l1_to_l2_message(&mut self, content: Field, secret: Field, sender: EthAddress) { let this = (*self).this_address(); let nullifier = process_l1_to_l2_message( self.historical_header.state.l1_to_l2_message_tree.root, @@ -202,19 +270,19 @@ impl PublicContext { self.push_new_nullifier(nullifier, 0) } - pub fn accumulate_encrypted_logs(&mut self, log: [Field; N]) { + fn accumulate_encrypted_logs(&mut self, log: [Field; N]) { let _void1 = self; let _void2 = log; // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) } - pub fn accumulate_unencrypted_logs(&mut self, log: T) { + fn accumulate_unencrypted_logs(&mut self, log: T) { let _void1 = self; let _void2 = log; // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) } - pub fn call_public_function( + fn call_public_function( self: &mut Self, contract_address: AztecAddress, function_selector: FunctionSelector, @@ -225,7 +293,7 @@ impl PublicContext { self.call_public_function_with_packed_args(contract_address, function_selector, args_hash, false, false) } - pub fn static_call_public_function( + fn static_call_public_function( self: &mut Self, contract_address: AztecAddress, function_selector: FunctionSelector, @@ -236,7 +304,7 @@ impl PublicContext { self.call_public_function_with_packed_args(contract_address, function_selector, args_hash, true, false) } - pub fn delegate_call_public_function( + fn delegate_call_public_function( self: &mut Self, contract_address: AztecAddress, function_selector: FunctionSelector, @@ -246,50 +314,4 @@ impl PublicContext { assert(args_hash == arguments::pack_arguments(args)); self.call_public_function_with_packed_args(contract_address, function_selector, args_hash, false, true) } - - pub fn call_public_function_no_args( - self: &mut Self, - contract_address: AztecAddress, - function_selector: FunctionSelector - ) -> [Field; RETURN_VALUES_LENGTH] { - self.call_public_function_with_packed_args(contract_address, function_selector, 0, false, false) - } - - pub fn static_call_public_function_no_args( - self: &mut Self, - contract_address: AztecAddress, - function_selector: FunctionSelector - ) -> [Field; RETURN_VALUES_LENGTH] { - self.call_public_function_with_packed_args(contract_address, function_selector, 0, true, false) - } - - pub fn delegate_call_public_function_no_args( - self: &mut Self, - contract_address: AztecAddress, - function_selector: FunctionSelector - ) -> [Field; RETURN_VALUES_LENGTH] { - self.call_public_function_with_packed_args(contract_address, function_selector, 0, false, true) - } - - pub fn call_public_function_with_packed_args( - self: &mut Self, - contract_address: AztecAddress, - function_selector: FunctionSelector, - args_hash: Field, - 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( - contract_address, - function_selector, - args_hash, - side_effect_counter, - is_static_call, - is_delegate_call - ) - } } diff --git a/noir-projects/aztec-nr/aztec/src/history/contract_inclusion.nr b/noir-projects/aztec-nr/aztec/src/history/contract_inclusion.nr index 1a16ea986c7..bd14d32fe86 100644 --- a/noir-projects/aztec-nr/aztec/src/history/contract_inclusion.nr +++ b/noir-projects/aztec-nr/aztec/src/history/contract_inclusion.nr @@ -1,64 +1,53 @@ use dep::protocol_types::{ - address::{AztecAddress, EthAddress}, - contract_class_id::ContractClassId, - grumpkin_point::GrumpkinPoint, - hash::silo_nullifier, - constants::DEPLOYER_CONTRACT_ADDRESS + address::{AztecAddress, EthAddress}, contract_class_id::ContractClassId, + grumpkin_point::GrumpkinPoint, hash::silo_nullifier, constants::DEPLOYER_CONTRACT_ADDRESS }; use dep::std::merkle::compute_merkle_root; -use crate::{ - context::PrivateContext, - history::{ - nullifier_inclusion::prove_nullifier_inclusion_at, - nullifier_non_inclusion::prove_nullifier_not_included_at, - } -}; +use crate::context::PrivateContext; -pub fn prove_contract_deployment_at( - contract_address: AztecAddress, - block_number: u32, - context: PrivateContext -) { +pub fn prove_contract_deployment_at(contract_address: AztecAddress, block_number: u32, context: PrivateContext) { // Compute deployment nullifier - let nullifier = silo_nullifier(AztecAddress::from_field(DEPLOYER_CONTRACT_ADDRESS), contract_address.to_field()); + let nullifier = silo_nullifier( + AztecAddress::from_field(DEPLOYER_CONTRACT_ADDRESS), + contract_address.to_field() + ); // Prove its inclusion - prove_nullifier_inclusion_at(nullifier, block_number, context); + assert( + context.nullifier_valid_inclusion_at(nullifier, block_number), "Cannot prove contract deployment nullifier inclusion" + ); } -pub fn prove_contract_non_deployment_at( - contract_address: AztecAddress, - block_number: u32, - context: PrivateContext -) { +pub fn prove_contract_non_deployment_at(contract_address: AztecAddress, block_number: u32, context: PrivateContext) { // Compute deployment nullifier - let nullifier = silo_nullifier(AztecAddress::from_field(DEPLOYER_CONTRACT_ADDRESS), contract_address.to_field()); + let nullifier = silo_nullifier( + AztecAddress::from_field(DEPLOYER_CONTRACT_ADDRESS), + contract_address.to_field() + ); // Prove its non-inclusion - prove_nullifier_not_included_at(nullifier, block_number, context); + assert( + context.nullifier_valid_non_inclusion_at(nullifier, block_number), "Cannot prove contract deployment nullifier non-inclusion" + ); } -pub fn prove_contract_initialization_at( - contract_address: AztecAddress, - block_number: u32, - context: PrivateContext -) { +pub fn prove_contract_initialization_at(contract_address: AztecAddress, block_number: u32, context: PrivateContext) { // Compute initialization nullifier let nullifier = silo_nullifier(contract_address, contract_address.to_field()); // Prove its inclusion - prove_nullifier_inclusion_at(nullifier, block_number, context); + assert( + context.nullifier_valid_inclusion_at(nullifier, block_number), "Cannot prove contract initialization nullifier inclusion" + ); } -pub fn prove_contract_non_initialization_at( - contract_address: AztecAddress, - block_number: u32, - context: PrivateContext -) { +pub fn prove_contract_non_initialization_at(contract_address: AztecAddress, block_number: u32, context: PrivateContext) { // Compute initialization nullifier let nullifier = silo_nullifier(contract_address, contract_address.to_field()); // Prove its non-inclusion - prove_nullifier_not_included_at(nullifier, block_number, context); + assert( + context.nullifier_valid_non_inclusion_at(nullifier, block_number), "Cannot prove contract initialization nullifier non-inclusion" + ); } diff --git a/noir-projects/aztec-nr/aztec/src/history/note_validity.nr b/noir-projects/aztec-nr/aztec/src/history/note_validity.nr index c929f15eca9..dfc293ca824 100644 --- a/noir-projects/aztec-nr/aztec/src/history/note_validity.nr +++ b/noir-projects/aztec-nr/aztec/src/history/note_validity.nr @@ -2,7 +2,8 @@ use crate::{ context::PrivateContext, history::{ note_inclusion::prove_note_inclusion, note_inclusion::_note_inclusion, - nullifier_non_inclusion::prove_note_not_nullified, nullifier_non_inclusion::_nullifier_non_inclusion + nullifier_non_inclusion::prove_note_not_nullified, + nullifier_non_inclusion::nullifier_valid_non_inclusion }, note::{utils::compute_siloed_nullifier, note_interface::NoteInterface} }; @@ -23,5 +24,5 @@ pub fn prove_note_validity_at( _note_inclusion(note, header); let nullifier = compute_siloed_nullifier(note, context); - _nullifier_non_inclusion(nullifier, header); + assert(nullifier_valid_non_inclusion(nullifier, header), "Proving nullifier non-inclusion failed!"); } diff --git a/noir-projects/aztec-nr/aztec/src/history/nullifier_inclusion.nr b/noir-projects/aztec-nr/aztec/src/history/nullifier_inclusion.nr index b047870a218..103d0084f2b 100644 --- a/noir-projects/aztec-nr/aztec/src/history/nullifier_inclusion.nr +++ b/noir-projects/aztec-nr/aztec/src/history/nullifier_inclusion.nr @@ -3,62 +3,31 @@ use dep::protocol_types::header::Header; use crate::{ context::{PrivateContext, ContextInterface}, - oracle::get_nullifier_membership_witness::get_nullifier_membership_witness, + oracle::get_nullifier_membership_witness::{get_nullifier_membership_witness, NullifierMembershipWitness}, note::{utils::compute_siloed_nullifier, note_interface::NoteInterface} }; -fn _nullifier_inclusion(nullifier: Field, header: Header) { +/** + * This function checks if a nullifier is included in the nullifier tree at a given header. + * IMPORTANT: If the function returns false, you don't have a proof of non-inclusion. + * + * @param nullifier The nullifier to prove inclusion of + * @param header The header at which we'll prove that the nullifier exists in the nullifier tree + * @return true if the nullifier is included in the nullifier tree, false otherwise + */ +pub fn nullifier_valid_inclusion(nullifier: Field, header: Header) -> bool { // 1) Get the membership witness of the nullifier - let witness = get_nullifier_membership_witness(header.global_variables.block_number as u32, nullifier); - + get_nullifier_membership_witness(header.global_variables.block_number as u32, nullifier) // 2) Check that the witness we obtained matches the nullifier - assert(witness.leaf_preimage.nullifier == nullifier, "Nullifier does not match value in witness"); - + .filter(|witness: NullifierMembershipWitness| witness.leaf_preimage.nullifier == nullifier) // 3) Compute the nullifier tree leaf - let nullifier_leaf = witness.leaf_preimage.hash(); - // 4) Prove that the nullifier is in the nullifier tree - assert( + .filter(|witness: NullifierMembershipWitness| { + let nullifier_leaf = witness.leaf_preimage.hash(); header.state.partial.nullifier_tree.root - == compute_merkle_root(nullifier_leaf, witness.index, witness.path), "Proving nullifier inclusion failed" - ); + == compute_merkle_root(nullifier_leaf, witness.index, witness.path) + }) // --> Now we have traversed the trees all the way up to archive root and verified that the nullifier - // was included in the nullifier tree. -} - -pub fn prove_nullifier_inclusion( - nullifier: Field, - context: TContext -) where TContext: ContextInterface { - _nullifier_inclusion(nullifier, context.get_header()); -} - -pub fn prove_nullifier_inclusion_at( - nullifier: Field, - block_number: u32, // The block at which we'll prove that the nullifier exists in the nullifier tree - context: PrivateContext -) { - let header = context.get_header_at(block_number); - - _nullifier_inclusion(nullifier, header); -} - -pub fn prove_note_is_nullified( - note: Note, - context: &mut PrivateContext -) where Note: NoteInterface { - let nullifier = compute_siloed_nullifier(note, context); - - _nullifier_inclusion(nullifier, context.historical_header); -} - -pub fn prove_note_is_nullified_at( - note: Note, - block_number: u32, - context: &mut PrivateContext -) where Note: NoteInterface { - let nullifier = compute_siloed_nullifier(note, context); - let header = context.get_header_at(block_number); - - _nullifier_inclusion(nullifier, header); + // was included in the nullifier tree. + .is_some() } diff --git a/noir-projects/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr b/noir-projects/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr index 1d17e2d96d8..2293390d7d1 100644 --- a/noir-projects/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr +++ b/noir-projects/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr @@ -2,45 +2,48 @@ use dep::std::merkle::compute_merkle_root; use dep::protocol_types::{header::Header, utils::field::{full_field_less_than, full_field_greater_than}}; use crate::{ context::PrivateContext, note::{utils::compute_siloed_nullifier, note_interface::NoteInterface}, - oracle::get_nullifier_membership_witness::get_low_nullifier_membership_witness + oracle::get_nullifier_membership_witness::{get_low_nullifier_membership_witness, NullifierMembershipWitness} }; -pub fn _nullifier_non_inclusion(nullifier: Field, header: Header) { +/** + * This function checks if a nullifier is NOT included in the nullifier tree at a given header. + * IMPORTANT: If the function returns false, you don't have a proof of inclusion. + * + * @param nullifier The nullifier to prove NON inclusion of + * @param header The header at which we'll prove that the nullifier NOT exists in the nullifier tree + * @return true if the nullifier is NOT included in the nullifier tree, false otherwise + */ +pub fn nullifier_valid_non_inclusion(nullifier: Field, header: Header) -> bool { // 1) Get the membership witness of a low nullifier of the nullifier - let witness = get_low_nullifier_membership_witness(header.global_variables.block_number as u32, nullifier); - + Option::some(get_low_nullifier_membership_witness(header.global_variables.block_number as u32, nullifier)) // 3) Prove that the nullifier is not included in the nullifier tree - // 3.a) Compute the low nullifier leaf and prove that it is in the nullifier tree - let low_nullifier_leaf = witness.leaf_preimage.hash(); - assert( + .filter(|witness: NullifierMembershipWitness| { + let low_nullifier_leaf = witness.leaf_preimage.hash(); header.state.partial.nullifier_tree.root - == compute_merkle_root(low_nullifier_leaf, witness.index, witness.path), "Proving nullifier non-inclusion failed: Could not prove low nullifier inclusion" - ); - + == compute_merkle_root(low_nullifier_leaf, witness.index, witness.path) + }) // 3.b) Prove that the low nullifier is smaller than the nullifier - assert( - full_field_less_than(witness.leaf_preimage.nullifier, nullifier), "Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed" - ); - + .filter(|witness: NullifierMembershipWitness| full_field_less_than(witness.leaf_preimage.nullifier, nullifier)) // 3.c) Prove that the low nullifier is pointing "over" the nullifier to prove that the nullifier is not // included in the nullifier tree (or to 0 if the to-be-inserted nullifier is the largest of all) - assert( + .filter(|witness: NullifierMembershipWitness| full_field_greater_than(witness.leaf_preimage.next_nullifier, nullifier) - | (witness.leaf_preimage.next_index == 0), "Proving nullifier non-inclusion failed: low_nullifier.next_value > nullifier.value check failed" - ); + | (witness.leaf_preimage.next_index == 0) + ) // --> Now we have traversed the trees all the way up to archive root and verified that the nullifier // was not yet included in the nullifier tree. + .is_some() } pub fn prove_nullifier_not_included(nullifier: Field, context: PrivateContext) { - _nullifier_non_inclusion(nullifier, context.historical_header); + assert(nullifier_valid_non_inclusion(nullifier, context.historical_header), "Proving nullifier non-inclusion failed!"); } pub fn prove_nullifier_not_included_at(nullifier: Field, block_number: u32, context: PrivateContext) { let header = context.get_header_at(block_number); - _nullifier_non_inclusion(nullifier, header); + assert(nullifier_valid_non_inclusion(nullifier, header), "Proving nullifier non-inclusion failed!"); } pub fn prove_note_not_nullified( @@ -49,7 +52,7 @@ pub fn prove_note_not_nullified( ) where Note: NoteInterface { let nullifier = compute_siloed_nullifier(note, context); - _nullifier_non_inclusion(nullifier, context.historical_header); + assert(nullifier_valid_non_inclusion(nullifier, context.historical_header), "Proving nullifier non-inclusion failed!"); } pub fn prove_note_not_nullified_at( @@ -60,5 +63,5 @@ pub fn prove_note_not_nullified_at( let nullifier = compute_siloed_nullifier(note, context); let header = context.get_header_at(block_number); - _nullifier_non_inclusion(nullifier, header); + assert(nullifier_valid_non_inclusion(nullifier, header), "Proving nullifier non-inclusion failed!"); } diff --git a/noir-projects/aztec-nr/aztec/src/initializer.nr b/noir-projects/aztec-nr/aztec/src/initializer.nr index 5b8727a364d..751d6d1c4e5 100644 --- a/noir-projects/aztec-nr/aztec/src/initializer.nr +++ b/noir-projects/aztec-nr/aztec/src/initializer.nr @@ -1,13 +1,11 @@ use dep::protocol_types::{ - hash::{silo_nullifier, pedersen_hash}, - constants::GENERATOR_INDEX__CONSTRUCTOR, - abis::function_selector::FunctionSelector, + hash::{silo_nullifier, pedersen_hash}, constants::GENERATOR_INDEX__CONSTRUCTOR, + abis::function_selector::FunctionSelector }; use crate::{ context::{PrivateContext, PublicContext, ContextInterface}, - oracle::get_contract_instance::get_contract_instance, - history::nullifier_inclusion::prove_nullifier_inclusion, + oracle::get_contract_instance::get_contract_instance }; pub fn mark_as_initialized(context: &mut TContext) where TContext: ContextInterface { @@ -17,7 +15,7 @@ pub fn mark_as_initialized(context: &mut TContext) where TContext: Con pub fn assert_is_initialized(context: &mut TContext) where TContext: ContextInterface { let init_nullifier = compute_contract_initialization_nullifier(*context); - prove_nullifier_inclusion(init_nullifier, *context); + assert((*context).nullifier_valid_inclusion(init_nullifier)); } pub fn compute_contract_initialization_nullifier(context: TContext) -> Field where TContext: ContextInterface { @@ -33,13 +31,18 @@ pub fn compute_unsiloed_contract_initialization_nullifier(context: TCo } pub fn assert_initialization_matches_address_preimage(context: TContext) where TContext: ContextInterface { - let address = context.this_address(); + let address = context.this_address(); let instance = get_contract_instance(address); let expected_init = compute_initialization_hash(context.selector(), context.get_args_hash()); assert(instance.initialization_hash == expected_init, "Initialization hash does not match"); - assert((instance.deployer.is_zero()) | (instance.deployer == context.msg_sender()), "Initializer address is not the contract deployer"); + assert( + (instance.deployer.is_zero()) | (instance.deployer == context.msg_sender()), "Initializer address is not the contract deployer" + ); } pub fn compute_initialization_hash(init_selector: FunctionSelector, init_args_hash: Field) -> Field { - pedersen_hash([init_selector.to_field(), init_args_hash], GENERATOR_INDEX__CONSTRUCTOR) -} \ No newline at end of file + pedersen_hash( + [init_selector.to_field(), init_args_hash], + GENERATOR_INDEX__CONSTRUCTOR + ) +} diff --git a/noir-projects/aztec-nr/aztec/src/oracle/get_nullifier_membership_witness.nr b/noir-projects/aztec-nr/aztec/src/oracle/get_nullifier_membership_witness.nr index 22d97991fda..0e8870df62b 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/get_nullifier_membership_witness.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/get_nullifier_membership_witness.nr @@ -4,7 +4,7 @@ use dep::protocol_types::{ }; // INDEX_LENGTH + NULLIFIER_LEAF_PREIMAGE_LENGTH + NULLIFIER_TREE_HEIGHT -global NULLIFIER_MEMBERSHIP_WITNESS: Field = 24; +global NULLIFIER_MEMBERSHIP_WITNESS_LENGTH: Field = 24; struct NullifierMembershipWitness { index: Field, @@ -13,7 +13,7 @@ struct NullifierMembershipWitness { } impl NullifierMembershipWitness { - pub fn deserialize(fields: [Field; NULLIFIER_MEMBERSHIP_WITNESS]) -> Self { + pub fn deserialize(fields: [Field; NULLIFIER_MEMBERSHIP_WITNESS_LENGTH]) -> Self { let leaf_preimage_fields = arr_copy_slice(fields, [0; NULLIFIER_LEAF_PREIMAGE_LENGTH], 1); Self { index: fields[0], @@ -31,7 +31,7 @@ impl NullifierMembershipWitness { fn get_low_nullifier_membership_witness_oracle( _block_number: u32, _nullifier: Field -) -> [Field; NULLIFIER_MEMBERSHIP_WITNESS] {} +) -> [Field; NULLIFIER_MEMBERSHIP_WITNESS_LENGTH] {} // Nullifier here refers to the nullifier we are looking to get non-inclusion proof for (by proving that a lower // nullifier's next_value is bigger than the nullifier) @@ -44,11 +44,19 @@ unconstrained pub fn get_low_nullifier_membership_witness(block_number: u32, nul fn get_nullifier_membership_witness_oracle( _block_number: u32, _nullifier: Field -) -> [Field; NULLIFIER_MEMBERSHIP_WITNESS] {} +) -> [Field; 1 + NULLIFIER_MEMBERSHIP_WITNESS_LENGTH] {} +// ^ found? -// Nullifier here refers to the nullifier we are looking to get non-inclusion proof for (by proving that a lower -// nullifier's next_value is bigger than the nullifier) -unconstrained pub fn get_nullifier_membership_witness(block_number: u32, nullifier: Field) -> NullifierMembershipWitness { +unconstrained pub fn get_nullifier_membership_witness( + block_number: u32, + nullifier: Field +) -> Option { let fields = get_nullifier_membership_witness_oracle(block_number, nullifier); - NullifierMembershipWitness::deserialize(fields) + let found = fields[0]; + if found == 0 { + Option::none() + } else { + let witness_fields = arr_copy_slice(fields, [0; NULLIFIER_MEMBERSHIP_WITNESS_LENGTH], 1); + Option::some(NullifierMembershipWitness::deserialize(witness_fields)) + } } 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 88a009c47e1..1bed81cb60b 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 @@ -28,7 +28,8 @@ contract AvmTest { use dep::aztec::state_vars::PublicMutable; use dep::aztec::protocol_types::{address::{AztecAddress, EthAddress}, constants::L1_TO_L2_MESSAGE_LENGTH}; use dep::aztec::protocol_types::abis::function_selector::FunctionSelector; - use dep::aztec::protocol_types::traits::{ToField}; + use dep::aztec::protocol_types::traits::ToField; + use dep::aztec::protocol_types::constants::RETURN_VALUES_LENGTH; use dep::compressed_string::CompressedString; // avm lib @@ -168,7 +169,7 @@ contract AvmTest { ************************************************************************/ #[aztec(public-vm)] fn get_address() -> pub AztecAddress { - context.address() + context.this_address() } #[aztec(public-vm)] @@ -178,7 +179,7 @@ contract AvmTest { #[aztec(public-vm)] fn get_sender() -> pub AztecAddress { - context.sender() + context.msg_sender() } #[aztec(public-vm)] @@ -188,7 +189,7 @@ contract AvmTest { #[aztec(public-vm)] fn get_portal() -> pub EthAddress { - context.portal() + context.this_portal_address() } #[aztec(public-vm)] @@ -233,14 +234,14 @@ contract AvmTest { #[aztec(public-vm)] fn emit_unencrypted_log() { - context.emit_unencrypted_log(/*event_selector=*/ 5, /*message=*/ [10, 20, 30]); - context.emit_unencrypted_log(/*event_selector=*/ 8, /*message=*/ "Hello, world!"); + context.accumulate_unencrypted_logs(/*event_selector=*/ 5, /*message=*/ [10, 20, 30]); + context.accumulate_unencrypted_logs(/*event_selector=*/ 8, /*message=*/ "Hello, world!"); let s: CompressedString<2,44> = CompressedString::from_string("A long time ago, in a galaxy far far away..."); - context.emit_unencrypted_log(/*event_selector=*/ 10, /*message=*/ s); + context.accumulate_unencrypted_logs(/*event_selector=*/ 10, /*message=*/ s); } #[aztec(public-vm)] - fn note_hash_exists(note_hash: Field, leaf_index: Field) -> pub u8 { + fn note_hash_exists(note_hash: Field, leaf_index: Field) -> pub bool { context.note_hash_exists(note_hash, leaf_index) } @@ -258,16 +259,16 @@ contract AvmTest { // Use the standard context interface to check for a nullifier #[aztec(public-vm)] - fn nullifier_exists(nullifier: Field) -> pub u8 { + fn nullifier_exists(nullifier: Field) -> pub bool { context.nullifier_exists(nullifier) } // Use the standard context interface to emit a new nullifier #[aztec(public-vm)] fn emit_nullifier_and_check(nullifier: Field) { - context.emit_nullifier(nullifier); + context.push_new_nullifier(nullifier, 0); let exists = context.nullifier_exists(nullifier); - assert(exists == 1, "Nullifier was just created, but its existence wasn't detected!"); + assert(exists, "Nullifier was just created, but its existence wasn't detected!"); } // Create the same nullifier twice (shouldn't work!) @@ -279,7 +280,7 @@ contract AvmTest { } #[aztec(public-vm)] - fn l1_to_l2_msg_exists(msg_hash: Field, msg_leaf_index: Field) -> pub u8 { + fn l1_to_l2_msg_exists(msg_hash: Field, msg_leaf_index: Field) -> pub bool { context.l1_to_l2_msg_exists(msg_hash, msg_leaf_index) } @@ -295,7 +296,7 @@ contract AvmTest { let gas = [/*l1Gas*/42, /*l2Gas*/24, /*daGas*/420]; // Nested call - let results = context.call(gas, context.address(), [argA, argB], selector); + let results = context.call_public_function_raw(gas, context.this_address(), selector, [argA, argB]); let returnData: [Field; 1] = results.0; // this explicit size ^ is necessary to ensure that retSize is compile-time // (ensure the returnData is in a HeapArray not a HeapVector) @@ -313,7 +314,7 @@ contract AvmTest { let selector = FunctionSelector::from_signature("add_args_return(Field,Field)"); // Nested call using standard context interface function - let returnData: [Field; 1] = context.call_public_function(context.address(), selector, [argA, argB]); + let returnData: [Field; RETURN_VALUES_LENGTH] = context.call_public_function(context.this_address(), selector, [argA, argB]); // this explicit size ^ is necessary to ensure that retSize is compile-time // (ensure the returnData is in a HeapArray not a HeapVector) @@ -327,7 +328,7 @@ contract AvmTest { let selector = FunctionSelector::from_signature("add_args_return(Field,Field)").to_field(); let gas = [/*l1Gas*/42, /*l2Gas*/24, /*daGas*/420]; - let (resultData, success): ([Field; 1], u8) = context.call_static(gas, context.address(), [argA, argB], selector); + let (resultData, success): ([Field; 1], u8) = context.static_call_public_function_raw(gas, context.this_address(), selector, [argA, argB]); (resultData[0], success) } @@ -339,7 +340,7 @@ contract AvmTest { let gas = [/*l1Gas*/42, /*l2Gas*/24, /*daGas*/420]; let calldata: [Field; 1] = [20]; - let (_returnData, success): ([Field; 0], u8) = context.call_static(gas, context.address(), calldata, selector); + let (_returnData, success): ([Field; 0], u8) = context.static_call_public_function_raw(gas, context.this_address(), selector, calldata); success } @@ -349,7 +350,7 @@ contract AvmTest { fn nested_static_call_to_add(argA: Field, argB: Field) -> pub Field { let selector = FunctionSelector::from_signature("add_args_return(Field,Field)"); - let resultData: [Field; 1] = context.static_call_public_function(context.address(), selector, [argA, argB]); + let resultData: [Field; RETURN_VALUES_LENGTH] = context.static_call_public_function(context.this_address(), selector, [argA, argB]); resultData[0] } @@ -360,6 +361,6 @@ contract AvmTest { let selector = FunctionSelector::from_signature("set_storage_single(Field)"); let calldata: [Field; 1] = [20]; - let _resultData: [Field; 0] = context.static_call_public_function(context.address(), selector, calldata); + let _resultData: [Field; RETURN_VALUES_LENGTH] = context.static_call_public_function(context.this_address(), selector, calldata); } } diff --git a/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr b/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr index 9fc57911d51..876cab3bdd7 100644 --- a/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr @@ -15,10 +15,6 @@ contract InclusionProofs { }, note_inclusion::{prove_note_inclusion, prove_note_inclusion_at}, note_validity::{prove_note_validity, prove_note_validity_at}, - nullifier_inclusion::{ - prove_nullifier_inclusion, prove_nullifier_inclusion_at, prove_note_is_nullified, - prove_note_is_nullified_at - }, nullifier_non_inclusion::{prove_note_not_nullified, prove_note_not_nullified_at}, public_value_inclusion::{prove_public_value_inclusion, prove_public_value_inclusion_at} }; @@ -186,17 +182,19 @@ contract InclusionProofs { ) { if (use_block_number) { // docs:start:prove_nullifier_inclusion - prove_nullifier_inclusion_at(nullifier, block_number, context); + assert( + context.nullifier_valid_inclusion_at(nullifier, block_number), "Witness not found for nullifier!" + ); // docs:end:prove_nullifier_inclusion } else { - prove_nullifier_inclusion(nullifier, context); + assert(context.nullifier_valid_inclusion(nullifier), "Witness not found for nullifier!"); } } // Proves nullifier existed at latest block #[aztec(public)] fn test_nullifier_inclusion_from_public(nullifier: Field) { - prove_nullifier_inclusion(nullifier, context); + assert(context.nullifier_valid_inclusion(nullifier), "Witness not found for nullifier!"); } #[aztec(private)] 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 2a2da7ae8ce..d96acfef3fc 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,9 +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}, - oracle::get_contract_instance::get_contract_instance, - initializer::assert_is_initialized, + deploy::deploy_contract as aztec_deploy_contract, context::{PublicContext, Context}, + oracle::get_contract_instance::get_contract_instance, initializer::assert_is_initialized }; struct Storage { diff --git a/yarn-project/circuit-types/src/interfaces/nullifier_tree.ts b/yarn-project/circuit-types/src/interfaces/nullifier_tree.ts index a3a1bd1a8d1..c782aaac492 100644 --- a/yarn-project/circuit-types/src/interfaces/nullifier_tree.ts +++ b/yarn-project/circuit-types/src/interfaces/nullifier_tree.ts @@ -24,6 +24,10 @@ export class NullifierMembershipWitness { public readonly siblingPath: SiblingPath, ) {} + public static empty(): NullifierMembershipWitness { + return new NullifierMembershipWitness(0n, NullifierLeafPreimage.empty(), SiblingPath.empty(NULLIFIER_TREE_HEIGHT)); + } + /** * Returns a field array representation of a nullifier witness. * @returns A field array representation of a nullifier witness. diff --git a/yarn-project/circuit-types/src/sibling_path/sibling_path.ts b/yarn-project/circuit-types/src/sibling_path/sibling_path.ts index fcfe0eb4d33..182989af8da 100644 --- a/yarn-project/circuit-types/src/sibling_path/sibling_path.ts +++ b/yarn-project/circuit-types/src/sibling_path/sibling_path.ts @@ -38,6 +38,15 @@ export class SiblingPath { return new SiblingPath(size, bufs); } + /** + * Returns an empty (invalid) sibling path of a given size. + * @param size - The size of the sibling path. + * @returns An empty (invalid) sibling path of a given size. + */ + public static empty(size: N): SiblingPath { + return new SiblingPath(size, new Array(size).fill(Buffer.alloc(32))); + } + /** * Constructor. * @param pathSize - The size of the sibling path. diff --git a/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts b/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts index 579dc24f13d..03900136f0f 100644 --- a/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts +++ b/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts @@ -8,7 +8,7 @@ import { setup } from './fixtures/utils.js'; process.env.AVM_ENABLED = 'absofrigginlutely'; const TIMEOUT = 100_000; -describe('e2e_nested_contract', () => { +describe('e2e_avm_simulator', () => { jest.setTimeout(TIMEOUT); let wallet: Wallet; diff --git a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts index fcb4959f8fc..4f55902a435 100644 --- a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts @@ -113,11 +113,9 @@ describe('e2e_inclusion_proofs_contract', () => { it('should throw when testing if note is not nullified at the current block', async () => { await expect( contract.methods.test_note_not_nullified(owner, true, currentBlockNumber, true).send().wait(), - ).rejects.toThrow( - /Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed/, - ); + ).rejects.toThrow(/nullifier_valid_non_inclusion/); await expect(contract.methods.test_note_not_nullified(owner, false, 0n, true).send().wait()).rejects.toThrow( - /Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed/, + /nullifier_valid_non_inclusion/, ); }); @@ -131,11 +129,9 @@ describe('e2e_inclusion_proofs_contract', () => { const blockNumber = await pxe.getBlockNumber(); await expect( contract.methods.test_note_validity(owner, true, blockNumber, true).send().wait(), - ).rejects.toThrow( - /Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed/, - ); + ).rejects.toThrow(/nullifier_valid_non_inclusion/); await expect(contract.methods.test_note_validity(owner, false, 0n, true).send().wait()).rejects.toThrow( - /Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed/, + /nullifier_valid_non_inclusion/, ); }); @@ -242,10 +238,10 @@ describe('e2e_inclusion_proofs_contract', () => { await expect( contract.methods.test_nullifier_inclusion(randomNullifier, true, blockNumber).send().wait(), - ).rejects.toThrow(`Nullifier witness not found for nullifier ${randomNullifier.toString()} at block`); + ).rejects.toThrow(/nullifier_valid_inclusion/); await expect(contract.methods.test_nullifier_inclusion(randomNullifier, false, 0n).send().wait()).rejects.toThrow( - `Nullifier witness not found for nullifier ${randomNullifier.toString()} at block`, + /nullifier_valid_inclusion/, ); }); }); @@ -267,7 +263,7 @@ describe('e2e_inclusion_proofs_contract', () => { // Or that the positive call fails when trying to prove in the older block await expect( contract.methods.test_contract_inclusion(address, olderBlock, testDeploy, testInit).simulate(), - ).rejects.toThrow(/not found/); + ).rejects.toThrow(/nullifier_valid_inclusion/); }; it('proves public deployment of a contract', async () => { diff --git a/yarn-project/simulator/src/acvm/oracle/oracle.ts b/yarn-project/simulator/src/acvm/oracle/oracle.ts index 17510858cfc..f7486bb6f11 100644 --- a/yarn-project/simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/oracle.ts @@ -1,4 +1,4 @@ -import { MerkleTreeId, UnencryptedL2Log } from '@aztec/circuit-types'; +import { MerkleTreeId, NullifierMembershipWitness, UnencryptedL2Log } from '@aztec/circuit-types'; import { RETURN_VALUES_LENGTH } from '@aztec/circuits.js'; import { EventSelector, FunctionSelector } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; @@ -97,10 +97,12 @@ export class Oracle { const parsedNullifier = fromACVMField(nullifier); const witness = await this.typedOracle.getNullifierMembershipWitness(parsedBlockNumber, parsedNullifier); + if (!witness) { - throw new Error(`Nullifier witness not found for nullifier ${parsedNullifier} at block ${parsedBlockNumber}.`); + return [toACVMField(0), ...NullifierMembershipWitness.empty().toFields().map(toACVMField)]; + } else { + return [toACVMField(1), ...witness.toFields().map(toACVMField)]; } - return witness.toFields().map(toACVMField); } async getLowNullifierMembershipWitness(