From 988f5656ea5810a706e17f236e9f845b443317f0 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Wed, 31 Jan 2024 18:00:09 +0000 Subject: [PATCH] wip --- .../src/private_kernel_ordering.nr | 91 +++++++++++++++++-- .../src/abis/combined_accumulated_data.nr | 59 +++++++++++- .../src/abis/kernel_circuit_public_inputs.nr | 8 +- .../src/crates/types/src/utils/arrays.nr | 36 +++++++- 4 files changed, 180 insertions(+), 14 deletions(-) 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 6c6496b7ad51..d19bf9a85e56 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 @@ -10,7 +10,7 @@ use dep::types::{ nullifier_key_validation_request::NullifierKeyValidationRequestContext, previous_kernel_data::PreviousKernelData, kernel_circuit_public_inputs::{ - KernelCircuitPublicInputsBuilder, + KernelCircuitPublicInputsBuilder, KernelCircuitPublicInputsFinal, }, side_effect::{SideEffect, SideEffectLinkedToNoteHash, Ordered}, @@ -34,6 +34,7 @@ use dep::types::{ struct PrivateKernelInputsOrdering { previous_kernel: PreviousKernelData, + meta_hwm: Field, sorted_new_commitments: [SideEffect; MAX_NEW_COMMITMENTS_PER_TX], sorted_new_commitments_indexes: [u32; MAX_NEW_COMMITMENTS_PER_TX], read_commitment_hints: [Field; MAX_READ_REQUESTS_PER_TX], @@ -45,7 +46,7 @@ struct PrivateKernelInputsOrdering { impl PrivateKernelInputsOrdering { fn validate_inputs(self) { - assert_eq(array_length(self.previous_kernel.public_inputs.end.private_call_stack), 0, + assert_eq(array_length(self.previous_kernel.public_inputs.end.private_call_stack), 0, "Private call stack must be empty when executing the ordering circuit"); } @@ -200,7 +201,7 @@ impl PrivateKernelInputsOrdering { self.validate_inputs(); common::validate_previous_kernel_values(self.previous_kernel.public_inputs.end); - + // Do this before any functions can modify the inputs. common::initialize_end_values(self.previous_kernel, &mut public_inputs); @@ -210,11 +211,11 @@ impl PrivateKernelInputsOrdering { self.match_reads_to_commitments(&mut public_inputs); - self.match_nullifiers_to_commitments_and_squash(&mut public_inputs); + self.match_nullifiers_to_commitments_and_squash(&mut public_inputs); - PrivateKernelInputsOrdering::apply_commitment_nonces(&mut public_inputs); + PrivateKernelInputsOrdering::apply_commitment_nonces(&mut public_inputs); - public_inputs.to_final() + public_inputs.to_final(self.meta_hwm as u32) } } @@ -241,6 +242,7 @@ mod tests { struct PrivateKernelOrderingInputsBuilder { previous_kernel: PreviousKernelDataBuilder, + meta_hwm: Field, read_commitment_hints: [Field; MAX_READ_REQUESTS_PER_TX], nullifier_commitment_hints: [Field; MAX_NEW_NULLIFIERS_PER_TX], } @@ -249,6 +251,7 @@ mod tests { pub fn new() -> Self { PrivateKernelOrderingInputsBuilder { previous_kernel: PreviousKernelDataBuilder::new(), + meta_hwm: 0, read_commitment_hints: [0; MAX_READ_REQUESTS_PER_TX], nullifier_commitment_hints: [0; MAX_NEW_NULLIFIERS_PER_TX], } @@ -266,7 +269,7 @@ mod tests { self.compute_unique_siloed_commitments(self.previous_kernel.end.new_commitments.storage) } - // A helper function that uses the first nullifer in the previous kernel to compute the unique siloed + // A helper function that uses the first nullifer in the previous kernel to compute the unique siloed // commitments for the given commitments. pub fn compute_unique_siloed_commitments(self, commitments: [SideEffect; N]) -> [SideEffect; N] { let first_nullifier = self.previous_kernel.end.new_nullifiers.get_unchecked(0); @@ -330,6 +333,7 @@ mod tests { } let kernel = PrivateKernelInputsOrdering { previous_kernel: self.previous_kernel.finish(), + meta_hwm: self.meta_hwm, sorted_new_commitments, sorted_new_commitments_indexes, read_commitment_hints: sorted_read_commitment_hints, @@ -465,6 +469,12 @@ mod tests { sorted_new_commitments ); + let fee_commitments = public_inputs.fee.new_commitments; + let app_commitments = public_inputs.end.new_commitments; + dep::std::println(f"fee: {fee_commitments}"); + dep::std::println(f"app: {app_commitments}"); + dep::std::println(f"commitments: {sorted_unique_commitments}"); + for i in 0..10 { assert(public_inputs.end.new_commitments[i].eq(sorted_unique_commitments[i])); // +1 due to the 0th nullifier being the tx hash @@ -532,4 +542,71 @@ mod tests { builder.previous_kernel.end.new_nullifiers = BoundedVec::new(SideEffectLinkedToNoteHash::empty()); builder.failed(); } + + #[test] + unconstrained fn partitions_data() { + let mut builder = PrivateKernelOrderingInputsBuilder::new(); + + let mut sorted_new_commitments = [SideEffect::empty(); 10]; + let mut sorted_new_nullifiers = [SideEffectLinkedToNoteHash::empty(); 10]; + + let mut counter = 1; + for i in 0..10 { + sorted_new_commitments[i] = SideEffect { value: (i + 1) as Field, counter: counter }; + counter += 1; + sorted_new_nullifiers[i] = SideEffectLinkedToNoteHash { value: (i + 11) as Field, counter: counter, note_hash: 0 }; + counter += 1; + } + + for i in 0..10 { + builder.previous_kernel.end.new_commitments.push(sorted_new_commitments[9 - i]); + builder.previous_kernel.end.new_nullifiers.push(sorted_new_nullifiers[9 - i]); + } + + builder.meta_hwm = (counter / 2) as Field; + + let public_inputs = builder.execute(); + + let sorted_unique_commitments = compute_unique_siloed_commitments( + public_inputs.fee.new_nullifiers[0].value, + sorted_new_commitments + ); + + // let fee_commitments = public_inputs.fee.new_nullifiers; + // let app_commitments = public_inputs.end.new_nullifiers; + // dep::std::println(f"fee: {fee_commitments}"); + // dep::std::println(""); + // dep::std::println(f"app: {app_commitments}"); + // dep::std::println(""); + // dep::std::println(f"commitments: {sorted_unique_commitments}"); + // dep::std::println(f"commitments: {sorted_new_nullifiers}"); + + for i in 0..5 { + let fee_commitment = public_inputs.fee.new_commitments[i]; + let commitment = sorted_unique_commitments[i]; + + assert(fee_commitment.eq(commitment)); + // assert(public_inputs.fee.new_nullifiers[i + 1].eq(sorted_new_nullifiers[i])); + } + + for i in 1..5 { + let fee_nullifier = public_inputs.fee.new_nullifiers[i]; + // -1 because of the first nullifier being tx hash + let nullifier = sorted_new_nullifiers[i - 1]; + // dep::std::println(f"i: {i}: {fee_nullifier} {nullifier}"); + assert(fee_nullifier.eq(nullifier)); + } + + for i in 0..5 { + let app_commitment = public_inputs.end.new_commitments[i]; + let commitment = sorted_unique_commitments[5 + i]; + assert(app_commitment.eq(commitment)); + } + for i in 0..5 { + let app_nullifier = public_inputs.end.new_nullifiers[i]; + let nullifier = sorted_new_nullifiers[4 + i]; + dep::std::println(f"i: {i}: {app_nullifier} {nullifier}"); + assert(app_nullifier.eq(nullifier)); + } + } } diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/combined_accumulated_data.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/combined_accumulated_data.nr index 9e74c0ae1034..fb20420c44d6 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/combined_accumulated_data.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/combined_accumulated_data.nr @@ -6,9 +6,10 @@ use crate::{ optionally_revealed_data::OptionallyRevealedData, public_data_read::PublicDataRead, public_data_update_request::PublicDataUpdateRequest, - side_effect::{SideEffect, SideEffectLinkedToNoteHash}, + side_effect::{SideEffect, SideEffectLinkedToNoteHash, Ordered}, }, - mocked::AggregationObject + mocked::AggregationObject, + utils::arrays::partition_array, }; use crate::constants::{ MAX_READ_REQUESTS_PER_TX, @@ -136,7 +137,57 @@ impl CombinedAccumulatedDataBuilder { public_data_reads: self.public_data_reads.storage, } - } + } + + pub fn to_final_final(self, meta_hwm: u32) -> (FinalAccumulatedData, FinalAccumulatedData) { + let (fee_commitments, app_commitments) = partition_array(self.new_commitments.storage, |x: SideEffect| -> bool x.counter() < meta_hwm); + let (fee_nullifiers, app_nullifiers) = partition_array(self.new_nullifiers.storage, |x: SideEffectLinkedToNoteHash| x.counter() < meta_hwm); + let (fee_private_call_stack, app_private_call_stack) = partition_array(self.private_call_stack.storage, |x: CallRequest| x.end_side_effect_counter < meta_hwm); + let (fee_public_call_stack, app_public_call_stack) = partition_array(self.public_call_stack.storage, |x: CallRequest| x.end_side_effect_counter < meta_hwm); + + let fee = FinalAccumulatedData { + aggregation_object: self.aggregation_object, + + new_commitments: fee_commitments, + new_nullifiers: fee_nullifiers, + + private_call_stack: fee_private_call_stack, + public_call_stack: fee_public_call_stack, + new_l2_to_l1_msgs: [0; MAX_NEW_L2_TO_L1_MSGS_PER_TX], + + encrypted_logs_hash: [0, 0], + unencrypted_logs_hash: [0, 0], + + encrypted_log_preimages_length: 0, + unencrypted_log_preimages_length: 0, + + new_contracts: [NewContractData::empty(); MAX_NEW_CONTRACTS_PER_TX], + + optionally_revealed_data: self.optionally_revealed_data, + }; + + let app = FinalAccumulatedData { + aggregation_object: self.aggregation_object, + + new_commitments: app_commitments, + new_nullifiers: app_nullifiers, + + private_call_stack: app_private_call_stack, + public_call_stack: app_public_call_stack, + new_l2_to_l1_msgs: self.new_l2_to_l1_msgs.storage, + + encrypted_logs_hash: self.encrypted_logs_hash, + unencrypted_logs_hash: self.unencrypted_logs_hash, + + encrypted_log_preimages_length: self.encrypted_log_preimages_length, + unencrypted_log_preimages_length: self.unencrypted_log_preimages_length, + + new_contracts: self.new_contracts.storage, + optionally_revealed_data: self.optionally_revealed_data, + }; + + (fee, app) + } pub fn to_final(self) -> FinalAccumulatedData { assert_eq(self.read_requests.len, 0, "Final accumulated data: read requests not empty"); @@ -163,5 +214,5 @@ impl CombinedAccumulatedDataBuilder { new_contracts: self.new_contracts.storage, optionally_revealed_data: self.optionally_revealed_data, } - } + } } diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/kernel_circuit_public_inputs.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/kernel_circuit_public_inputs.nr index c847b901b36d..10bc83f96b42 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/kernel_circuit_public_inputs.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/kernel_circuit_public_inputs.nr @@ -14,6 +14,7 @@ struct KernelCircuitPublicInputs { } struct KernelCircuitPublicInputsFinal { + fee: FinalAccumulatedData, end: FinalAccumulatedData, constants: CombinedConstantData, is_private: bool, @@ -23,6 +24,7 @@ struct KernelCircuitPublicInputsBuilder { end: CombinedAccumulatedDataBuilder, constants: CombinedConstantData, is_private: bool, + meta_hwm: Field, } impl KernelCircuitPublicInputsBuilder { @@ -34,9 +36,11 @@ impl KernelCircuitPublicInputsBuilder { } } - pub fn to_final(self) -> KernelCircuitPublicInputsFinal { + pub fn to_final(self, meta_hwm: u32) -> KernelCircuitPublicInputsFinal { + let (fee, app) = self.end.to_final_final(meta_hwm); KernelCircuitPublicInputsFinal { - end: self.end.to_final(), + fee: fee, + end: app, constants: self.constants, is_private: self.is_private, } diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/utils/arrays.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/utils/arrays.nr index 609071c4d847..8975efd3f646 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/utils/arrays.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/utils/arrays.nr @@ -33,7 +33,7 @@ pub fn validate_array(array: [T; N]) where T: Empty + Eq { assert((last_non_zero_pos as u64) <= (first_zero_pos as u64), "invalid array"); } -// Helper method to determine the number of non-zero/empty elements in a validated array (ie, validate_array(array) +// Helper method to determine the number of non-zero/empty elements in a validated array (ie, validate_array(array) // should be true). pub fn array_length(array: [T; N]) -> Field where T: Empty + Eq { let mut length = 0; @@ -57,6 +57,31 @@ pub fn array_eq(array: [T; N], expected: [T; S]) -> bool where T: Empty eq } +/** + * Splits an array in two parts, where the first part contains all elements for which the predicate is true, + * and the second part contains all elements for which the predicate is false. + */ +pub fn partition_array(array: [T; N], predicate: fn[Env](T) -> bool) -> ([T; N], [T; N]) where T: Empty { + let mut partition_a = [T::empty(); N]; + let mut a_idx = 0; + + let mut partition_b = [T::empty(); N]; + let mut b_idx = 0; + + for i in 0..N { + let elem = array[i]; + if predicate(elem) { + partition_a[a_idx] = elem; + a_idx += 1; + } else { + partition_b[b_idx] = elem; + b_idx += 1; + } + } + + (partition_a, partition_b) +} + #[test] fn smoke_validate_array() { let valid_array = []; @@ -102,3 +127,12 @@ fn test_array_length() { assert_eq(array_length([123, 0, 456]), 1); assert_eq(array_length([0, 123, 0, 456]), 0); } + +#[test] +fn test_partition_array() { + let array: [u32; 9] = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + let (partition_a, partition_b) = partition_array(array, |x| x % 2 == 0); + + assert_eq(partition_a, [2, 4, 6, 8, 0, 0, 0, 0, 0]); + assert_eq(partition_b, [1, 3, 5, 7, 9, 0, 0, 0, 0]); +}