diff --git a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/address.nr b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/address.nr index bef85cc3a63..ecfdc5d90ba 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/address.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/address.nr @@ -1,11 +1,16 @@ use crate::utils; // Aztec address -struct Address{ +struct Address { inner : Field } -impl Address{ +impl Address { + pub fn ZERO() -> Self { + Self { + inner: 0 + } + } pub fn default() -> Self { Self { @@ -44,6 +49,11 @@ struct EthAddress{ } impl EthAddress{ + pub fn ZERO() -> Self { + Self { + inner: 0 + } + } pub fn default() -> Self { Self { diff --git a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr index 87fec27fd84..1fc3139d510 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr @@ -289,7 +289,9 @@ pub fn contract_logic(private_call : PrivateCallData, public_inputs : &mut Kerne public_inputs.end.new_nullifiers.push(new_contract_address_nullifier); } else { // non-contract deployments must specify contract address being interacted with - assert(storage_contract_address.to_field() != 0, "contract address can't be 0 for non-contract deployment related transactions"); + // TODO - Allow special characters in error message. + // assert(storage_contract_address.to_field() != 0, "contract address can't be 0 for non-contract deployment related transactions"); + assert(storage_contract_address.to_field() != 0, "contract address cannot be 0"); /* We need to compute the root of the contract tree, starting from the function's VK: * - Compute the vk_hash (done above) @@ -302,7 +304,9 @@ pub fn contract_logic(private_call : PrivateCallData, public_inputs : &mut Kerne // Ensures that if the function is internal, only the contract itself can call it if (private_call.call_stack_item.function_data().is_internal) { let msg_sender = private_call.call_stack_item.public_inputs().call_context.msg_sender; - assert(storage_contract_address.eq(msg_sender), "call is internal, but msg_sender is not self"); + // TODO - Allow special characters in error message. + // assert(storage_contract_address.eq(msg_sender), "call is internal, but msg_sender is not self"); + assert(storage_contract_address.eq(msg_sender), "call is internal but msg_sender is not self"); } // The logic below ensures that the contract exists in the contracts tree @@ -321,7 +325,9 @@ pub fn contract_logic(private_call : PrivateCallData, public_inputs : &mut Kerne private_call.contract_leaf_membership_witness.sibling_path); let purported_contract_tree_root = private_call.call_stack_item.public_inputs().historical_block_data.contract_tree_root(); - assert_eq(computed_contract_tree_root, purported_contract_tree_root, "computed_contract_tree_root doesn't match purported_contract_tree_root"); + // TODO - Allow special characters in error message. + // assert_eq(computed_contract_tree_root, purported_contract_tree_root, "computed_contract_tree_root doesn't match purported_contract_tree_root"); + assert_eq(computed_contract_tree_root, purported_contract_tree_root, "computed_contract_tree_root does not match purported_contract_tree_root"); } } diff --git a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_inner.nr b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_inner.nr index af82f8ea8e1..86e0fd51181 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_inner.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_inner.nr @@ -3,7 +3,6 @@ use crate::common; use crate::mocked::{Proof, AggregationObject, verify_previous_kernel_state}; use crate::transaction::request::TxRequest; use crate::abis::{ - combined_constant_data::CombinedConstantData, previous_kernel_data::PreviousKernelData, private_kernel::private_call_data::PrivateCallData, new_contract_data::NewContractData, @@ -27,7 +26,6 @@ impl PrivateKernelInputsInner { let popped_private_call_hash = public_inputs.end.private_call_stack.pop(); let calculated_this_private_call_hash = private_call.call_stack_item.hash(); - assert(popped_private_call_hash == calculated_this_private_call_hash, "calculated private_call_hash does not match provided private_call_hash at the top of the callstack"); } @@ -35,7 +33,7 @@ impl PrivateKernelInputsInner { let purported_contract_tree_root = self.private_call.call_stack_item.public_inputs().historical_block_data.contract_tree_root(); let previous_kernel_contract_tree_root = self.previous_kernel.public_inputs.constants.block_data.contract_tree_root(); - assert(purported_contract_tree_root == previous_kernel_contract_tree_root, "purported_contract_tree_root doesn't match previous_kernel_contract_tree_root"); + assert(purported_contract_tree_root == previous_kernel_contract_tree_root, "purported_contract_tree_root does not match previous_kernel_contract_tree_root"); } fn validate_inputs(self) { @@ -93,3 +91,631 @@ impl PrivateKernelInputsInner { public_inputs.finish() } } + + +mod tests { + use crate::private_kernel_inner::PrivateKernelInputsInner; + use crate::abis::{ + private_circuit_public_inputs::PrivateCircuitPublicInputs, + read_request_membership_witness::ReadRequestMembershipWitness, + }; + use crate::tests::{ + testing_harness::{ + create_previous_kernel_data, + create_private_call_data, + generate_read_requests, + PrivateAppInputs, + non_zero_items, + }, + apps::{ + constructor::constructor_app, + deposit::deposit_app, + }, + }; + use crate::address::Address; + use crate::hash::compute_logs_hash; + use dep::aztec::constants_gen::{ + MAX_READ_REQUESTS_PER_CALL, + MAX_READ_REQUESTS_PER_TX, + MAX_NEW_COMMITMENTS_PER_CALL, + MAX_NEW_COMMITMENTS_PER_TX, + MAX_NEW_NULLIFIERS_PER_CALL, + MAX_NEW_NULLIFIERS_PER_TX, + EMPTY_NULLIFIED_COMMITMENT, + MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, + MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, + MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, + MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, + MAX_NEW_L2_TO_L1_MSGS_PER_CALL, + MAX_NEW_L2_TO_L1_MSGS_PER_TX, + NOTE_HASH_TREE_HEIGHT, + }; + + fn build_inputs( + function: fn (PrivateAppInputs, T) -> PrivateCircuitPublicInputs, + app_params: T, + ) -> PrivateKernelInputsInner { + let msg_sender = Address::from_field(27); + let params = dep::std::unsafe::zeroed(); + let previous_kernel = create_previous_kernel_data( + false, + constructor_app, + params, + msg_sender, + ); + + let (private_call, _) = create_private_call_data( + false, + function, + app_params, + msg_sender, + ); + + PrivateKernelInputsInner { + previous_kernel, + private_call, + } + } + + #[test(should_fail_with = "contract address cannot be 0")] + fn private_function_zero_storage_contract_address_fails() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + // Set storage_contract_address to 0 + private_inputs.private_call.call_stack_item.inner.public_inputs.call_context.storage_contract_address = Address::ZERO(); + + // We need to update the previous_kernel's private_call_stack because the current call_stack_item has changed. + private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = private_inputs.private_call.call_stack_item.hash(); + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with = "call is internal but msg_sender is not self")] + fn private_function_incorrect_is_internal() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + // Make the call internal but msg_sender != storage_contract_address. + private_inputs.private_call.call_stack_item.inner.function_data.is_internal = true; + private_inputs.private_call.call_stack_item.inner.public_inputs.call_context.msg_sender = Address::from_field(1); + private_inputs.private_call.call_stack_item.inner.public_inputs.call_context.storage_contract_address = Address::from_field(2); + + // We need to update the previous_kernel's private_call_stack because the current call_stack_item has changed. + private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = private_inputs.private_call.call_stack_item.hash(); + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with = "purported_contract_tree_root does not match previous_kernel_contract_tree_root")] + fn private_function_incorrect_contract_tree_root_fails() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + // Set historic_tree_root to a wrong value (the correct value + 1). + let contract_tree_root = private_inputs.previous_kernel.public_inputs.constants.block_data.block.contract_tree_root; + private_inputs.previous_kernel.public_inputs.constants.block_data.block.contract_tree_root = contract_tree_root + 1; + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with = "computed_contract_tree_root does not match purported_contract_tree_root")] + fn private_function_incorrect_contract_leaf_index_fails() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + // Set the leaf index of the contract leaf to a wrong value (the correct value + 1). + let leaf_index = private_inputs.private_call.contract_leaf_membership_witness.leaf_index; + private_inputs.private_call.contract_leaf_membership_witness.leaf_index = leaf_index + 1; + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with = "computed_contract_tree_root does not match purported_contract_tree_root")] + fn private_function_incorrect_contract_leaf_sibling_path_fails() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + // Set the first value of the sibling path to a wrong value (the correct value + 1). + let sibling_path_0 = private_inputs.private_call.contract_leaf_membership_witness.sibling_path[0]; + private_inputs.private_call.contract_leaf_membership_witness.sibling_path[0] = sibling_path_0 + 1; + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with = "computed_contract_tree_root does not match purported_contract_tree_root")] + fn private_function_incorrect_function_leaf_index_fails() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + // Set the leaf index of the function leaf to a wrong value (the correct value + 1). + let leaf_index = private_inputs.private_call.function_leaf_membership_witness.leaf_index; + private_inputs.private_call.function_leaf_membership_witness.leaf_index = leaf_index + 1; + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with = "computed_contract_tree_root does not match purported_contract_tree_root")] + fn private_function_incorrect_function_leaf_sibling_path_fails() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + // Set the first value of the sibling path to a wrong value (the correct value + 1). + let sibling_path_0 = private_inputs.private_call.function_leaf_membership_witness.sibling_path[0]; + private_inputs.private_call.function_leaf_membership_witness.sibling_path[0] = sibling_path_0 + 1; + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with = "calculated private_call_hash does not match provided private_call_hash at the top of the callstack")] + fn private_function_incorrect_call_stack_item_hash_fails() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + // Set the first call stack hash to a wrong value (the correct value + 1). + let hash_0 = private_inputs.private_call.call_stack_item.inner.public_inputs.private_call_stack[0]; + private_inputs.private_call.call_stack_item.inner.public_inputs.private_call_stack[0] = hash_0 + 1; + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with = "invalid array")] + fn input_validation_malformed_arrays_return_values() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let malformed_return_values = [0,0,0,553]; + private_inputs.private_call.call_stack_item.inner.public_inputs.return_values = malformed_return_values; + private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = private_inputs.private_call.call_stack_item.hash(); + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with = "invalid array")] + fn input_validation_malformed_arrays_read_requests() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let mut malformed_read_requests = [0; MAX_READ_REQUESTS_PER_CALL]; + malformed_read_requests[1] = 9123; + malformed_read_requests[3] = 12; + private_inputs.private_call.call_stack_item.inner.public_inputs.read_requests = malformed_read_requests; + private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = private_inputs.private_call.call_stack_item.hash(); + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with = "invalid array")] + fn input_validation_malformed_arrays_commitments() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let mut malformed_commitments = [0; MAX_NEW_COMMITMENTS_PER_CALL]; + malformed_commitments[1] = 9123; + private_inputs.private_call.call_stack_item.inner.public_inputs.new_commitments = malformed_commitments; + private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = private_inputs.private_call.call_stack_item.hash(); + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with = "invalid array")] + fn input_validation_malformed_arrays_nullifiers() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let mut malformed_nullifiers = [0; MAX_NEW_NULLIFIERS_PER_CALL]; + malformed_nullifiers[MAX_NEW_NULLIFIERS_PER_CALL-1] = 12; + private_inputs.private_call.call_stack_item.inner.public_inputs.new_nullifiers = malformed_nullifiers; + private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = private_inputs.private_call.call_stack_item.hash(); + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with = "invalid array")] + fn input_validation_malformed_arrays_nullified_commitments() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let mut malformed_nullified_commitments = [0; MAX_NEW_NULLIFIERS_PER_CALL]; + malformed_nullified_commitments[2] = EMPTY_NULLIFIED_COMMITMENT; + private_inputs.private_call.call_stack_item.inner.public_inputs.nullified_commitments = malformed_nullified_commitments; + private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = private_inputs.private_call.call_stack_item.hash(); + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with = "invalid array")] + fn input_validation_malformed_arrays_private_call_stack() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let mut malformed_private_call_stack = [0; MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL]; + malformed_private_call_stack[1] = 888; + private_inputs.private_call.call_stack_item.inner.public_inputs.private_call_stack = malformed_private_call_stack; + private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = private_inputs.private_call.call_stack_item.hash(); + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with = "invalid array")] + fn input_validation_malformed_arrays_public_call_stack() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let mut malformed_public_call_stack = [0; MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL]; + malformed_public_call_stack[1] = 888; + private_inputs.private_call.call_stack_item.inner.public_inputs.public_call_stack = malformed_public_call_stack; + private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = private_inputs.private_call.call_stack_item.hash(); + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with = "invalid array")] + fn input_validation_malformed_arrays_new_l2_to_l1_msgs() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let mut malformed_new_l2_to_l1_msgs = [0; MAX_NEW_L2_TO_L1_MSGS_PER_CALL]; + malformed_new_l2_to_l1_msgs[1] = 888; + private_inputs.private_call.call_stack_item.inner.public_inputs.new_l2_to_l1_msgs = malformed_new_l2_to_l1_msgs; + private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = private_inputs.private_call.call_stack_item.hash(); + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with = "invalid array")] + fn input_validation_malformed_end_arrays_read_requests() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let mut malformed_read_requests = [0; MAX_READ_REQUESTS_PER_TX]; + malformed_read_requests[1] = 9123; + malformed_read_requests[3] = 12; + private_inputs.previous_kernel.public_inputs.end.read_requests = malformed_read_requests; + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with = "invalid array")] + fn input_validation_malformed_end_arrays_commitments() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let mut malformed_commitments = [0; MAX_NEW_COMMITMENTS_PER_TX]; + malformed_commitments[1] = 9123; + private_inputs.previous_kernel.public_inputs.end.new_commitments = malformed_commitments; + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with = "invalid array")] + fn input_validation_malformed_end_arrays_nullifiers() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let mut malformed_nullifiers = [0; MAX_NEW_NULLIFIERS_PER_TX]; + malformed_nullifiers[MAX_NEW_NULLIFIERS_PER_TX - 1] = 12; + private_inputs.previous_kernel.public_inputs.end.new_nullifiers = malformed_nullifiers; + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with = "invalid array")] + fn input_validation_malformed_end_arrays_nullified_commitments() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let mut malformed_nullified_commitments = [0; MAX_NEW_NULLIFIERS_PER_TX]; + malformed_nullified_commitments[2] = EMPTY_NULLIFIED_COMMITMENT; + private_inputs.previous_kernel.public_inputs.end.nullified_commitments = malformed_nullified_commitments; + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with = "invalid array")] + fn input_validation_malformed_end_arrays_private_call_stack() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let mut malformed_private_call_stack = [0; MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX]; + malformed_private_call_stack[1] = 888; + private_inputs.previous_kernel.public_inputs.end.private_call_stack = malformed_private_call_stack; + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with = "invalid array")] + fn input_validation_malformed_end_arrays_public_call_stack() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let mut malformed_public_call_stack = [0; MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX]; + malformed_public_call_stack[1] = 888; + private_inputs.previous_kernel.public_inputs.end.public_call_stack = malformed_public_call_stack; + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with = "invalid array")] + fn input_validation_malformed_end_arrays_l2_to_l1_msgs() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let mut malformed_l2_to_l1_msgs = [0; MAX_NEW_L2_TO_L1_MSGS_PER_TX]; + malformed_l2_to_l1_msgs[MAX_NEW_L2_TO_L1_MSGS_PER_TX - 1] = 1; + private_inputs.previous_kernel.public_inputs.end.new_l2_to_l1_msgs = malformed_l2_to_l1_msgs; + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with = "push_vec out of bounds")] + fn private_kernel_should_fail_if_aggregating_too_many_commitments() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + // The current call stack has 1 commitment; + private_inputs.private_call.call_stack_item.inner.public_inputs.new_commitments[0] = 1; + + // We need to update the previous_kernel's private_call_stack because the current call_stack_item has changed. + private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = private_inputs.private_call.call_stack_item.hash(); + + // Mock the previous new commitments to be full, therefore no more commitments can be added. + let mut full_new_commitments = [0; MAX_NEW_COMMITMENTS_PER_TX]; + for i in 0..MAX_NEW_COMMITMENTS_PER_TX { + full_new_commitments[i] = i + 1; + } + private_inputs.previous_kernel.public_inputs.end.new_commitments = full_new_commitments; + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with="private data tree root mismatch")] + fn native_read_request_bad_request() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(deposit_app, params); + + let (mut read_requests, mut read_request_membership_witnesses) = generate_read_requests(2); + + // tweak read_request so it gives wrong root when paired with its sibling path + read_requests[1] += 1; + + private_inputs.private_call.call_stack_item.inner.public_inputs.read_requests = read_requests; + private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; + + // We need to update the previous_kernel's private_call_stack because the current call_stack_item has changed. + private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = private_inputs.private_call.call_stack_item.hash(); + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with="private data tree root mismatch")] + fn native_read_request_bad_leaf_index() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(deposit_app, params); + + let (mut read_requests, mut read_request_membership_witnesses) = generate_read_requests(2); + + // tweak leaf index so it gives wrong root when paired with its request and sibling path + read_request_membership_witnesses[1].leaf_index += 1; + private_inputs.private_call.call_stack_item.inner.public_inputs.read_requests = read_requests; + private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; + + // We need to update the previous_kernel's private_call_stack because the current call_stack_item has changed. + private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = private_inputs.private_call.call_stack_item.hash(); + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with="private data tree root mismatch")] + fn native_read_request_bad_sibling_path() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(deposit_app, params); + + let (mut read_requests, mut read_request_membership_witnesses) = generate_read_requests(2); + + // tweak sibling path so it gives wrong root when paired with its request + read_request_membership_witnesses[1].sibling_path[1] += 1; + private_inputs.private_call.call_stack_item.inner.public_inputs.read_requests = read_requests; + private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; + + // We need to update the previous_kernel's private_call_stack because the current call_stack_item has changed. + private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = private_inputs.private_call.call_stack_item.hash(); + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test(should_fail_with="private data tree root mismatch")] + fn native_read_request_root_mismatch() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(deposit_app, params); + + let (mut read_requests, mut read_request_membership_witnesses) = generate_read_requests(1); + + private_inputs.private_call.call_stack_item.inner.public_inputs.read_requests = read_requests; + private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; + private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = private_inputs.private_call.call_stack_item.hash(); + + // Set the root to be a different root so the above read request is not under this root. + let old_root = private_inputs.previous_kernel.public_inputs.constants.block_data.block.note_hash_tree_root; + private_inputs.previous_kernel.public_inputs.constants.block_data.block.note_hash_tree_root = old_root + 1; + + private_inputs.native_private_kernel_circuit_inner(); + } + + #[test] + fn native_no_read_requests_works() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(deposit_app, params); + + let (mut read_requests, mut read_request_membership_witnesses) = generate_read_requests(0); + + private_inputs.private_call.call_stack_item.inner.public_inputs.read_requests = read_requests; + private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; + + // We need to update the previous_kernel's private_call_stack because the current call_stack_item has changed. + private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = private_inputs.private_call.call_stack_item.hash(); + + let public_inputs = private_inputs.native_private_kernel_circuit_inner(); + + // non-transient read requests are NOT forwarded + assert_eq(non_zero_items(public_inputs.end.read_requests), 0); + } + + #[test] + fn native_one_read_requests_works() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(deposit_app, params); + + let (mut read_requests, mut read_request_membership_witnesses) = generate_read_requests(1); + + private_inputs.private_call.call_stack_item.inner.public_inputs.read_requests = read_requests; + private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; + private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = private_inputs.private_call.call_stack_item.hash(); + + let public_inputs = private_inputs.native_private_kernel_circuit_inner(); + + // non-transient read requests are NOT forwarded + assert_eq(non_zero_items(public_inputs.end.read_requests), 0); + } + + #[test] + fn native_two_read_requests_works() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(deposit_app, params); + + let (mut read_requests, mut read_request_membership_witnesses) = generate_read_requests(2); + + private_inputs.private_call.call_stack_item.inner.public_inputs.read_requests = read_requests; + private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; + private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = private_inputs.private_call.call_stack_item.hash(); + + let public_inputs = private_inputs.native_private_kernel_circuit_inner(); + + // non-transient read requests are NOT forwarded + assert_eq(non_zero_items(public_inputs.end.read_requests), 0); + } + + #[test] + fn native_max_read_requests_works() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(deposit_app, params); + + let (mut read_requests, mut read_request_membership_witnesses) = generate_read_requests(MAX_READ_REQUESTS_PER_CALL as u64); + + private_inputs.private_call.call_stack_item.inner.public_inputs.read_requests = read_requests; + private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; + private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = private_inputs.private_call.call_stack_item.hash(); + + let public_inputs = private_inputs.native_private_kernel_circuit_inner(); + + // non-transient read requests are NOT forwarded + assert_eq(non_zero_items(public_inputs.end.read_requests), 0); + } + + #[test] + fn native_one_transient_read_requests_works() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(deposit_app, params); + + let (mut read_requests, mut read_request_membership_witnesses) = generate_read_requests(1); + + private_inputs.private_call.call_stack_item.inner.public_inputs.read_requests = read_requests; + private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses.map(|mut witness: ReadRequestMembershipWitness| { + witness.is_transient = true; + witness + }); + private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = private_inputs.private_call.call_stack_item.hash(); + + let public_inputs = private_inputs.native_private_kernel_circuit_inner(); + + // non-transient read requests are NOT forwarded + assert_eq(non_zero_items(public_inputs.end.read_requests), 1); + } + + #[test] + fn native_max_read_requests_one_transient_works() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(deposit_app, params); + + let (mut read_requests, mut read_request_membership_witnesses) = generate_read_requests(MAX_READ_REQUESTS_PER_CALL as u64); + + read_request_membership_witnesses[1].is_transient = true; + private_inputs.private_call.call_stack_item.inner.public_inputs.read_requests = read_requests; + private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; + private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = private_inputs.private_call.call_stack_item.hash(); + + let public_inputs = private_inputs.native_private_kernel_circuit_inner(); + + // non-transient read requests are NOT forwarded + assert_eq(non_zero_items(public_inputs.end.read_requests), 1); + } + + #[test] + fn native_max_read_requests_all_transient_works() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(deposit_app, params); + + let (mut read_requests, mut read_request_membership_witnesses) = generate_read_requests(MAX_READ_REQUESTS_PER_CALL as u64); + + private_inputs.private_call.call_stack_item.inner.public_inputs.read_requests = read_requests; + private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses.map(|mut witness: ReadRequestMembershipWitness| { + witness.is_transient = true; + witness + }); + private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = private_inputs.private_call.call_stack_item.hash(); + + let public_inputs = private_inputs.native_private_kernel_circuit_inner(); + + // non-transient read requests are NOT forwarded + assert_eq(non_zero_items(public_inputs.end.read_requests), MAX_READ_REQUESTS_PER_CALL as u64); + } + + #[test] + fn native_logs_are_hashed_as_expected() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(deposit_app, params); + + // Logs for the current call stack. + let encrypted_logs_hash = [16, 69]; + let encrypted_log_preimages_length = 100; + let unencrypted_logs_hash = [26, 47]; + let unencrypted_log_preimages_length = 50; + private_inputs.private_call.call_stack_item.inner.public_inputs.encrypted_logs_hash = encrypted_logs_hash; + private_inputs.private_call.call_stack_item.inner.public_inputs.encrypted_log_preimages_length = encrypted_log_preimages_length; + private_inputs.private_call.call_stack_item.inner.public_inputs.unencrypted_logs_hash = unencrypted_logs_hash; + private_inputs.private_call.call_stack_item.inner.public_inputs.unencrypted_log_preimages_length = unencrypted_log_preimages_length; + + private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = private_inputs.private_call.call_stack_item.hash(); + + // Logs for the previous call stack. + let prev_encrypted_logs_hash = [80, 429]; + let prev_encrypted_log_preimages_length = 13; + let prev_unencrypted_logs_hash = [956, 112]; + let prev_unencrypted_log_preimages_length = 24; + private_inputs.previous_kernel.public_inputs.end.encrypted_logs_hash = prev_encrypted_logs_hash; + private_inputs.previous_kernel.public_inputs.end.encrypted_log_preimages_length = prev_encrypted_log_preimages_length; + private_inputs.previous_kernel.public_inputs.end.unencrypted_logs_hash = prev_unencrypted_logs_hash; + private_inputs.previous_kernel.public_inputs.end.unencrypted_log_preimages_length = prev_unencrypted_log_preimages_length; + + let public_inputs = private_inputs.native_private_kernel_circuit_inner(); + + assert_eq(public_inputs.end.encrypted_log_preimages_length, encrypted_log_preimages_length + prev_encrypted_log_preimages_length); + assert_eq(public_inputs.end.unencrypted_log_preimages_length, unencrypted_log_preimages_length + prev_unencrypted_log_preimages_length); + + let expected_encrypted_logs_hash = compute_logs_hash(prev_encrypted_logs_hash, encrypted_logs_hash); + assert_eq(public_inputs.end.encrypted_logs_hash, expected_encrypted_logs_hash); + + let expected_unencrypted_logs_hash = compute_logs_hash(prev_unencrypted_logs_hash, unencrypted_logs_hash); + assert_eq(public_inputs.end.unencrypted_logs_hash, expected_unencrypted_logs_hash); + } + + #[test(should_fail_with="The 0th nullifier in the accumulated nullifier array is zero")] + fn zero_0th_nullifier_fails() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(deposit_app, params); + + private_inputs.previous_kernel.public_inputs.end.new_nullifiers[0] = 0; + + private_inputs.native_private_kernel_circuit_inner(); + } +} diff --git a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_ordering.nr b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_ordering.nr index f2c216f7dfd..681ad16bcbc 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_ordering.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_ordering.nr @@ -69,6 +69,7 @@ impl PrivateKernelInputsOrdering { let mut new_nullifiers = public_inputs.end.new_nullifiers.storage; for n_idx in 0..MAX_NEW_NULLIFIERS_PER_TX { + // TODO - should not be able to squash the first nullifier. let nullified_commitment = nullified_commitments[n_idx]; let nullifier_commitment_hint = nullifier_commitment_hints[n_idx]; let hint_pos = nullifier_commitment_hint as u64; @@ -153,3 +154,352 @@ impl PrivateKernelInputsOrdering { public_inputs.to_final() } } + +mod tests { + use crate::abis::{ + private_circuit_public_inputs::PrivateCircuitPublicInputs, + read_request_membership_witness::ReadRequestMembershipWitness, + }; + use crate::address::Address; + use crate::hash::{ + compute_commitment_nonce, + compute_unique_commitment, + }; + use crate::private_kernel_ordering::PrivateKernelInputsOrdering; + use crate::tests::{ + apps::constructor::constructor_app, + testing_harness::{ + create_previous_kernel_data, + non_zero_items, + PrivateAppInputs, + }, + }; + use dep::aztec::constants_gen::{ + MAX_READ_REQUESTS_PER_TX, + MAX_NEW_COMMITMENTS_PER_TX, + MAX_NEW_NULLIFIERS_PER_TX, + EMPTY_NULLIFIED_COMMITMENT, + MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, + MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, + MAX_NEW_L2_TO_L1_MSGS_PER_TX, + }; + use crate::utils::{ + bounded_vec::BoundedVec, + }; + + fn build_inputs( + function: fn (PrivateAppInputs, T) -> PrivateCircuitPublicInputs, + app_params: T, + ) -> PrivateKernelInputsOrdering { + let msg_sender = Address::from_field(27); + let previous_kernel = create_previous_kernel_data( + false, + function, + app_params, + msg_sender, + ); + + let read_commitment_hints = [0; MAX_READ_REQUESTS_PER_TX]; + let nullifier_commitment_hints = [0; MAX_NEW_NULLIFIERS_PER_TX]; + + PrivateKernelInputsOrdering { + previous_kernel, + read_commitment_hints, + nullifier_commitment_hints, + } + } + + fn generate_unique_siloed_commitments( + private_inputs: PrivateKernelInputsOrdering, + siloed_commitments: [Field; MAX_NEW_COMMITMENTS_PER_TX], + squashed_indices: [Field; N], + ) -> [Field; MAX_NEW_COMMITMENTS_PER_TX] { + let mut valid_indices = [true; MAX_NEW_COMMITMENTS_PER_TX]; + for i in 0..N { + valid_indices[squashed_indices[i]] = false; + } + + let mut unique_siloed_commitments = BoundedVec::new(0); + let first_nullifier = private_inputs.previous_kernel.public_inputs.end.new_nullifiers[0]; + for i in 0..MAX_NEW_COMMITMENTS_PER_TX { + let siloed_commitment = siloed_commitments[i]; + if (siloed_commitment != 0) & valid_indices[i] { + let idx = unique_siloed_commitments.len(); + let nonce = compute_commitment_nonce(first_nullifier, idx); + let unique_siloed_commitment = compute_unique_commitment(nonce, siloed_commitment); + unique_siloed_commitments.push(unique_siloed_commitment); + } + } + + unique_siloed_commitments.storage + } + + fn mock_new_commitments(private_inputs: &mut PrivateKernelInputsOrdering, num_new_commitments: Field) -> ( + [Field; MAX_NEW_COMMITMENTS_PER_TX], + [Field; MAX_NEW_COMMITMENTS_PER_TX], + ) { + let mut new_commitments = [0; MAX_NEW_COMMITMENTS_PER_TX]; + for i in 0..MAX_NEW_COMMITMENTS_PER_TX { + if i as u64 < num_new_commitments as u64 { + let siloed_commitment = i + 623; + new_commitments[i] = siloed_commitment; + } + } + + private_inputs.previous_kernel.public_inputs.end.new_commitments = new_commitments; + + let unique_siloed_commitments = generate_unique_siloed_commitments(*private_inputs, new_commitments, []); + + (new_commitments, unique_siloed_commitments) + } + + fn mock_new_nullifiers(private_inputs: &mut PrivateKernelInputsOrdering, num_extra_nullifier: Field) -> [Field; MAX_NEW_NULLIFIERS_PER_TX] { + let mut new_nullifiers = [0; MAX_NEW_NULLIFIERS_PER_TX]; + let first_nullifier = private_inputs.previous_kernel.public_inputs.end.new_nullifiers[0]; + new_nullifiers[0] = first_nullifier; + + for i in 1..MAX_NEW_NULLIFIERS_PER_TX { + if i as u64 <= num_extra_nullifier as u64 { + // Set a random value that's different to the first nullifier. + new_nullifiers[i] = first_nullifier + i; + } + } + + private_inputs.previous_kernel.public_inputs.end.new_nullifiers = new_nullifiers; + + new_nullifiers + } + + #[test] + fn native_matching_one_read_request_to_commitment_works() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let (new_commitments, unique_siloed_commitments) = mock_new_commitments(&mut private_inputs, 1); + private_inputs.read_commitment_hints[0] = 0; + private_inputs.previous_kernel.public_inputs.end.read_requests[0] = new_commitments[0]; + + let public_inputs = private_inputs.native_private_kernel_circuit_ordering(); + assert(non_zero_items(public_inputs.end.new_commitments) == 1); + assert(public_inputs.end.new_commitments[0] == unique_siloed_commitments[0]); + } + + #[test] + fn native_matching_some_read_requests_to_commitments_works() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let (new_commitments, unique_siloed_commitments) = mock_new_commitments(&mut private_inputs, MAX_NEW_COMMITMENTS_PER_TX); + // Read the commitment at index 1; + private_inputs.read_commitment_hints[0] = 1; + private_inputs.previous_kernel.public_inputs.end.read_requests[0] = new_commitments[1]; + // Read the commitment at index 3; + private_inputs.read_commitment_hints[1] = 3; + private_inputs.previous_kernel.public_inputs.end.read_requests[1] = new_commitments[3]; + + let public_inputs = private_inputs.native_private_kernel_circuit_ordering(); + assert(non_zero_items(public_inputs.end.new_commitments) == MAX_NEW_COMMITMENTS_PER_TX as u64); + for i in 0..MAX_NEW_COMMITMENTS_PER_TX { + assert(public_inputs.end.new_commitments[i] == unique_siloed_commitments[i]); + } + } + + #[test(should_fail_with="read request is transient but does not match any commitment")] + fn native_read_request_unknown_fails() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let (new_commitments, _) = mock_new_commitments(&mut private_inputs, 1); + private_inputs.read_commitment_hints[0] = 0; + // The read request does not match the commitment at index 0; + private_inputs.previous_kernel.public_inputs.end.read_requests[0] = new_commitments[0] + 1; + + private_inputs.native_private_kernel_circuit_ordering(); + } + + #[test] + fn native_squash_one_of_one_transient_matches_works() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let (new_commitments, _) = mock_new_commitments(&mut private_inputs, 1); + let new_nullifiers = mock_new_nullifiers(&mut private_inputs, 2); + // The nullifier at index 1 is nullifying the commitment at index 0; + let transient_nullifier_index = 1; + let nullified_commitment_index = 0; + private_inputs.previous_kernel.public_inputs.end.nullified_commitments[transient_nullifier_index] = new_commitments[nullified_commitment_index]; + private_inputs.nullifier_commitment_hints[transient_nullifier_index] = nullified_commitment_index; + + let public_inputs = private_inputs.native_private_kernel_circuit_ordering(); + assert(non_zero_items(public_inputs.end.new_commitments) == 0); + assert(non_zero_items(public_inputs.end.new_nullifiers) == 2); + assert(public_inputs.end.new_nullifiers[0] == new_nullifiers[0]); + assert(public_inputs.end.new_nullifiers[1] == new_nullifiers[2]); + } + + #[test] + fn native_squash_one_of_two_transient_matches_works() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let (new_commitments, _) = mock_new_commitments(&mut private_inputs, 2); + let new_nullifiers = mock_new_nullifiers(&mut private_inputs, 2); + // The nullifier at index 1 is nullifying the commitment at index 0; + let transient_nullifier_index = 1; + let nullified_commitment_index = 0; + private_inputs.previous_kernel.public_inputs.end.nullified_commitments[transient_nullifier_index] = new_commitments[nullified_commitment_index]; + private_inputs.nullifier_commitment_hints[transient_nullifier_index] = nullified_commitment_index; + + // The 0th commitment is chopped. + let unique_siloed_commitments = generate_unique_siloed_commitments(private_inputs, new_commitments, [0]); + + let public_inputs = private_inputs.native_private_kernel_circuit_ordering(); + assert(non_zero_items(public_inputs.end.new_commitments) == 1); + assert(public_inputs.end.new_commitments[0] == unique_siloed_commitments[0]); + assert(non_zero_items(public_inputs.end.new_nullifiers) == 2); + assert(public_inputs.end.new_nullifiers[0] == new_nullifiers[0]); + assert(public_inputs.end.new_nullifiers[1] == new_nullifiers[2]); + } + + #[test] + fn native_squash_two_of_two_transient_matches_works() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let (new_commitments, _) = mock_new_commitments(&mut private_inputs, 2); + let new_nullifiers = mock_new_nullifiers(&mut private_inputs, 2); + // The nullifier at index 1 is nullifying the commitment at index 1; + private_inputs.previous_kernel.public_inputs.end.nullified_commitments[1] = new_commitments[1]; + private_inputs.nullifier_commitment_hints[1] = 1; + // The nullifier at index 2 is nullifying the commitment at index 0; + private_inputs.previous_kernel.public_inputs.end.nullified_commitments[2] = new_commitments[0]; + private_inputs.nullifier_commitment_hints[2] = 0; + + let public_inputs = private_inputs.native_private_kernel_circuit_ordering(); + assert(non_zero_items(public_inputs.end.new_commitments) == 0); + assert(non_zero_items(public_inputs.end.new_nullifiers) == 1); + assert(public_inputs.end.new_nullifiers[0] == new_nullifiers[0]); + } + + #[test] + fn native_empty_nullified_commitment_means_persistent_nullifier_0() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + mock_new_commitments(&mut private_inputs, 2); + mock_new_nullifiers(&mut private_inputs, 2); + + let public_inputs = private_inputs.native_private_kernel_circuit_ordering(); + assert(non_zero_items(public_inputs.end.new_commitments) == 2); + assert(non_zero_items(public_inputs.end.new_nullifiers) == 3); + } + + // same as previous test, but this time there are 0 commitments! + // (Do we really need this test?) + #[test] + fn native_empty_nullified_commitment_means_persistent_nullifier_1() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + mock_new_nullifiers(&mut private_inputs, 2); + + let public_inputs = private_inputs.native_private_kernel_circuit_ordering(); + assert(non_zero_items(public_inputs.end.new_commitments) == 0); + assert(non_zero_items(public_inputs.end.new_nullifiers) == 3); + } + + #[test(should_fail_with="The 0th nullifier in the accumulated nullifier array is zero")] + fn zero_0th_nullifier_fails() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + private_inputs.previous_kernel.public_inputs.end.new_nullifiers[0] = 0; + + private_inputs.native_private_kernel_circuit_ordering(); + } + + #[test(should_fail_with="invalid array")] + fn input_validation_malformed_end_arrays_read_requests() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let mut malformed_read_requests = [0; MAX_READ_REQUESTS_PER_TX]; + malformed_read_requests[1] = 9123; + malformed_read_requests[3] = 12; + private_inputs.previous_kernel.public_inputs.end.read_requests = malformed_read_requests; + + private_inputs.native_private_kernel_circuit_ordering(); + } + + #[test(should_fail_with="invalid array")] + fn input_validation_malformed_end_arrays_commitments() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let mut malformed_commitments = [0; MAX_NEW_COMMITMENTS_PER_TX]; + malformed_commitments[1] = 9123; + private_inputs.previous_kernel.public_inputs.end.new_commitments = malformed_commitments; + + private_inputs.native_private_kernel_circuit_ordering(); + } + + #[test(should_fail_with="invalid array")] + fn input_validation_malformed_end_arrays_nullifiers() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let mut malformed_nullifiers = [0; MAX_NEW_NULLIFIERS_PER_TX]; + malformed_nullifiers[MAX_NEW_NULLIFIERS_PER_TX - 1] = 12; + private_inputs.previous_kernel.public_inputs.end.new_nullifiers = malformed_nullifiers; + + private_inputs.native_private_kernel_circuit_ordering(); + } + + #[test(should_fail_with="invalid array")] + fn input_validation_malformed_end_arrays_nullified_commitments() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let mut malformed_nullified_commitments = [0; MAX_NEW_NULLIFIERS_PER_TX]; + malformed_nullified_commitments[2] = EMPTY_NULLIFIED_COMMITMENT; + private_inputs.previous_kernel.public_inputs.end.nullified_commitments = malformed_nullified_commitments; + + private_inputs.native_private_kernel_circuit_ordering(); + } + + #[test(should_fail_with="invalid array")] + fn input_validation_malformed_end_arrays_private_call_stack() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let mut malformed_private_call_stack = [0; MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX]; + malformed_private_call_stack[1] = 888; + private_inputs.previous_kernel.public_inputs.end.private_call_stack = malformed_private_call_stack; + + private_inputs.native_private_kernel_circuit_ordering(); + } + + #[test(should_fail_with="invalid array")] + fn input_validation_malformed_end_arrays_public_call_stack() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let mut malformed_public_call_stack = [0; MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX]; + malformed_public_call_stack[1] = 888; + private_inputs.previous_kernel.public_inputs.end.public_call_stack = malformed_public_call_stack; + + private_inputs.native_private_kernel_circuit_ordering(); + } + + #[test(should_fail_with="invalid array")] + fn input_validation_malformed_end_arrays_l2_to_l1_msgs() { + let params = dep::std::unsafe::zeroed(); + let mut private_inputs = build_inputs(constructor_app, params); + + let mut malformed_l2_to_l1_msgs = [0; MAX_NEW_L2_TO_L1_MSGS_PER_TX]; + malformed_l2_to_l1_msgs[MAX_NEW_L2_TO_L1_MSGS_PER_TX - 1] = 1; + private_inputs.previous_kernel.public_inputs.end.new_l2_to_l1_msgs = malformed_l2_to_l1_msgs; + + private_inputs.native_private_kernel_circuit_ordering(); + } +} diff --git a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/tests/testing_harness.nr b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/tests/testing_harness.nr index 2f6dacbe0e4..f94410f170a 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/tests/testing_harness.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/tests/testing_harness.nr @@ -8,6 +8,8 @@ use crate::{ deployment_data::ContractDeploymentData, }, abis::{ + combined_constant_data::CombinedConstantData, + previous_kernel_data::PreviousKernelData, private_kernel::private_call_data::PrivateCallData, historical_block_data::HistoricalBlockData, call_context::CallContext, @@ -35,6 +37,7 @@ use crate::{ use dep::aztec::{ abi::hash_args, constants_gen::{ + EMPTY_NULLIFIED_COMMITMENT, MAX_READ_REQUESTS_PER_CALL, MAX_NEW_COMMITMENTS_PER_CALL, MAX_NEW_NULLIFIERS_PER_CALL, @@ -154,6 +157,20 @@ impl PrivateCircuitPublicInputsBuilder { } } +pub fn build_tx_context( + is_constructor: bool, + contract_deployment_data: ContractDeploymentData, +) -> TxContext { + TxContext { + is_fee_payment_tx : false, + is_rebate_payment_tx : false, + is_contract_deployment_tx : is_constructor, + contract_deployment_data, + chain_id : 1, + version: 0, + } +} + pub fn build_tx_request( is_constructor: bool, contract_deployment_data: ContractDeploymentData, @@ -317,6 +334,42 @@ pub fn create_private_call_data( (private_call, contract_deployment_data) } +pub fn create_previous_kernel_data( + is_constructor: bool, + function: fn (PrivateAppInputs, T) -> PrivateCircuitPublicInputs, + params: T, + msg_sender: Address, +) -> PreviousKernelData { + let (private_call, contract_deployment_data) = create_private_call_data( + is_constructor, + function, + params, + msg_sender, + ); + + // Turn the private call data into the previous kernel. + let mut previous_kernel: PreviousKernelData = dep::std::unsafe::zeroed(); + + let tx_context = build_tx_context(is_constructor, contract_deployment_data); + previous_kernel.public_inputs.constants = CombinedConstantData { + block_data: private_call.call_stack_item.public_inputs().historical_block_data, + tx_context, + }; + + previous_kernel.public_inputs.is_private = true; + previous_kernel.public_inputs.end.encrypted_logs_hash = [0, 0]; + previous_kernel.public_inputs.end.unencrypted_logs_hash = [0, 0]; + previous_kernel.public_inputs.end.encrypted_log_preimages_length = 0; + previous_kernel.public_inputs.end.unencrypted_log_preimages_length = 0; + + previous_kernel.public_inputs.end.new_nullifiers[0] = 321; // 0th nullifier must be non-zero. + previous_kernel.public_inputs.end.nullified_commitments[0] = EMPTY_NULLIFIED_COMMITMENT; + + previous_kernel.public_inputs.end.private_call_stack[0] = private_call.call_stack_item.hash(); + + previous_kernel +} + pub fn generate_read_requests(how_many: u64) -> ([Field; MAX_READ_REQUESTS_PER_CALL], [ReadRequestMembershipWitness; MAX_READ_REQUESTS_PER_CALL]) { let mut read_requests = [0; MAX_READ_REQUESTS_PER_CALL]; let mut read_request_membership_witnesses: [ReadRequestMembershipWitness; MAX_READ_REQUESTS_PER_CALL] = dep::std::unsafe::zeroed(); diff --git a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/utils/bounded_vec.nr b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/utils/bounded_vec.nr index d1282eaf9f8..fc5fcdf7b2a 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/utils/bounded_vec.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/utils/bounded_vec.nr @@ -22,7 +22,7 @@ impl BoundedVec { } pub fn push(&mut self, elem: T) { - assert(self.len as u64 < MaxLen as u64); + assert(self.len as u64 < MaxLen as u64, "push out of bounds"); self.storage[self.len] = elem; self.len += 1; @@ -44,7 +44,7 @@ impl BoundedVec { pub fn push_array(&mut self, array: [T; Len]) { let newLen = self.len + array.len(); - assert(newLen as u64 <= MaxLen as u64); + assert(newLen as u64 <= MaxLen as u64, "push_array out of bounds"); for i in 0..array.len() { self.storage[self.len + i] = array[i]; } @@ -53,7 +53,7 @@ impl BoundedVec { pub fn push_vec(&mut self, vec: BoundedVec) { let newLen = self.len + vec.len(); - assert(newLen as u64 <= MaxLen as u64); + assert(newLen as u64 <= MaxLen as u64, "push_vec out of bounds"); for i in 0..Len { if i < (vec.len() as u64) { self.storage[self.len + (i as Field)] = vec.get_unchecked(i as Field); @@ -110,6 +110,20 @@ fn test_vec_push_array() { assert(vec.get(1) == 4); } +#[test(should_fail_with="push_array out of bounds")] +fn test_vec_push_array_out_of_bound() { + let mut vec: BoundedVec = BoundedVec::new(0); + vec.push_array([2, 4, 6]); +} + +#[test(should_fail_with="push_array out of bounds")] +fn test_vec_push_array_twice_out_of_bound() { + let mut vec: BoundedVec = BoundedVec::new(0); + vec.push_array([2]); + assert(vec.len == 1); + vec.push_array([4, 6]); +} + #[test(should_fail)] fn test_vec_get_out_of_bound() { let mut vec: BoundedVec = BoundedVec::new(0); @@ -130,13 +144,34 @@ fn test_vec_get_uninitialized() { let _x = vec.get(0); } -#[test(should_fail)] -fn test_vec_push_overflow() { +#[test(should_fail_with="push out of bounds")] +fn test_vec_push_out_of_bound() { let mut vec: BoundedVec = BoundedVec::new(0); vec.push(1); vec.push(2); } +#[test(should_fail_with="push_vec out of bounds")] +fn test_vec_push_vec_out_of_bound() { + let mut vec: BoundedVec = BoundedVec::new(0); + + let mut another_vec: BoundedVec = BoundedVec::new(0); + another_vec.push_array([1, 2, 3]); + + vec.push_vec(another_vec); +} + +#[test(should_fail_with="push_vec out of bounds")] +fn test_vec_push_vec_twice_out_of_bound() { + let mut vec: BoundedVec = BoundedVec::new(0); + vec.push_array([1, 2]); + + let mut another_vec: BoundedVec = BoundedVec::new(0); + another_vec.push(3); + + vec.push_vec(another_vec); +} + #[test] fn test_vec_any() { let mut vec: BoundedVec = BoundedVec::new(0);