From 8b87099e301c89d5332d40fac06a1f0636dccbe1 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Fri, 27 Oct 2023 17:12:40 +0000 Subject: [PATCH 1/3] test: added some inner tests --- .../src/private_kernel_inner.nr | 379 +++++++++++++++++- .../src/tests/testing_harness.nr | 14 + 2 files changed, 392 insertions(+), 1 deletion(-) 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..507cfa70acc 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 @@ -27,7 +27,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"); } @@ -93,3 +92,381 @@ impl PrivateKernelInputsInner { public_inputs.finish() } } + + +mod tests { + use crate::private_kernel_inner::PrivateKernelInputsInner; + use crate::abis::{ + kernel_circuit_public_inputs::{KernelCircuitPublicInputs, KernelCircuitPublicInputsBuilder}, + private_circuit_public_inputs::PrivateCircuitPublicInputs, + read_request_membership_witness::ReadRequestMembershipWitness, + combined_constant_data::CombinedConstantData, + previous_kernel_data::PreviousKernelData, + }; + use crate::tests::{ + testing_harness::{ + create_private_call_data, + generate_read_requests, + build_tx_request, + PrivateAppInputs, + non_zero_items, + build_tx_context, + }, + apps::deposit::{ + deposit_app, + DepositParams, + }, + apps::constructor::{ + constructor_app, + ConstructorParams, + } + }; + use crate::address::Address; + use crate::hash::{accumulate_sha256}; + use crate::utils::uint128::U128; + use dep::aztec::constants_gen::{ + MAX_READ_REQUESTS_PER_CALL, + MAX_NEW_COMMITMENTS_PER_CALL, + MAX_NEW_NULLIFIERS_PER_CALL, + EMPTY_NULLIFIED_COMMITMENT, + MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, + MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, + MAX_NEW_L2_TO_L1_MSGS_PER_CALL, + NOTE_HASH_TREE_HEIGHT, + }; + + fn build_inputs( + function: fn (PrivateAppInputs, T) -> PrivateCircuitPublicInputs, + app_params: T, + ) -> PrivateKernelInputsInner { + + let msg_sender = Address::from_field(27); + let (private_call, contract_deployment_data) = create_private_call_data( + false, + function, + app_params, + msg_sender, + ); + + let tx_context = build_tx_context(false, contract_deployment_data); + + let mut previous_kernel: PreviousKernelData = dep::std::unsafe::zeroed(); + 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(); + + PrivateKernelInputsInner { + previous_kernel, + private_call, + } + } + + + // TODO + + // TODO + + + #[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(); + + let public_inputs = 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(); + } + + // TODO + + // TODO + + + #[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; + 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; + 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; + 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] + 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; + 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); + } + + + // TODO + + // TODO + + +} 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..1b43bbce4ed 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 @@ -154,6 +154,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, From e9738f3ffcb1b72cdf23358d39e41287ce89d020 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Tue, 31 Oct 2023 17:52:29 +0000 Subject: [PATCH 2/3] Migrate inner kernel cpp tests. --- .../crates/private-kernel-lib/src/address.nr | 14 +- .../crates/private-kernel-lib/src/common.nr | 12 +- .../src/private_kernel_inner.nr | 292 +++++++++++++++++- .../src/utils/bounded_vec.nr | 45 ++- 4 files changed, 342 insertions(+), 21 deletions(-) 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 507cfa70acc..2ce17a2d48b 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 @@ -34,7 +34,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) { @@ -122,16 +122,22 @@ mod tests { } }; use crate::address::Address; - use crate::hash::{accumulate_sha256}; + use crate::hash::{compute_logs_hash, NUM_FIELDS_PER_SHA256}; use crate::utils::uint128::U128; 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, }; @@ -139,7 +145,6 @@ mod tests { function: fn (PrivateAppInputs, T) -> PrivateCircuitPublicInputs, app_params: T, ) -> PrivateKernelInputsInner { - let msg_sender = Address::from_field(27); let (private_call, contract_deployment_data) = create_private_call_data( false, @@ -173,11 +178,107 @@ mod tests { } } + #[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); - // TODO + // 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; - // TODO + 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() { @@ -188,7 +289,7 @@ mod tests { 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(); - let public_inputs = private_inputs.native_private_kernel_circuit_inner(); + private_inputs.native_private_kernel_circuit_inner(); } #[test(should_fail_with = "invalid array")] @@ -283,10 +384,111 @@ mod tests { private_inputs.native_private_kernel_circuit_inner(); } - // TODO + #[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); - // TODO + 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() { @@ -300,6 +502,8 @@ mod tests { 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(); @@ -316,6 +520,8 @@ mod tests { 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(); @@ -330,10 +536,30 @@ mod tests { // 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(); } @@ -346,6 +572,8 @@ mod tests { 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(); @@ -443,7 +671,7 @@ mod tests { assert_eq(non_zero_items(public_inputs.end.read_requests), 1); } - #[test] + #[test] fn native_max_read_requests_all_transient_works() { let params = dep::std::unsafe::zeroed(); let mut private_inputs = build_inputs(deposit_app, params); @@ -463,10 +691,52 @@ mod tests { 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); - // TODO + 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); + } - // TODO + #[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/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); From f6a09d386fd60c7e020a328afe07769bbab46d86 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Tue, 31 Oct 2023 22:31:07 +0000 Subject: [PATCH 3/3] Migrate ordering kernel cpp tests. --- .../src/private_kernel_inner.nr | 49 +-- .../src/private_kernel_ordering.nr | 350 ++++++++++++++++++ .../src/tests/testing_harness.nr | 39 ++ 3 files changed, 403 insertions(+), 35 deletions(-) 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 2ce17a2d48b..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, @@ -97,33 +96,24 @@ impl PrivateKernelInputsInner { mod tests { use crate::private_kernel_inner::PrivateKernelInputsInner; use crate::abis::{ - kernel_circuit_public_inputs::{KernelCircuitPublicInputs, KernelCircuitPublicInputsBuilder}, private_circuit_public_inputs::PrivateCircuitPublicInputs, read_request_membership_witness::ReadRequestMembershipWitness, - combined_constant_data::CombinedConstantData, - previous_kernel_data::PreviousKernelData, }; use crate::tests::{ testing_harness::{ + create_previous_kernel_data, create_private_call_data, generate_read_requests, - build_tx_request, PrivateAppInputs, non_zero_items, - build_tx_context, }, - apps::deposit::{ - deposit_app, - DepositParams, + apps::{ + constructor::constructor_app, + deposit::deposit_app, }, - apps::constructor::{ - constructor_app, - ConstructorParams, - } }; use crate::address::Address; - use crate::hash::{compute_logs_hash, NUM_FIELDS_PER_SHA256}; - use crate::utils::uint128::U128; + use crate::hash::compute_logs_hash; use dep::aztec::constants_gen::{ MAX_READ_REQUESTS_PER_CALL, MAX_READ_REQUESTS_PER_TX, @@ -146,32 +136,21 @@ mod tests { app_params: T, ) -> PrivateKernelInputsInner { let msg_sender = Address::from_field(27); - let (private_call, contract_deployment_data) = create_private_call_data( + 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, ); - let tx_context = build_tx_context(false, contract_deployment_data); - - let mut previous_kernel: PreviousKernelData = dep::std::unsafe::zeroed(); - 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(); - PrivateKernelInputsInner { previous_kernel, private_call, 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 1b43bbce4ed..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, @@ -331,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();