From 1cf14d83b15fab9ef4d918fd8d0f666017a6004b Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Wed, 28 Aug 2024 11:31:12 +0000 Subject: [PATCH 01/14] Add counter to public data read. --- .../src/reset/mutable_data_read_request.nr | 106 ++++++++++++------ .../rollup-lib/src/base/base_rollup_inputs.nr | 5 +- .../crates/types/src/abis/public_data_read.nr | 23 ++-- .../crates/types/src/constants.nr | 2 +- .../crates/types/src/tests/fixture_builder.nr | 13 +-- .../src/hints/build_public_data_hints.test.ts | 3 +- ...ild_public_data_read_request_hints.test.ts | 3 +- yarn-project/circuits.js/src/structs/index.ts | 2 +- ...ta_read_request.ts => public_data_read.ts} | 34 ++---- .../src/structs/public_validation_requests.ts | 2 +- .../circuits.js/src/tests/factories.ts | 4 +- .../src/type_conversion.ts | 7 +- 12 files changed, 117 insertions(+), 87 deletions(-) rename yarn-project/circuits.js/src/structs/{public_data_read_request.ts => public_data_read.ts} (56%) diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/mutable_data_read_request.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/mutable_data_read_request.nr index e54a6cc45802..dea8e8b6a111 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/mutable_data_read_request.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/mutable_data_read_request.nr @@ -15,7 +15,7 @@ impl LeafDataReadHint { } } -fn validate_pending_read_requests( +fn validate_pending_read_requests( read_requests: [PublicDataRead; READ_REQUEST_LEN], data_writes: [PublicDataUpdateRequest; PENDING_VALUE_LEN], hints: [PendingReadHint; NUM_PENDING_READS] @@ -31,16 +31,16 @@ fn validate_pending_read_requests pending_value.counter, "Read request counter must be greater than the counter of the data write" - // ); + assert( + read_request.counter > pending_value.counter, "Read request counter must be greater than the counter of the data write" + ); + // TODO // assert((read_request.counter < pending_value.next_counter) | (pending_value.next_counter == 0), "Read request counter must be less than the counter of the next data write"); } } } -fn validate_leaf_data_read_requests( +fn validate_leaf_data_read_requests( read_requests: [PublicDataRead; READ_REQUEST_LEN], leaf_data_hints: [H; NUM_LEAF_DATA_HINTS], hints: [LeafDataReadHint; NUM_LEAF_DATA_READS] @@ -54,13 +54,15 @@ fn validate_leaf_data_read_requests( +fn ensure_all_read_requests_are_verified( read_requests: [PublicDataRead; READ_REQUEST_LEN], read_request_statuses: [ReadRequestStatus; READ_REQUEST_LEN], pending_read_hints: [PendingReadHint; NUM_PENDING_READS], @@ -85,7 +87,14 @@ fn ensure_all_read_requests_are_verified( +pub fn reset_mutable_data_read_requests< + let READ_REQUEST_LEN: u32, + let PENDING_VALUE_LEN: u32, + H, + let NUM_LEAF_DATA_HINTS: u32, + let NUM_PENDING_READS: u32, + let NUM_LEAF_DATA_READS: u32 +>( read_requests: [PublicDataRead; READ_REQUEST_LEN], read_request_statuses: [ReadRequestStatus; READ_REQUEST_LEN], data_writes: [PublicDataUpdateRequest; PENDING_VALUE_LEN], @@ -121,6 +130,7 @@ mod tests { struct TestLeafDataHint { leaf_slot: Field, value: Field, + override_counter: u32, } impl LeafDataHint for TestLeafDataHint { @@ -131,45 +141,58 @@ mod tests { fn value(self) -> Field { self.value } - - fn override_counter(_self: Self) -> u32 { - 0 + + fn override_counter(self) -> u32 { + self.override_counter } } global data_writes = [ - PublicDataUpdateRequest { leaf_slot: 22, new_value: 200, counter: 0 }, - PublicDataUpdateRequest { leaf_slot: 11, new_value: 100, counter: 1 }, - PublicDataUpdateRequest { leaf_slot: 33, new_value: 300, counter: 2 }, - PublicDataUpdateRequest { leaf_slot: 44, new_value: 400, counter: 3 } + PublicDataUpdateRequest { leaf_slot: 22, new_value: 200, counter: 10 }, + PublicDataUpdateRequest { leaf_slot: 11, new_value: 100, counter: 20 }, + PublicDataUpdateRequest { leaf_slot: 33, new_value: 300, counter: 30 }, + PublicDataUpdateRequest { leaf_slot: 44, new_value: 400, counter: 40 } ]; global leaf_data_hints = [ - TestLeafDataHint { leaf_slot: 7, value: 70 }, - TestLeafDataHint { leaf_slot: 6, value: 60 }, - TestLeafDataHint { leaf_slot: 5, value: 50 }, + TestLeafDataHint { leaf_slot: 7, value: 70, override_counter: 0 }, + TestLeafDataHint { leaf_slot: 6, value: 60, override_counter: 90 }, + TestLeafDataHint { leaf_slot: 5, value: 50, override_counter: 0 }, ]; - fn create_pending_read_requests(data_write_indices: [u32; N]) -> ([PublicDataRead; N], [PendingReadHint; N]) { - let read_requests = data_write_indices.map( - |data_write_index: u32| PublicDataRead { leaf_slot: data_writes[data_write_index].leaf_slot, value: data_writes[data_write_index].new_value } - ); + fn create_pending_read_requests(data_write_indices: [u32; N]) -> ([PublicDataRead; N], [PendingReadHint; N]) { + let mut read_requests = BoundedVec::new(); let mut hints = BoundedVec::new(); + let last_counter = 1 + data_writes[data_writes.len() - 1].counter(); for i in 0..N { - hints.push(PendingReadHint { read_request_index: i, pending_value_index: data_write_indices[i] }); + let pending_value_index = data_write_indices[i]; + read_requests.push( + PublicDataRead { + leaf_slot: data_writes[pending_value_index].leaf_slot, + value: data_writes[pending_value_index].new_value, + counter: last_counter + i + } + ); + hints.push(PendingReadHint { read_request_index: i, pending_value_index }); } - (read_requests, hints.storage) + (read_requests.storage, hints.storage) } - fn create_leaf_data_read_requests(data_hint_indices: [u32; N]) -> ([PublicDataRead; N], [LeafDataReadHint; N]) { - let read_requests = data_hint_indices.map( - |data_hint_index: u32| PublicDataRead { leaf_slot: leaf_data_hints[data_hint_index].leaf_slot, value: leaf_data_hints[data_hint_index].value } - ); + fn create_leaf_data_read_requests(data_hint_indices: [u32; N]) -> ([PublicDataRead; N], [LeafDataReadHint; N]) { + let mut read_requests = BoundedVec::new(); let mut hints = BoundedVec::new(); for i in 0..N { - hints.push(LeafDataReadHint { read_request_index: i, data_hint_index: data_hint_indices[i] }); + let data_hint_index = data_hint_indices[i]; + read_requests.push( + PublicDataRead { + leaf_slot: leaf_data_hints[data_hint_index].leaf_slot, + value: leaf_data_hints[data_hint_index].value, + counter: 80 + i + } + ); + hints.push(LeafDataReadHint { read_request_index: i, data_hint_index }); } - (read_requests, hints.storage) + (read_requests.storage, hints.storage) } #[test] @@ -186,7 +209,7 @@ mod tests { #[test] fn reset_pending_reads_skips_nada() { - let read_requests = [PublicDataRead { leaf_slot: 88, value: 9999 }]; + let read_requests = [PublicDataRead { leaf_slot: 88, value: 9999, counter: 2 }]; let hints = [PendingReadHint::nada(1)]; validate_pending_read_requests(read_requests, data_writes, hints); } @@ -205,6 +228,13 @@ mod tests { validate_pending_read_requests(read_requests, data_writes, hints); } + #[test(should_fail_with="Read request counter must be greater than the counter of the data write")] + fn reset_pending_reads_value_write_after_fails() { + let mut (read_requests, hints) = create_pending_read_requests([1]); + read_requests[0].counter = data_writes[1].counter - 1; + validate_pending_read_requests(read_requests, data_writes, hints); + } + #[test] fn reset_leaf_data_reads_succeeds() { let (read_requests, hints) = create_leaf_data_read_requests([2, 1, 0]); @@ -219,7 +249,7 @@ mod tests { #[test] fn reset_leaf_data_reads_skips_nada() { - let read_requests = [PublicDataRead { leaf_slot: 88, value: 9999 }]; + let read_requests = [PublicDataRead { leaf_slot: 88, value: 9999, counter: 2 }]; let hints = [LeafDataReadHint::nada(1)]; validate_leaf_data_read_requests(read_requests, leaf_data_hints, hints); } @@ -238,6 +268,14 @@ mod tests { validate_leaf_data_read_requests(read_requests, leaf_data_hints, hints); } + #[test(should_fail_with=""Hinted leaf is overridden before the read request")] + fn reset_leaf_reads_overridden_value_fails() { + let mut leaf_data_hints_with_override_counter = leaf_data_hints; + let mut (read_requests, hints) = create_leaf_data_read_requests([1]); + leaf_data_hints_with_override_counter[1].override_counter = read_requests[0].counter - 1; + validate_leaf_data_read_requests(read_requests, leaf_data_hints_with_override_counter, hints); + } + #[test] fn ensure_all_read_requests_are_verified_succeeds() { let mut (pending_read_requests, pending_read_hints) = create_pending_read_requests([1]); diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr index 0f30eabfed14..43345b48155a 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr @@ -15,8 +15,7 @@ use dep::types::{ storage::map::derive_storage_slot_in_map, address::AztecAddress, abis::{ append_only_tree_snapshot::AppendOnlyTreeSnapshot, nullifier_leaf_preimage::NullifierLeafPreimage, - public_data_update_request::PublicDataUpdateRequest, public_data_read::PublicDataRead, - kernel_data::KernelData + public_data_update_request::PublicDataUpdateRequest, kernel_data::KernelData }, messaging::l2_to_l1_message::ScopedL2ToL1Message, constants::{ @@ -467,7 +466,7 @@ mod tests { use dep::types::{ abis::{ append_only_tree_snapshot::AppendOnlyTreeSnapshot, - nullifier_leaf_preimage::NullifierLeafPreimage, public_data_read::PublicDataRead, + nullifier_leaf_preimage::NullifierLeafPreimage, public_data_update_request::PublicDataUpdateRequest, kernel_data::KernelData, accumulated_data::CombinedAccumulatedData, gas::Gas }, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_read.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_read.nr index 0d12098c4f55..4a850c650629 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_read.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_read.nr @@ -5,8 +5,9 @@ use crate::data::hash::{compute_public_data_tree_value, compute_public_data_tree use crate::address::AztecAddress; struct PublicDataRead { - leaf_slot : Field, - value : Field, + leaf_slot: Field, + value: Field, + counter: u32, } impl PublicDataRead { @@ -16,22 +17,24 @@ impl PublicDataRead { ) -> PublicDataRead { PublicDataRead { leaf_slot: compute_public_data_tree_index(contract_address, read_request.storage_slot), - value: compute_public_data_tree_value(read_request.current_value) + value: compute_public_data_tree_value(read_request.current_value), + counter: read_request.counter } } } impl Eq for PublicDataRead { - fn eq(self, public_data_read: PublicDataRead) -> bool { - (public_data_read.leaf_slot == self.leaf_slot) & (public_data_read.value == self.value) + fn eq(self, other: PublicDataRead) -> bool { + (other.leaf_slot == self.leaf_slot) & (other.value == self.value) & (other.counter == self.counter) } } impl Empty for PublicDataRead { fn empty() -> Self { Self { - leaf_slot : 0, - value : 0, + leaf_slot: 0, + value: 0, + counter: 0, } } } @@ -41,19 +44,20 @@ impl Hash for PublicDataRead { crate::hash::poseidon2_hash_with_separator([ self.leaf_slot, self.value, + self.counter as Field, ], GENERATOR_INDEX__PUBLIC_DATA_READ) } } impl PublicDataRead { pub fn is_empty(self) -> bool { - (self.leaf_slot == 0) & (self.value == 0) + (self.leaf_slot == 0) & (self.value == 0) & (self.counter == 0) } } impl Serialize for PublicDataRead { fn serialize(self) -> [Field; PUBLIC_DATA_READ_LENGTH] { - [self.leaf_slot, self.value] + [self.leaf_slot, self.value, self.counter as Field] } } @@ -62,6 +66,7 @@ impl Deserialize for PublicDataRead { PublicDataRead { leaf_slot: fields[0], value: fields[1], + counter: fields[2] as u32, } } } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index ebb231a48959..499fe1e4d26f 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -247,7 +247,7 @@ global PUBLIC_CONTEXT_INPUTS_LENGTH: u32 = CALL_CONTEXT_LENGTH + HEADER_LENGTH + global AGGREGATION_OBJECT_LENGTH: u32 = 16; global SCOPED_READ_REQUEST_LEN = READ_REQUEST_LENGTH + 1; -global PUBLIC_DATA_READ_LENGTH = 2; +global PUBLIC_DATA_READ_LENGTH = 3; global PRIVATE_VALIDATION_REQUESTS_LENGTH = ROLLUP_VALIDATION_REQUESTS_LENGTH + (SCOPED_READ_REQUEST_LEN * MAX_NOTE_HASH_READ_REQUESTS_PER_TX) + (SCOPED_READ_REQUEST_LEN * MAX_NULLIFIER_READ_REQUESTS_PER_TX) + (SCOPED_KEY_VALIDATION_REQUEST_AND_GENERATOR_LENGTH * MAX_KEY_VALIDATION_REQUESTS_PER_TX) + 2; global PUBLIC_VALIDATION_REQUESTS_LENGTH = ROLLUP_VALIDATION_REQUESTS_LENGTH + (SCOPED_READ_REQUEST_LEN * MAX_NULLIFIER_READ_REQUESTS_PER_TX) + (SCOPED_READ_REQUEST_LEN * MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX) + (PUBLIC_DATA_READ_LENGTH * MAX_PUBLIC_DATA_READS_PER_TX); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr index 0427e00154ae..8bd06548d490 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr @@ -723,15 +723,15 @@ impl FixtureBuilder { } pub fn add_public_data_read_request(&mut self, leaf_slot: Field, value: Field) { - self.public_data_reads.push(PublicDataRead { leaf_slot, value }); + self.public_data_reads.push(PublicDataRead { leaf_slot, value, counter: self.next_counter() }); } pub fn append_public_data_read_requests(&mut self, num_reads: u32) { let index_offset = self.public_data_reads.len(); for i in 0..self.public_data_reads.max_len() { if i < num_reads { - let read_request = self.mock_public_data_read(index_offset + i); - self.add_public_data_read_request(read_request.leaf_slot, read_request.value); + let (leaf_slot, value) = self.mock_public_data_read(index_offset + i); + self.add_public_data_read_request(leaf_slot, value); } } } @@ -792,8 +792,7 @@ impl FixtureBuilder { pub fn add_read_request_for_pending_public_data(&mut self, public_date_update_request_index: u32) -> u32 { let new_read_request_index = self.public_data_reads.len(); let public_write = self.public_data_update_requests.get(public_date_update_request_index); - let read_request = PublicDataRead { leaf_slot: public_write.leaf_slot, value: public_write.new_value }; - self.public_data_reads.push(read_request); + self.add_public_data_read_request(public_write.leaf_slot, public_write.new_value); new_read_request_index } @@ -1029,9 +1028,9 @@ impl FixtureBuilder { KeyValidationRequestAndGenerator { request, sk_app_generator: 3 + value_offset } } - fn mock_public_data_read(self, index: u32) -> PublicDataRead { + fn mock_public_data_read(self, index: u32) -> (Field, Field) { let value_offset = 4545 + self.value_offset + index as Field; - PublicDataRead { leaf_slot: value_offset, value: 1 + value_offset } + (value_offset, value_offset + 1) } fn mock_public_data_write(self, index: u32) -> (Field, Field) { diff --git a/yarn-project/circuits.js/src/hints/build_public_data_hints.test.ts b/yarn-project/circuits.js/src/hints/build_public_data_hints.test.ts index 3e038e13c9de..74a91f38d244 100644 --- a/yarn-project/circuits.js/src/hints/build_public_data_hints.test.ts +++ b/yarn-project/circuits.js/src/hints/build_public_data_hints.test.ts @@ -40,7 +40,8 @@ describe('buildPublicDataHints', () => { new PublicDataTreeLeafPreimage(new Fr(0), new Fr(0), new Fr(11), 0n), ]; - const makePublicDataRead = (leafSlot: number, value: number) => new PublicDataRead(new Fr(leafSlot), new Fr(value)); + const makePublicDataRead = (leafSlot: number, value: number) => + new PublicDataRead(new Fr(leafSlot), new Fr(value), nextSideEffectCounter()); const makePublicDataWrite = (leafSlot: number, value: number) => new PublicDataUpdateRequest(new Fr(leafSlot), new Fr(value), nextSideEffectCounter()); diff --git a/yarn-project/circuits.js/src/hints/build_public_data_read_request_hints.test.ts b/yarn-project/circuits.js/src/hints/build_public_data_read_request_hints.test.ts index 4afea4a13b90..e1e0193decce 100644 --- a/yarn-project/circuits.js/src/hints/build_public_data_read_request_hints.test.ts +++ b/yarn-project/circuits.js/src/hints/build_public_data_read_request_hints.test.ts @@ -34,7 +34,8 @@ describe('buildPublicDataReadRequestHints', () => { hint.value = new Fr(value); return hint; }; - const makePublicDataRead = (leafSlot: number, value: number) => new PublicDataRead(new Fr(leafSlot), new Fr(value)); + const makePublicDataRead = (leafSlot: number, value: number) => + new PublicDataRead(new Fr(leafSlot), new Fr(value), nextCounter()); const makePendingHint = (readRequestIndex: number, hintIndex: number) => new PendingReadHint(readRequestIndex, hintIndex); const makeLeafDataHint = (readRequestIndex: number, hintIndex: number) => diff --git a/yarn-project/circuits.js/src/structs/index.ts b/yarn-project/circuits.js/src/structs/index.ts index e58f824e9f8d..d5e042313a6f 100644 --- a/yarn-project/circuits.js/src/structs/index.ts +++ b/yarn-project/circuits.js/src/structs/index.ts @@ -60,7 +60,7 @@ export * from './public_call_stack_item.js'; export * from './public_call_stack_item_compressed.js'; export * from './public_circuit_public_inputs.js'; export * from './public_data_hint.js'; -export * from './public_data_read_request.js'; +export * from './public_data_read.js'; export * from './public_data_read_request_hints.js'; export * from './public_data_update_request.js'; export * from './public_validation_requests.js'; diff --git a/yarn-project/circuits.js/src/structs/public_data_read_request.ts b/yarn-project/circuits.js/src/structs/public_data_read.ts similarity index 56% rename from yarn-project/circuits.js/src/structs/public_data_read_request.ts rename to yarn-project/circuits.js/src/structs/public_data_read.ts index 4244f93ff9ea..e5a8010fd7eb 100644 --- a/yarn-project/circuits.js/src/structs/public_data_read_request.ts +++ b/yarn-project/circuits.js/src/structs/public_data_read.ts @@ -1,7 +1,6 @@ import { Fr } from '@aztec/foundation/fields'; import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; -// TODO: Rename to PublicDataReadRequest /** * Read operations from the public state tree. */ @@ -16,51 +15,34 @@ export class PublicDataRead { */ public readonly value: Fr, /** - * Optional side effect counter tracking position of this event in tx execution. + * Side effect counter tracking position of this event in tx execution. */ - public readonly sideEffectCounter?: number, + public readonly counter: number, ) {} - static from(args: { - /** - * Index of the leaf in the public data tree. - */ - leafIndex: Fr; - /** - * Returned value from the public data tree. - */ - value: Fr; - }) { - return new PublicDataRead(args.leafIndex, args.value); - } - toBuffer() { - return serializeToBuffer(this.leafSlot, this.value); + return serializeToBuffer(this.leafSlot, this.value, this.counter); } isEmpty() { - return this.leafSlot.isZero() && this.value.isZero(); + return this.leafSlot.isZero() && this.value.isZero() && !this.counter; } static fromFields(fields: Fr[] | FieldReader) { const reader = FieldReader.asReader(fields); - return new PublicDataRead(reader.readField(), reader.readField()); + return new PublicDataRead(reader.readField(), reader.readField(), reader.readU32()); } static fromBuffer(buffer: Buffer | BufferReader) { const reader = BufferReader.asReader(buffer); - return new PublicDataRead(Fr.fromBuffer(reader), Fr.fromBuffer(reader)); + return new PublicDataRead(Fr.fromBuffer(reader), Fr.fromBuffer(reader), reader.readNumber()); } static empty() { - return new PublicDataRead(Fr.ZERO, Fr.ZERO); - } - - toFriendlyJSON() { - return `Leaf=${this.leafSlot.toFriendlyJSON()}: ${this.value.toFriendlyJSON()}`; + return new PublicDataRead(Fr.ZERO, Fr.ZERO, 0); } equals(other: PublicDataRead) { - return this.leafSlot.equals(other.leafSlot) && this.value.equals(other.value); + return this.leafSlot.equals(other.leafSlot) && this.value.equals(other.value) && this.counter == other.counter; } } diff --git a/yarn-project/circuits.js/src/structs/public_validation_requests.ts b/yarn-project/circuits.js/src/structs/public_validation_requests.ts index af29d5537369..97c607cd0fcc 100644 --- a/yarn-project/circuits.js/src/structs/public_validation_requests.ts +++ b/yarn-project/circuits.js/src/structs/public_validation_requests.ts @@ -10,7 +10,7 @@ import { MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_PUBLIC_DATA_READS_PER_TX, } from '../constants.gen.js'; -import { PublicDataRead } from './public_data_read_request.js'; +import { PublicDataRead } from './public_data_read.js'; import { ScopedReadRequest } from './read_request.js'; import { RollupValidationRequests } from './rollup_validation_requests.js'; diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index a9e06ab980ef..d543cba90c0d 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -278,7 +278,7 @@ export function makeEmptyPublicDataUpdateRequest(): PublicDataUpdateRequest { * @returns A public data read. */ export function makePublicDataRead(seed = 1): PublicDataRead { - return new PublicDataRead(fr(seed), fr(seed + 1)); + return new PublicDataRead(fr(seed), fr(seed + 1), 0); } /** @@ -286,7 +286,7 @@ export function makePublicDataRead(seed = 1): PublicDataRead { * @returns An empty public data read. */ export function makeEmptyPublicDataRead(): PublicDataRead { - return new PublicDataRead(fr(0), fr(0)); + return new PublicDataRead(fr(0), fr(0), 0); } /** diff --git a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts index 150396a60dd9..fe629bbdb28e 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -1052,7 +1052,11 @@ export function mapPublicDataUpdateRequestToNoir( * @returns The parsed public data read. */ export function mapPublicDataReadFromNoir(publicDataRead: PublicDataReadNoir): PublicDataRead { - return new PublicDataRead(mapFieldFromNoir(publicDataRead.leaf_slot), mapFieldFromNoir(publicDataRead.value)); + return new PublicDataRead( + mapFieldFromNoir(publicDataRead.leaf_slot), + mapFieldFromNoir(publicDataRead.value), + mapNumberFromNoir(publicDataRead.counter), + ); } /** @@ -1064,6 +1068,7 @@ export function mapPublicDataReadToNoir(publicDataRead: PublicDataRead): PublicD return { leaf_slot: mapFieldToNoir(publicDataRead.leafSlot), value: mapFieldToNoir(publicDataRead.value), + counter: mapNumberToNoir(publicDataRead.counter), }; } From 87083944b890e5bbc49860d5b0b9049988793718 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Fri, 30 Aug 2024 13:50:18 +0000 Subject: [PATCH 02/14] Validate public reads and writes properly. --- .../components/public_tail_output_composer.nr | 23 +- .../combine_data.nr | 15 +- ...combined_overridable_public_data_writes.nr | 29 + .../public_tail_output_validator.nr | 48 +- .../output_hints.nr | 25 +- .../validate_linked_public_data_writes.nr | 101 ++++ .../validate_public_data_hints.nr | 63 ++ .../src/public_kernel_tail.nr | 274 +++++---- .../crates/reset-kernel-lib/src/lib.nr | 4 +- .../src/public_data_read_request_hints.nr | 47 ++ .../src/public_data_read_request_reset.nr | 9 - .../public_validation_request_processor.nr | 45 +- .../src/reset/mutable_data_read_request.nr | 540 +++++++++++------- .../src/reset/read_request.nr | 28 +- .../crates/reset-kernel-lib/src/tests/mod.nr | 1 - .../public_data_read_request_hints_builder.nr | 29 - .../crates/types/src/abis/mod.nr | 1 + .../crates/types/src/abis/note_hash.nr | 4 +- .../types/src/abis/note_hash_leaf_preimage.nr | 2 +- .../crates/types/src/abis/nullifier.nr | 8 +- .../types/src/abis/nullifier_leaf_preimage.nr | 2 +- .../crates/types/src/abis/public_data_read.nr | 23 +- .../src/abis/public_data_update_request.nr | 23 +- .../types/src/abis/public_data_write.nr | 57 ++ .../crates/types/src/abis/side_effect.nr | 12 +- .../crates/types/src/contrakt/storage_read.nr | 3 +- .../crates/types/src/data/leaf_data_hint.nr | 6 - .../crates/types/src/data/mod.nr | 1 - .../crates/types/src/data/public_data_hint.nr | 34 +- .../crates/types/src/tests/fixture_builder.nr | 7 - .../crates/types/src/utils/arrays.nr | 16 +- .../arrays/assert_combined_deduped_array.nr | 218 ------- .../assert_combined_transformed_array.nr | 25 + .../src/utils/arrays/assert_deduped_array.nr | 30 + ...ild_public_data_read_request_hints.test.ts | 153 ----- .../build_public_data_read_request_hints.ts | 45 -- yarn-project/circuits.js/src/hints/index.ts | 1 - yarn-project/circuits.js/src/structs/index.ts | 1 - ...blic_kernel_tail_circuit_private_inputs.ts | 4 - .../structs/public_data_read_request_hints.ts | 86 --- .../circuits.js/src/tests/factories.ts | 2 - .../src/type_conversion.ts | 20 - .../simulator/src/public/hints_builder.ts | 11 - .../src/public/tail_phase_manager.ts | 7 - 44 files changed, 995 insertions(+), 1088 deletions(-) create mode 100644 noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_combined_overridable_public_data_writes.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_linked_public_data_writes.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_hints.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_data_read_request_hints.nr delete mode 100644 noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_data_read_request_reset.nr delete mode 100644 noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/public_data_read_request_hints_builder.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_write.nr delete mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/data/leaf_data_hint.nr delete mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_transformed_array.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_deduped_array.nr delete mode 100644 yarn-project/circuits.js/src/hints/build_public_data_read_request_hints.test.ts delete mode 100644 yarn-project/circuits.js/src/hints/build_public_data_read_request_hints.ts delete mode 100644 yarn-project/circuits.js/src/structs/public_data_read_request_hints.ts diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer.nr index d19f34dbef36..9b369e5cd011 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer.nr @@ -1,14 +1,22 @@ mod combine_data; +mod generate_combined_overridable_public_data_writes; -use crate::components::public_tail_output_composer::combine_data::combine_data; +use crate::components::public_tail_output_composer::{ + combine_data::combine_data, + generate_combined_overridable_public_data_writes::generate_combined_overridable_public_data_writes +}; use dep::types::{ - abis::{kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PublicKernelCircuitPublicInputs}}, - partial_state_reference::PartialStateReference + abis::{ + kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PublicKernelCircuitPublicInputs}, + public_data_write::OverridablePublicDataWrite +}, + constants::MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, partial_state_reference::PartialStateReference }; struct PublicTailOutputComposer { previous_kernel: PublicKernelCircuitPublicInputs, start_state: PartialStateReference, + overridable_public_data_writes: [OverridablePublicDataWrite; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], } impl PublicTailOutputComposer { @@ -16,13 +24,18 @@ impl PublicTailOutputComposer { previous_kernel: PublicKernelCircuitPublicInputs, start_state: PartialStateReference ) -> Self { - PublicTailOutputComposer { previous_kernel, start_state } + let overridable_public_data_writes = generate_combined_overridable_public_data_writes( + previous_kernel.end_non_revertible.public_data_update_requests, + previous_kernel.end.public_data_update_requests + ); + PublicTailOutputComposer { previous_kernel, start_state, overridable_public_data_writes } } pub fn finish(self) -> KernelCircuitPublicInputs { let end = combine_data( self.previous_kernel.end_non_revertible, - self.previous_kernel.end + self.previous_kernel.end, + self.overridable_public_data_writes ); KernelCircuitPublicInputs { diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/combine_data.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/combine_data.nr index 778ccbac6ce5..3fab33176475 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/combine_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/combine_data.nr @@ -1,15 +1,17 @@ use dep::types::{ abis::{ accumulated_data::{CombinedAccumulatedData, PublicAccumulatedData}, - log_hash::{LogHash, ScopedLogHash}, nullifier::Nullifier + log_hash::{LogHash, ScopedLogHash}, nullifier::Nullifier, + public_data_write::OverridablePublicDataWrite }, - constants::MAX_NOTE_HASHES_PER_TX, hash::silo_note_hash, + constants::{MAX_NOTE_HASHES_PER_TX, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX}, hash::silo_note_hash, utils::arrays::{array_merge, dedupe_array, sort_by_counter_asc} }; unconstrained pub fn combine_data( non_revertible: PublicAccumulatedData, - revertible: PublicAccumulatedData + revertible: PublicAccumulatedData, + overridable_public_data_writes: [OverridablePublicDataWrite; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] ) -> CombinedAccumulatedData { let mut note_hashes = [0; MAX_NOTE_HASHES_PER_TX]; let sorted_unsiloed_note_hashes = sort_by_counter_asc(array_merge(non_revertible.note_hashes, revertible.note_hashes)); @@ -28,12 +30,7 @@ unconstrained pub fn combine_data( let l2_to_l1_msgs = sort_by_counter_asc(array_merge(non_revertible.l2_to_l1_msgs, revertible.l2_to_l1_msgs)); - let public_data_update_requests = dedupe_array( - array_merge( - non_revertible.public_data_update_requests, - revertible.public_data_update_requests - ) - ); + let public_data_update_requests = dedupe_array(overridable_public_data_writes); let note_encrypted_logs_hashes = sort_by_counter_asc( array_merge( diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_combined_overridable_public_data_writes.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_combined_overridable_public_data_writes.nr new file mode 100644 index 000000000000..277ab9a9f1a6 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_combined_overridable_public_data_writes.nr @@ -0,0 +1,29 @@ +use dep::types::{ + abis::{public_data_update_request::PublicDataUpdateRequest, public_data_write::OverridablePublicDataWrite}, + utils::arrays::array_merge +}; + +unconstrained pub fn generate_combined_overridable_public_data_writes( + non_revertible: [PublicDataUpdateRequest; N], + revertible: [PublicDataUpdateRequest; N] +) -> [OverridablePublicDataWrite; N] { + let mut overridable_public_data_writes = array_merge(non_revertible, revertible).map(|write| OverridablePublicDataWrite { write, override_counter: 0 }); + + for i in 0..overridable_public_data_writes.len() { + let write = overridable_public_data_writes[i].write; + if write.counter != 0 { + let mut override_counter = 0; + for j in 0..overridable_public_data_writes.len() { + let other = overridable_public_data_writes[j].write; + if (other.leaf_slot == write.leaf_slot) + & (other.counter > write.counter) + & ((override_counter == 0) | (other.counter < override_counter)) { + override_counter = other.counter; + } + } + overridable_public_data_writes[i].override_counter = override_counter; + } + } + + overridable_public_data_writes +} diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator.nr index 2dba886c754e..46e609498960 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator.nr @@ -1,31 +1,46 @@ mod output_hints; +mod validate_linked_public_data_writes; +mod validate_public_data_hints; -use crate::components::public_tail_output_validator::output_hints::{generate_output_hints, OutputHints, SiloedNoteHashHint}; +use crate::components::public_tail_output_validator::{ + output_hints::{generate_output_hints, OutputHints, SiloedNoteHashHint}, + validate_linked_public_data_writes::validate_linked_public_data_writes, + validate_public_data_hints::validate_public_data_hints +}; use dep::types::{ abis::{ kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PublicKernelCircuitPublicInputs}, - log_hash::{LogHash, ScopedLogHash}, note_hash::{NoteHash, ScopedNoteHash}, nullifier::Nullifier + log_hash::{LogHash, ScopedLogHash}, note_hash::{NoteHash, ScopedNoteHash}, nullifier::Nullifier, + public_data_update_request::PublicDataUpdateRequest, public_data_write::OverridablePublicDataWrite }, + constants::MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, data::public_data_hint::PublicDataHint, hash::silo_note_hash, messaging::l2_to_l1_message::ScopedL2ToL1Message, partial_state_reference::PartialStateReference, - utils::arrays::{array_length, assert_combined_sorted_transformed_value_array_asc, assert_combined_deduped_array} + utils::arrays::{ + array_length, assert_combined_sorted_transformed_value_array_asc, assert_combined_transformed_array, + assert_deduped_array +} }; -struct PublicTailOutputValidator { +struct PublicTailOutputValidator { output: KernelCircuitPublicInputs, previous_kernel: PublicKernelCircuitPublicInputs, start_state: PartialStateReference, + overridable_public_data_writes: [OverridablePublicDataWrite; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + public_data_hints: [PublicDataHint; N], hints: OutputHints } -impl PublicTailOutputValidator { +impl PublicTailOutputValidator { pub fn new( output: KernelCircuitPublicInputs, previous_kernel: PublicKernelCircuitPublicInputs, - start_state: PartialStateReference + start_state: PartialStateReference, + overridable_public_data_writes: [OverridablePublicDataWrite; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + public_data_hints: [PublicDataHint; N] ) -> Self { let hints = generate_output_hints(previous_kernel, output); - PublicTailOutputValidator { output, previous_kernel, start_state, hints } + PublicTailOutputValidator { output, previous_kernel, start_state, overridable_public_data_writes, public_data_hints, hints } } pub fn validate(self) { @@ -139,12 +154,23 @@ impl PublicTailOutputValidator { } fn validate_deduped_public_data_writes(self) { - assert_combined_deduped_array( + validate_public_data_hints( + self.public_data_hints, + self.start_state.public_data_tree.root + ); + + assert_combined_transformed_array( self.previous_kernel.end_non_revertible.public_data_update_requests, self.previous_kernel.end.public_data_update_requests, - self.hints.sorted_public_data_update_requests, - self.output.end.public_data_update_requests, - self.hints.deduped_public_data_update_request_hints + self.overridable_public_data_writes, + |from: PublicDataUpdateRequest, to: OverridablePublicDataWrite| to.write == from // override_counter is checked in validate_linked_public_data_writes. + ); + + validate_linked_public_data_writes(self.overridable_public_data_writes, self.public_data_hints); + + assert_deduped_array( + self.overridable_public_data_writes, + self.output.end.public_data_update_requests ); } diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/output_hints.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/output_hints.nr index 95d26ece7e24..3d7bf824602b 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/output_hints.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/output_hints.nr @@ -1,18 +1,11 @@ use dep::types::{ - abis::{ - kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PublicKernelCircuitPublicInputs}, - public_data_update_request::PublicDataUpdateRequest -}, + abis::{kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PublicKernelCircuitPublicInputs}}, constants::{ MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, MAX_L2_TO_L1_MSGS_PER_TX, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX, - MAX_ENCRYPTED_LOGS_PER_TX + MAX_UNENCRYPTED_LOGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX }, hash::silo_note_hash, traits::Empty, - utils::arrays::{ - array_merge, CombinedOrderHint, DedupedHints, get_combined_order_hints_asc, get_deduped_hints, - sort_by_position_then_counter -} + utils::arrays::{array_merge, CombinedOrderHint, get_combined_order_hints_asc} }; struct SiloedNoteHashHint { @@ -40,8 +33,6 @@ struct OutputHints { sorted_note_encrypted_log_hash_hints: [CombinedOrderHint; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], sorted_encrypted_log_hash_hints: [CombinedOrderHint; MAX_ENCRYPTED_LOGS_PER_TX], sorted_unencrypted_log_hash_hints: [CombinedOrderHint; MAX_UNENCRYPTED_LOGS_PER_TX], - sorted_public_data_update_requests: [PublicDataUpdateRequest; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - deduped_public_data_update_request_hints: DedupedHints, } unconstrained pub fn generate_output_hints( @@ -74,16 +65,6 @@ unconstrained pub fn generate_output_hints( sorted_unencrypted_log_hash_hints: get_combined_order_hints_asc( non_revertible.unencrypted_logs_hashes, revertible.unencrypted_logs_hashes - ), - sorted_public_data_update_requests: sort_by_position_then_counter( - array_merge( - non_revertible.public_data_update_requests, - revertible.public_data_update_requests - ) - ), - deduped_public_data_update_request_hints: get_deduped_hints( - non_revertible.public_data_update_requests, - revertible.public_data_update_requests ) } } diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_linked_public_data_writes.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_linked_public_data_writes.nr new file mode 100644 index 000000000000..0f9235318b81 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_linked_public_data_writes.nr @@ -0,0 +1,101 @@ +use dep::types::{ + abis::public_data_write::OverridablePublicDataWrite, data::public_data_hint::PublicDataHint, + traits::Empty, utils::arrays::find_index_hint +}; + +struct LinkedIndexHint { + is_first_write: bool, + prev_index: u32, + next_index: u32, +} + +impl Empty for LinkedIndexHint { + fn empty() -> Self { + LinkedIndexHint { is_first_write: false, prev_index: 0, next_index: 0 } + } +} + +unconstrained fn generate_linked_index_hints( + overridable_public_data_writes: [OverridablePublicDataWrite; NUM_WRITES], + public_leaf_data: [PublicDataHint; NUM_DATA] +) -> [LinkedIndexHint; NUM_WRITES] { + let mut hints = [LinkedIndexHint::empty(); NUM_WRITES]; + + for i in 0..overridable_public_data_writes.len() { + let write = overridable_public_data_writes[i]; + if write.counter() != 0 { + let mut is_first_write = false; + let mut prev_index = 0; + let mut prev_counter = 0; + let mut next_index = 0; + for j in 0..overridable_public_data_writes.len() { + let other = overridable_public_data_writes[j].write; + if (j != i) & (other.leaf_slot == write.inner().leaf_slot) { + if other.counter == write.override_counter { + next_index = j; + } else if ((other.counter < write.counter()) + & ((prev_counter == 0) | (other.counter > prev_counter))) { + prev_counter = other.counter; + prev_index = j; + } + } + } + if prev_counter == 0 { + is_first_write = true; + prev_index = find_index_hint(public_leaf_data, |leaf: PublicDataHint| leaf.leaf_slot == write.inner().leaf_slot); + } + hints[i] = LinkedIndexHint { is_first_write, prev_index, next_index }; + } + } + + hints +} + +fn validate_linked_public_data_writes_with_hints( + overridable_public_data_writes: [OverridablePublicDataWrite; NUM_WRITES], + public_leaf_data: [PublicDataHint; NUM_DATA], + hints: [LinkedIndexHint; NUM_WRITES] +) { + for i in 0..overridable_public_data_writes.len() { + let write = overridable_public_data_writes[i]; + let hint = hints[i]; + if write.counter() != 0 { + if hint.is_first_write { + // It's linked to a leaf in the tree. + let data_hint = public_leaf_data[hint.prev_index]; + assert_eq(data_hint.leaf_slot, write.inner().leaf_slot, "hinted data has different leaf_slot"); + assert_eq( + data_hint.override_counter, write.counter(), "hinted data does not link to current write" + ); + } else { + // It's linked to another write for the same leaf slot. + let prev = overridable_public_data_writes[hint.prev_index]; + assert_eq( + prev.inner().leaf_slot, write.inner().leaf_slot, "hinted write has different leaf_slot" + ); + assert(prev.counter() < write.counter(), "previous write must have a smaller counter"); + assert_eq( + prev.override_counter, write.counter(), "hinted previous write does not link to current write" + ); + } + } + + if write.override_counter != 0 { + let next = overridable_public_data_writes[hint.next_index]; + let next_hint = hints[hint.next_index]; + assert_eq( + write.override_counter, next.counter(), "hinted next write does not link to current write" + ); + assert_eq(next_hint.prev_index, i, "next write has different counter"); + // leaf_slot will be checked when processing the next write. + } + } +} + +pub fn validate_linked_public_data_writes( + overridable_public_data_writes: [OverridablePublicDataWrite; NUM_WRITES], + public_leaf_data: [PublicDataHint; NUM_DATA] +) { + let hints = generate_linked_index_hints(overridable_public_data_writes, public_leaf_data); + validate_linked_public_data_writes_with_hints(overridable_public_data_writes, public_leaf_data, hints); +} diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_hints.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_hints.nr new file mode 100644 index 000000000000..b5035269a243 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_hints.nr @@ -0,0 +1,63 @@ +use dep::types::{ + abis::public_data_read::PublicDataRead, data::public_data_hint::PublicDataHint, + utils::arrays::get_sorted_tuple::get_sorted_tuple +}; + +fn sort_by_leaf_slot(a: PublicDataHint, b: PublicDataHint) -> bool { + (b.leaf_slot == 0) | ((a.leaf_slot != 0) & a.leaf_slot.lt(b.leaf_slot)) +} + +unconstrained fn generate_unique_leaf_slot_hints(public_data_hints: [PublicDataHint; N]) -> ([Field; N], [u32; N]) { + let mut sorted_leaf_slots = [0; N]; + let mut sorted_leaf_slot_indexes = [N - 1; N]; + + let sorted_tuples = get_sorted_tuple(public_data_hints, sort_by_leaf_slot); + for i in 0..N { + let elem = sorted_tuples[i].elem; + sorted_leaf_slots[i] = elem.leaf_slot; + + let original_index = sorted_tuples[i].original_index; + sorted_leaf_slot_indexes[original_index] = i; + } + + (sorted_leaf_slots, sorted_leaf_slot_indexes) +} + +// All non-zero leaf slots in public_data_hints must be unique, ensuring that writes with the same leaf slot are grouped into a single group. +fn validate_unique_leaf_slots( + public_data_hints: [PublicDataHint; N], + sorted_leaf_slots: [Field; N], + sorted_leaf_slot_indexes: [u32; N] +) { + let mut prev_slot = 0; + let mut num_non_zeros = 0; + for i in 0..N { + let data = public_data_hints[i]; + let sorted_index = if data.leaf_slot != 0 { + sorted_leaf_slot_indexes[i] + } else { + num_non_zeros += 1; + N - num_non_zeros + }; + let hinted_leaf_slot = sorted_leaf_slots[sorted_index]; + assert_eq(hinted_leaf_slot, data.leaf_slot); + + let curr_leaf_alot = sorted_leaf_slots[i]; + if data.leaf_slot != 0 { + assert(prev_slot.lt(curr_leaf_alot)); + } else { + assert_eq(curr_leaf_alot, 0); + } + prev_slot = curr_leaf_alot; + } +} + +pub fn validate_public_data_hints(public_data_hints: [PublicDataHint; N], tree_root: Field) { + let (sorted_leaf_slots, sorted_leaf_slot_indexes) = generate_unique_leaf_slot_hints(public_data_hints); + validate_unique_leaf_slots(public_data_hints, sorted_leaf_slots, sorted_leaf_slot_indexes); + + // validate membership + for i in 0..public_data_hints.len() { + public_data_hints[i].validate(tree_root); + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr index 3c95ad18bad4..3702be63a560 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr @@ -6,15 +6,15 @@ use crate::{ }, public_kernel_phase::PublicKernelPhase }; -use dep::reset_kernel_lib::{ - NullifierReadRequestHints, NullifierNonExistentReadRequestHints, PublicDataReadRequestHints, - PublicValidationRequestProcessor -}; +use dep::reset_kernel_lib::{NullifierReadRequestHints, NullifierNonExistentReadRequestHints, PublicValidationRequestProcessor}; use dep::types::{ - abis::{kernel_circuit_public_inputs::KernelCircuitPublicInputs, public_kernel_data::PublicKernelData}, + abis::{ + kernel_circuit_public_inputs::KernelCircuitPublicInputs, public_kernel_data::PublicKernelData, + public_data_write::OverridablePublicDataWrite +}, constants::{ - MAX_PUBLIC_DATA_HINTS, MAX_NULLIFIER_READ_REQUESTS_PER_TX, PUBLIC_KERNEL_SETUP_INDEX, - PUBLIC_KERNEL_APP_LOGIC_INDEX, PUBLIC_KERNEL_TEARDOWN_INDEX + MAX_PUBLIC_DATA_HINTS, MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + PUBLIC_KERNEL_SETUP_INDEX, PUBLIC_KERNEL_APP_LOGIC_INDEX, PUBLIC_KERNEL_TEARDOWN_INDEX }, data::public_data_hint::PublicDataHint, partial_state_reference::PartialStateReference }; @@ -30,13 +30,13 @@ struct PublicKernelTailCircuitPrivateInputs { nullifier_read_request_hints: NullifierReadRequestHints, nullifier_non_existent_read_request_hints: NullifierNonExistentReadRequestHints, public_data_hints: [PublicDataHint; MAX_PUBLIC_DATA_HINTS], - public_data_read_request_hints: PublicDataReadRequestHints, start_state: PartialStateReference, } impl PublicKernelTailCircuitPrivateInputs { - unconstrained fn generate_output(self) -> KernelCircuitPublicInputs { - PublicTailOutputComposer::new(self.previous_kernel.public_inputs, self.start_state).finish() + unconstrained fn generate_output(self) -> (KernelCircuitPublicInputs, [OverridablePublicDataWrite; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]) { + let composer = PublicTailOutputComposer::new(self.previous_kernel.public_inputs, self.start_state); + (composer.finish(), composer.overridable_public_data_writes) } pub fn execute(self) -> KernelCircuitPublicInputs { @@ -44,20 +44,24 @@ impl PublicKernelTailCircuitPrivateInputs { previous_kernel_validator.validate_phase(PublicKernelPhase.TAIL); previous_kernel_validator.validate_proof(ALLOWED_PREVIOUS_CIRCUITS); - let previous_public_inputs = self.previous_kernel.public_inputs; + let (output, overridable_public_data_writes) = self.generate_output(); + PublicValidationRequestProcessor::new( - previous_public_inputs, + self.previous_kernel.public_inputs, self.nullifier_read_request_hints, self.nullifier_non_existent_read_request_hints, self.start_state.nullifier_tree.root, - self.public_data_read_request_hints, - self.public_data_hints, - self.start_state.public_data_tree.root + overridable_public_data_writes, + self.public_data_hints ).validate(); - let output = self.generate_output(); - - PublicTailOutputValidator::new(output, previous_public_inputs, self.start_state).validate(); + PublicTailOutputValidator::new( + output, + self.previous_kernel.public_inputs, + self.start_state, + overridable_public_data_writes, + self.public_data_hints + ).validate(); output } @@ -68,15 +72,15 @@ mod tests { use dep::reset_kernel_lib::{ tests::{ nullifier_non_existent_read_request_hints_builder::NullifierNonExistentReadRequestHintsBuilder, - nullifier_read_request_hints_builder::NullifierReadRequestHintsBuilder, - public_data_read_request_hints_builder::PublicDataReadRequestHintsBuilder + nullifier_read_request_hints_builder::NullifierReadRequestHintsBuilder }, - PublicDataHint, reset::read_request::{PendingReadHint, ReadRequestState, ReadRequestStatus} + reset::read_request::{PendingReadHint, ReadRequestState, ReadRequestStatus} }; use dep::types::{ abis::{ kernel_circuit_public_inputs::KernelCircuitPublicInputs, public_kernel_data::PublicKernelData, - nullifier::ScopedNullifier, nullifier_leaf_preimage::NullifierLeafPreimage + nullifier::ScopedNullifier, nullifier_leaf_preimage::NullifierLeafPreimage, + public_data_update_request::PublicDataUpdateRequest }, address::AztecAddress, constants::{ @@ -88,17 +92,17 @@ mod tests { PUBLIC_KERNEL_APP_LOGIC_INDEX, BASE_ROLLUP_INDEX, PUBLIC_KERNEL_SETUP_INDEX, PUBLIC_KERNEL_TEARDOWN_INDEX }, - hash::{compute_siloed_nullifier, silo_note_hash}, + data::public_data_hint::PublicDataHint, hash::{compute_siloed_nullifier, silo_note_hash}, public_data_tree_leaf_preimage::PublicDataTreeLeafPreimage, tests::{ fixture_builder::FixtureBuilder, merkle_tree_utils::NonEmptyMerkleTree, - utils::{assert_array_eq, swap_items} + utils::{assert_array_eq, pad_end, swap_items} }, traits::is_empty, partial_state_reference::PartialStateReference, - utils::arrays::{array_length, array_merge}, merkle_tree::MembershipWitness + utils::arrays::{array_length, array_merge, find_index_hint}, merkle_tree::MembershipWitness }; - fn build_nullifier_tree() -> NonEmptyMerkleTree { + fn build_nullifier_tree() -> NonEmptyMerkleTree { let mut pre_existing_nullifiers = [NullifierLeafPreimage::empty(); MAX_NULLIFIERS_PER_TX]; pre_existing_nullifiers[0] = NullifierLeafPreimage { nullifier: 0, next_nullifier: 100, next_index: 1 }; pre_existing_nullifiers[1] = NullifierLeafPreimage { nullifier: 100, next_nullifier: 0, next_index: 0 }; @@ -110,37 +114,31 @@ mod tests { ) } - fn get_settled_public_data_leaves() -> [PublicDataTreeLeafPreimage; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] { - let mut settled_public_data_leaves = [PublicDataTreeLeafPreimage::empty(); MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; - settled_public_data_leaves[0] = PublicDataTreeLeafPreimage { slot: 22, value: 200, next_slot: 33, next_index: 1 }; - settled_public_data_leaves[1] = PublicDataTreeLeafPreimage { slot: 33, value: 300, next_slot: 0, next_index: 0 }; - settled_public_data_leaves[2] = PublicDataTreeLeafPreimage { slot: 11, value: 100, next_slot: 22, next_index: 0 }; - settled_public_data_leaves - } - - fn build_public_data_tree() -> NonEmptyMerkleTree { - let settled_public_data_leaves = get_settled_public_data_leaves(); + fn build_public_data_tree(public_data_leaves: [PublicDataTreeLeafPreimage; N]) -> NonEmptyMerkleTree { NonEmptyMerkleTree::new( - settled_public_data_leaves.map(|preimage: PublicDataTreeLeafPreimage| preimage.hash()), + pad_end( + public_data_leaves.map(|preimage: PublicDataTreeLeafPreimage| preimage.hash()), + 0 + ), [0; PUBLIC_DATA_TREE_HEIGHT], [0; PUBLIC_DATA_TREE_HEIGHT - PUBLIC_DATA_SUBTREE_HEIGHT], [0; PUBLIC_DATA_SUBTREE_HEIGHT] ) } - struct PublicKernelTailCircuitPrivateInputsBuilder { + struct PublicKernelTailCircuitPrivateInputsBuilder { previous_kernel: FixtureBuilder, previous_revertible: FixtureBuilder, nullifier_read_request_hints_builder: NullifierReadRequestHintsBuilder, nullifier_non_existent_read_request_hints_builder: NullifierNonExistentReadRequestHintsBuilder, - public_data_read_request_hints_builder: PublicDataReadRequestHintsBuilder, public_data_hints: BoundedVec, public_data_tree: NonEmptyMerkleTree, + public_data_leaves: [PublicDataTreeLeafPreimage; N], start_state: PartialStateReference, } - impl PublicKernelTailCircuitPrivateInputsBuilder { - pub fn new() -> Self { + impl PublicKernelTailCircuitPrivateInputsBuilder { + pub fn new() -> PublicKernelTailCircuitPrivateInputsBuilder<6> { let previous_kernel = FixtureBuilder::new().in_vk_tree(PUBLIC_KERNEL_APP_LOGIC_INDEX); let previous_revertible = FixtureBuilder::new(); let nullifier_non_existent_read_request_hints_builder = NullifierNonExistentReadRequestHintsBuilder::new(); @@ -150,10 +148,10 @@ mod tests { previous_revertible, nullifier_read_request_hints_builder: NullifierReadRequestHintsBuilder::new(), nullifier_non_existent_read_request_hints_builder, - public_data_read_request_hints_builder: PublicDataReadRequestHintsBuilder::new(MAX_PUBLIC_DATA_READS_PER_TX), public_data_hints: BoundedVec::new(), public_data_tree: NonEmptyMerkleTree::empty(), - start_state: PartialStateReference::empty() + start_state: PartialStateReference::empty(), + public_data_leaves: pad_end([], PublicDataTreeLeafPreimage::empty()) } } @@ -167,7 +165,14 @@ mod tests { } pub fn with_public_data_tree(&mut self) -> Self { - let public_data_tree = build_public_data_tree(); + let public_data_leaves = [ + PublicDataTreeLeafPreimage { slot: 0, value: 0, next_slot: 1111, next_index: 3 }, + PublicDataTreeLeafPreimage { slot: 2222, value: 200, next_slot: 3333, next_index: 2 }, + PublicDataTreeLeafPreimage { slot: 3333, value: 300, next_slot: 0, next_index: 0 }, + PublicDataTreeLeafPreimage { slot: 1111, value: 100, next_slot: 2222, next_index: 1 } + ]; + let public_data_tree = build_public_data_tree(public_data_leaves); + self.public_data_leaves = pad_end(public_data_leaves, PublicDataTreeLeafPreimage::empty()); self.public_data_tree = public_data_tree; self.start_state.public_data_tree.root = public_data_tree.get_root(); *self @@ -228,35 +233,60 @@ mod tests { self.nullifier_non_existent_read_request_hints_builder.add_value_read(siloed_nullifier); } - pub fn add_public_data_hint_for_settled_public_data(&mut self, leaf_index: u32) { - let leaf_preimage = get_settled_public_data_leaves()[leaf_index]; - let membership_witness = MembershipWitness { leaf_index: leaf_index as Field, sibling_path: self.public_data_tree.get_sibling_path(leaf_index) }; - let hint = PublicDataHint { - leaf_slot: leaf_preimage.slot, - value: leaf_preimage.value, - override_counter: 0, - membership_witness, - leaf_preimage - }; - self.public_data_hints.push(hint); + pub fn add_public_data_write(&mut self, leaf_slot: Field, value: Field) { + let counter = self.previous_kernel.counter; + self.previous_kernel.add_public_data_update_request(leaf_slot, value); + self.add_public_data_hint(leaf_slot, counter); } - pub fn add_public_data_hint_for_non_existent_public_data(&mut self, leaf_slot: Field, low_leaf_index: u32) { - let leaf_preimage = get_settled_public_data_leaves()[low_leaf_index]; - let membership_witness = MembershipWitness { - leaf_index: low_leaf_index as Field, - sibling_path: self.public_data_tree.get_sibling_path(low_leaf_index) - }; - let hint = PublicDataHint { leaf_slot, value: 0, override_counter: 0, membership_witness, leaf_preimage }; - self.public_data_hints.push(hint); + pub fn read_public_write(&mut self, leaf_slot: Field, value: Field) { + self.previous_kernel.add_public_data_read_request(leaf_slot, value); + self.add_public_data_hint(leaf_slot, 0); } - pub fn add_pending_public_data_read_request(&mut self, public_date_update_request_index: u32) { - let read_request_index = self.previous_kernel.add_read_request_for_pending_public_data(public_date_update_request_index); - let hint_index = self.public_data_read_request_hints_builder.pending_read_hints.len(); - let hint = PendingReadHint { read_request_index, pending_value_index: public_date_update_request_index }; - self.public_data_read_request_hints_builder.pending_read_hints.push(hint); - self.public_data_read_request_hints_builder.read_request_statuses[read_request_index] = ReadRequestStatus { state: ReadRequestState.PENDING, hint_index }; + pub fn add_read_request_for_empty_slot(&mut self, leaf_slot: Field) { + self.previous_kernel.add_public_data_read_request(leaf_slot, 0); + self.add_public_data_hint(leaf_slot, 0); + } + + fn add_public_data_hint(&mut self, leaf_slot: Field, override_counter: u32) { + let existing_index = find_index_hint( + self.public_data_hints.storage, + |d: PublicDataHint| d.leaf_slot == leaf_slot + ); + if existing_index != self.public_data_hints.storage.len() { + let data = self.public_data_hints.storage[existing_index]; + if data.override_counter == 0 { + self.public_data_hints.storage[existing_index].override_counter = override_counter; + } + } else { + let mut low_leaf_index = 0; + let mut low_leaf_slot = 0; + for i in 0..self.public_data_leaves.len() { + let leaf = self.public_data_leaves[i]; + if !leaf_slot.lt(leaf.slot) & low_leaf_slot.lt(leaf.slot) { + low_leaf_index = i; + low_leaf_slot = leaf.slot; + } + } + + let leaf_preimage = self.public_data_leaves[low_leaf_index]; + let membership_witness = MembershipWitness { + leaf_index: low_leaf_index as Field, + sibling_path: self.public_data_tree.get_sibling_path(low_leaf_index) + }; + + let exists_in_tree = low_leaf_slot == leaf_slot; + let read_value = if exists_in_tree { + leaf_preimage.value + } else { + 0 + }; + + self.public_data_hints.push( + PublicDataHint { leaf_slot, value: read_value, override_counter, membership_witness, leaf_preimage } + ); + } } fn sync_counters(&mut self) { @@ -280,7 +310,6 @@ mod tests { nullifier_read_request_hints: self.nullifier_read_request_hints_builder.to_hints(), nullifier_non_existent_read_request_hints: self.nullifier_non_existent_read_request_hints_builder.to_hints(), public_data_hints: self.public_data_hints.storage, - public_data_read_request_hints: self.public_data_read_request_hints_builder.to_hints(), start_state: self.start_state }; @@ -304,7 +333,7 @@ mod tests { } #[test] - fn measuring_of_log_lengths() { + unconstrained fn measuring_of_log_lengths() { let mut builder = PublicKernelTailCircuitPrivateInputsBuilder::new(); // Logs for the previous call stack. let prev_encrypted_logs_hash = 80; @@ -418,90 +447,53 @@ mod tests { } #[test] - unconstrained fn validate_public_data_hints() { - let mut builder = PublicKernelTailCircuitPrivateInputsBuilder::new().with_public_data_tree(); - - builder.add_public_data_hint_for_settled_public_data(1); - builder.add_public_data_hint_for_settled_public_data(0); - builder.add_public_data_hint_for_settled_public_data(2); - - builder.succeeded(); - } - - #[test(should_fail_with="Hinted public data value does not match the value in leaf preimage")] - unconstrained fn validate_public_data_hints_failed_mismatch_value() { - let mut builder = PublicKernelTailCircuitPrivateInputsBuilder::new().with_public_data_tree(); - - builder.add_public_data_hint_for_settled_public_data(1); - - let mut hint = builder.public_data_hints.pop(); - hint.value += 1; - builder.public_data_hints.push(hint); - - builder.failed(); - } - - #[test] - unconstrained fn validate_public_data_hints_uninitialized_value() { + unconstrained fn public_data_reads_and_writes_succeeds() { let mut builder = PublicKernelTailCircuitPrivateInputsBuilder::new().with_public_data_tree(); - builder.add_public_data_hint_for_non_existent_public_data(25, 0); + builder.read_public_write(22, 0); - builder.succeeded(); - } + builder.add_public_data_write(11, 500); + builder.add_public_data_write(22, 700); - #[test(should_fail_with="Value must be 0 for non-existent public data")] - unconstrained fn validate_public_data_hints_failed_non_zero_uninitialized_value() { - let mut builder = PublicKernelTailCircuitPrivateInputsBuilder::new().with_public_data_tree(); + builder.read_public_write(22, 700); + builder.read_public_write(11, 500); + builder.read_public_write(3333, 300); - builder.add_public_data_hint_for_non_existent_public_data(25, 0); + // Override the previous value at leaf slot 22. + builder.add_public_data_write(22, 701); - let mut hint = builder.public_data_hints.pop(); - hint.value = 1; - builder.public_data_hints.push(hint); + // Override the value of the leaf. + builder.add_public_data_write(3333, 301); - builder.failed(); - } - - #[test] - unconstrained fn pending_public_data_read_requests() { - let mut builder = PublicKernelTailCircuitPrivateInputsBuilder::new(); + // Read the new values. + builder.read_public_write(22, 701); + builder.read_public_write(3333, 301); - builder.previous_kernel.append_public_data_update_requests(3); + let prev_writes = builder.previous_kernel.public_data_update_requests.storage; - builder.add_pending_public_data_read_request(1); - builder.add_pending_public_data_read_request(0); - builder.add_pending_public_data_read_request(2); + // Shuffle the items so that they are not sorted by counter. + swap_items(&mut builder.previous_kernel.public_data_update_requests, 0, 3); + swap_items(&mut builder.previous_kernel.public_data_update_requests, 1, 3); + swap_items(&mut builder.previous_kernel.public_data_reads, 1, 4); + swap_items(&mut builder.previous_kernel.public_data_reads, 0, 3); - builder.succeeded(); - } - - #[test(should_fail_with="Hinted slot of data write does not match read request")] - unconstrained fn pending_public_data_read_requests_failed_wrong_write_index() { - let mut builder = PublicKernelTailCircuitPrivateInputsBuilder::new(); - - builder.previous_kernel.append_public_data_update_requests(2); - - builder.add_pending_public_data_read_request(1); - - let mut hint = builder.public_data_read_request_hints_builder.pending_read_hints.pop(); - hint.pending_value_index += 1; - builder.public_data_read_request_hints_builder.pending_read_hints.push(hint); + let public_inputs = builder.execute(); - builder.failed(); + assert_array_eq( + public_inputs.end.public_data_update_requests, + [ + PublicDataUpdateRequest { leaf_slot: 3333, new_value: 301, counter: prev_writes[4].counter }, + PublicDataUpdateRequest { leaf_slot: 11, new_value: 500, counter: prev_writes[1].counter }, + PublicDataUpdateRequest { leaf_slot: 22, new_value: 701, counter: prev_writes[3].counter } + ] + ); } - #[test(should_fail_with="Hinted value of data write does not match read request")] - unconstrained fn pending_public_data_read_requests_failed_wrong_write_value() { - let mut builder = PublicKernelTailCircuitPrivateInputsBuilder::new(); - - builder.previous_kernel.append_public_data_update_requests(1); - - builder.add_pending_public_data_read_request(0); + #[test(should_fail_with="value in PublicDataHint does not match read request")] + unconstrained fn reading_uninitialized_public_data_non_zero_value_fails() { + let mut builder = PublicKernelTailCircuitPrivateInputsBuilder::new().with_public_data_tree(); - let mut public_data_write = builder.previous_kernel.public_data_update_requests.pop(); - public_data_write.new_value += 1; - builder.previous_kernel.public_data_update_requests.push(public_data_write); + builder.read_public_write(1234, 1); builder.failed(); } diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/lib.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/lib.nr index c67fa479d22d..6c217af4fa74 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/lib.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/lib.nr @@ -2,19 +2,17 @@ use note_hash_read_request_reset::NoteHashReadRequestHints; use nullifier_non_existent_read_request_reset::NullifierNonExistentReadRequestHints; use nullifier_read_request_reset::NullifierReadRequestHints; use private_validation_request_processor::PrivateValidationRequestProcessor; -use public_data_read_request_reset::PublicDataReadRequestHints; use public_validation_request_processor::PublicValidationRequestProcessor; use reset::{ key_validation_hint::KeyValidationHint, transient_data::{TransientDataIndexHint, verify_squashed_transient_data} }; -use dep::types::data::public_data_hint::PublicDataHint; mod note_hash_read_request_reset; mod nullifier_non_existent_read_request_reset; mod nullifier_read_request_reset; mod private_validation_request_processor; -mod public_data_read_request_reset; +mod public_data_read_request_hints; mod public_validation_request_processor; mod reset; mod tests; diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_data_read_request_hints.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_data_read_request_hints.nr new file mode 100644 index 000000000000..c9dbbd36276c --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_data_read_request_hints.nr @@ -0,0 +1,47 @@ +use crate::reset::{mutable_data_read_request::ReadIndexHint, read_request::ReadRequestStatus}; +use dep::types::{ + abis::{public_data_read::PublicDataRead, public_data_write::OverridablePublicDataWrite}, + data::public_data_hint::PublicDataHint, utils::arrays::find_index_hint +}; + +struct PublicDataReadRequestHints { + read_request_statuses: [ReadRequestStatus; NUM_READS], + pending_read_hints: [ReadIndexHint; NUM_READS], + leaf_data_read_hints: [ReadIndexHint; NUM_READS], +} + +unconstrained pub fn build_public_data_read_request_hints( + reads: [PublicDataRead; NUM_READS], + writes: [OverridablePublicDataWrite; NUM_WRITES], + leaf_data: [PublicDataHint; NUM_PUBLIC_DATA_HINTS] +) -> PublicDataReadRequestHints { + let mut read_request_statuses = [ReadRequestStatus::empty(); NUM_READS]; + let mut pending_read_hints = [ReadIndexHint::nada(NUM_READS); NUM_READS]; + let mut leaf_data_read_hints = [ReadIndexHint::nada(NUM_READS); NUM_READS]; + let mut num_pending_reads = 0; + let mut num_leaf_data_reads = 0; + for i in 0..reads.len() { + let read = reads[i]; + if read.counter != 0 { + let write_index = find_index_hint( + writes, + |w: OverridablePublicDataWrite| (w.inner().leaf_slot == read.leaf_slot) & (read.counter > w.counter()) & ((read.counter < w.override_counter) | (w.override_counter == 0)) + ); + if write_index != writes.len() { + pending_read_hints[num_pending_reads] = ReadIndexHint { read_request_index: i, value_index: write_index }; + read_request_statuses[i] = ReadRequestStatus::pending(num_pending_reads); + num_pending_reads += 1; + } else { + let leaf_data_index = find_index_hint(leaf_data, |d: PublicDataHint| d.leaf_slot == read.leaf_slot); + assert( + leaf_data_index != leaf_data.len(), "cannot find a public data leaf or a pending write for the read request" + ); + leaf_data_read_hints[num_leaf_data_reads] = ReadIndexHint { read_request_index: i, value_index: leaf_data_index }; + read_request_statuses[i] = ReadRequestStatus::settled(num_leaf_data_reads); + num_leaf_data_reads += 1; + } + } + } + + PublicDataReadRequestHints { read_request_statuses, pending_read_hints, leaf_data_read_hints } +} diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_data_read_request_reset.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_data_read_request_reset.nr deleted file mode 100644 index 7c6b66340742..000000000000 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_data_read_request_reset.nr +++ /dev/null @@ -1,9 +0,0 @@ -use crate::reset::{mutable_data_read_request::LeafDataReadHint, read_request::{PendingReadHint, ReadRequestStatus}}; -use dep::types::constants::MAX_PUBLIC_DATA_READS_PER_TX; - -// The MAX_PUBLIC_DATA_READS_PER_TX for pending_read_hints and leaf_data_read_hints can change if we create various circuits that deal with different number of reads. -struct PublicDataReadRequestHints { - read_request_statuses: [ReadRequestStatus; MAX_PUBLIC_DATA_READS_PER_TX], - pending_read_hints: [PendingReadHint; MAX_PUBLIC_DATA_READS_PER_TX], - leaf_data_read_hints: [LeafDataReadHint; MAX_PUBLIC_DATA_READS_PER_TX], -} diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_validation_request_processor.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_validation_request_processor.nr index 0e069fdd86f1..74da0d2299f9 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_validation_request_processor.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_validation_request_processor.nr @@ -6,62 +6,62 @@ use crate::{ }, nullifier_read_request_reset::NullifierReadRequestHints, nullifier_non_existent_read_request_reset::NullifierNonExistentReadRequestHints, - public_data_read_request_reset::PublicDataReadRequestHints + public_data_read_request_hints::{build_public_data_read_request_hints, PublicDataReadRequestHints} }; use dep::types::{ abis::{ kernel_circuit_public_inputs::PublicKernelCircuitPublicInputs, nullifier::Nullifier, - public_data_update_request::PublicDataUpdateRequest, read_request::ScopedReadRequest, + public_data_write::OverridablePublicDataWrite, read_request::ScopedReadRequest, validation_requests::PublicValidationRequests }, data::public_data_hint::PublicDataHint, - constants::{MAX_NULLIFIERS_PER_TX, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX}, + constants::{MAX_NULLIFIERS_PER_TX, MAX_PUBLIC_DATA_READS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX}, hash::compute_siloed_nullifier, traits::is_empty, utils::arrays::{array_merge, array_to_bounded_vec, assert_sorted_array} }; -struct PublicValidationRequestProcessor { +struct PublicValidationRequestProcessor { validation_requests: PublicValidationRequests, pending_nullifiers: [Nullifier; MAX_NULLIFIERS_PER_TX], - pending_public_data_writes: [PublicDataUpdateRequest; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], nullifier_read_request_hints: NullifierReadRequestHints, nullifier_non_existent_read_request_hints: NullifierNonExistentReadRequestHints, nullifier_tree_root: Field, - public_data_read_request_hints: PublicDataReadRequestHints, + pending_public_data_writes: [OverridablePublicDataWrite; NUN_PUBLIC_DATA_WRITES], + public_data_read_request_hints: PublicDataReadRequestHints, public_data_hints: [PublicDataHint; NUM_PUBLIC_DATA_HINTS], - public_data_tree_root: Field, } -impl PublicValidationRequestProcessor { +impl PublicValidationRequestProcessor { pub fn new( public_inputs: PublicKernelCircuitPublicInputs, nullifier_read_request_hints: NullifierReadRequestHints, nullifier_non_existent_read_request_hints: NullifierNonExistentReadRequestHints, nullifier_tree_root: Field, - public_data_read_request_hints: PublicDataReadRequestHints, - public_data_hints: [PublicDataHint; NUM_PUBLIC_DATA_HINTS], - public_data_tree_root: Field + pending_public_data_writes: [OverridablePublicDataWrite; NUN_PUBLIC_DATA_WRITES], + public_data_hints: [PublicDataHint; NUM_PUBLIC_DATA_HINTS] ) -> Self { + let validation_requests = public_inputs.validation_requests; + let end_non_revertible = public_inputs.end_non_revertible; let end = public_inputs.end; let pending_nullifiers = array_merge(end_non_revertible.nullifiers, end.nullifiers); - let pending_public_data_writes = array_merge( - end_non_revertible.public_data_update_requests, - end.public_data_update_requests + let public_data_read_request_hints = build_public_data_read_request_hints( + validation_requests.public_data_reads, + pending_public_data_writes, + public_data_hints ); PublicValidationRequestProcessor { - validation_requests: public_inputs.validation_requests, + validation_requests, pending_nullifiers, - pending_public_data_writes, nullifier_read_request_hints, nullifier_non_existent_read_request_hints, nullifier_tree_root, + pending_public_data_writes, public_data_read_request_hints, - public_data_hints, - public_data_tree_root + public_data_hints } } @@ -117,9 +117,8 @@ impl PublicValidationRequestProcessor PublicValidationRequestProcessor Self { - LeafDataReadHint { read_request_index: read_request_len, data_hint_index: 0 } + ReadIndexHint { read_request_index: read_request_len, value_index: 0 } } } -fn validate_pending_read_requests( - read_requests: [PublicDataRead; READ_REQUEST_LEN], - data_writes: [PublicDataUpdateRequest; PENDING_VALUE_LEN], - hints: [PendingReadHint; NUM_PENDING_READS] -) { - for i in 0..NUM_PENDING_READS { - let read_request_index = hints[i].read_request_index; +fn validate_pending_read_requests( + read_requests: [R; READ_REQUEST_LEN], + pending_values: [V; PENDING_VALUE_LEN], + index_hints: [ReadIndexHint; NUM_HINTS] +) where R: Ordered, V: Readable + Ordered + Overridable { + for i in 0..index_hints.len() { + let index_hint = index_hints[i]; + let read_request_index = index_hint.read_request_index; if read_request_index != READ_REQUEST_LEN { let read_request = read_requests[read_request_index]; - let pending_value = data_writes[hints[i].pending_value_index]; + let pending_value = pending_values[index_hint.value_index]; + pending_value.assert_match_read_request(read_request); assert( - read_request.leaf_slot.eq(pending_value.leaf_slot), "Hinted slot of data write does not match read request" + read_request.counter() > pending_value.counter(), "Read request counter must be greater than the counter of the data write" ); assert( - read_request.value.eq(pending_value.new_value), "Hinted value of data write does not match read request" + (read_request.counter() < pending_value.override_counter()) + | (pending_value.override_counter() == 0), "Read request counter must be less than the counter of the next data write" ); - assert( - read_request.counter > pending_value.counter, "Read request counter must be greater than the counter of the data write" - ); - // TODO - // assert((read_request.counter < pending_value.next_counter) | (pending_value.next_counter == 0), "Read request counter must be less than the counter of the next data write"); } } } -fn validate_leaf_data_read_requests( - read_requests: [PublicDataRead; READ_REQUEST_LEN], - leaf_data_hints: [H; NUM_LEAF_DATA_HINTS], - hints: [LeafDataReadHint; NUM_LEAF_DATA_READS] -) where H: LeafDataHint { - for i in 0..NUM_LEAF_DATA_READS { - let read_request_index = hints[i].read_request_index; +fn validate_leaf_data_read_requests( + read_requests: [R; READ_REQUEST_LEN], + leaf_data: [L; LEAF_DATA_LEN], + index_hints: [ReadIndexHint; NUM_HINTS] +) where R: Ordered, L: Readable + Overridable { + for i in 0..index_hints.len() { + let index_hint = index_hints[i]; + let read_request_index = index_hint.read_request_index; if read_request_index != READ_REQUEST_LEN { let read_request = read_requests[read_request_index]; - let data_hint = leaf_data_hints[hints[i].data_hint_index]; + let data = leaf_data[index_hint.value_index]; + data.assert_match_read_request(read_request); assert( - read_request.leaf_slot == data_hint.leaf_slot(), "Hinted slot does not match read request" - ); - assert(read_request.value == data_hint.value(), "Hinted value does not match read request"); - assert( - (read_request.counter < data_hint.override_counter()) - | (data_hint.override_counter() == 0), "Hinted leaf is overridden before the read request" + (read_request.counter() < data.override_counter()) | (data.override_counter() == 0), "Hinted leaf is overridden before the read request" ); } } } -fn ensure_all_read_requests_are_verified( - read_requests: [PublicDataRead; READ_REQUEST_LEN], +fn ensure_all_read_requests_are_verified( + read_requests: [R; READ_REQUEST_LEN], read_request_statuses: [ReadRequestStatus; READ_REQUEST_LEN], - pending_read_hints: [PendingReadHint; NUM_PENDING_READS], - leaf_data_read_hints: [LeafDataReadHint; NUM_LEAF_DATA_READS] -) { + pending_read_hints: [ReadIndexHint; NUM_PENDING_READS], + leaf_data_read_hints: [ReadIndexHint; NUM_LEAF_DATA_READS] +) where R: Ordered { for i in 0..READ_REQUEST_LEN { let read_request = read_requests[i]; - if !is_empty(read_request) { + if read_request.counter() != 0 { let status = read_request_statuses[i]; if status.state == ReadRequestState.PENDING { assert( pending_read_hints[status.hint_index].read_request_index == i, "Hinted pending read request does not match status" ); - } else if status.state == ReadRequestState.SETTLED { + } else { assert( leaf_data_read_hints[status.hint_index].read_request_index == i, "Hinted settled read request does not match status" ); - } else { - assert(false, "Read request status must be PENDING or SETTLED"); } } } } pub fn reset_mutable_data_read_requests< + R, let READ_REQUEST_LEN: u32, + V, let PENDING_VALUE_LEN: u32, - H, + L, let NUM_LEAF_DATA_HINTS: u32, let NUM_PENDING_READS: u32, let NUM_LEAF_DATA_READS: u32 >( - read_requests: [PublicDataRead; READ_REQUEST_LEN], + read_requests: [R; READ_REQUEST_LEN], read_request_statuses: [ReadRequestStatus; READ_REQUEST_LEN], - data_writes: [PublicDataUpdateRequest; PENDING_VALUE_LEN], - leaf_data_hints: [H; NUM_LEAF_DATA_HINTS], - pending_read_hints: [PendingReadHint; NUM_PENDING_READS], - leaf_data_read_hints: [LeafDataReadHint; NUM_LEAF_DATA_READS] -) where H: LeafDataHint { - validate_pending_read_requests(read_requests, data_writes, pending_read_hints); + pending_values: [V; PENDING_VALUE_LEN], + leaf_data: [L; NUM_LEAF_DATA_HINTS], + pending_read_hints: [ReadIndexHint; NUM_PENDING_READS], + leaf_data_read_hints: [ReadIndexHint; NUM_LEAF_DATA_READS] +) where R: Ordered, V: Readable + Ordered + Overridable, L: Readable + Overridable { + validate_pending_read_requests(read_requests, pending_values, pending_read_hints); - validate_leaf_data_read_requests(read_requests, leaf_data_hints, leaf_data_read_hints); + validate_leaf_data_read_requests(read_requests, leaf_data, leaf_data_read_hints); ensure_all_read_requests_are_verified( read_requests, @@ -117,239 +109,369 @@ pub fn reset_mutable_data_read_requests< mod tests { use crate::reset::{ mutable_data_read_request::{ - ensure_all_read_requests_are_verified, reset_mutable_data_read_requests, LeafDataReadHint, + ensure_all_read_requests_are_verified, ReadIndexHint, reset_mutable_data_read_requests, validate_pending_read_requests, validate_leaf_data_read_requests }, - read_request::{PendingReadHint, ReadRequestState, ReadRequestStatus} + read_request::{ReadRequestState, ReadRequestStatus} }; use dep::types::{ - abis::{public_data_read::PublicDataRead, public_data_update_request::PublicDataUpdateRequest}, - data::leaf_data_hint::LeafDataHint + abis::{ + public_data_read::PublicDataRead, public_data_update_request::PublicDataUpdateRequest, + public_data_write::OverridablePublicDataWrite, side_effect::{Overridable, Readable} + }, + tests::utils::pad_end, traits::Empty }; - struct TestLeafDataHint { - leaf_slot: Field, + struct TestLeafData { + leaf_index: Field, value: Field, override_counter: u32, } - impl LeafDataHint for TestLeafDataHint { - fn leaf_slot(self) -> Field { - self.leaf_slot - } - - fn value(self) -> Field { - self.value + impl Empty for TestLeafData { + fn empty() -> Self { + TestLeafData { leaf_index: 0, value: 0, override_counter: 0 } } + } + impl Overridable for TestLeafData { fn override_counter(self) -> u32 { self.override_counter } } - global data_writes = [ - PublicDataUpdateRequest { leaf_slot: 22, new_value: 200, counter: 10 }, - PublicDataUpdateRequest { leaf_slot: 11, new_value: 100, counter: 20 }, - PublicDataUpdateRequest { leaf_slot: 33, new_value: 300, counter: 30 }, - PublicDataUpdateRequest { leaf_slot: 44, new_value: 400, counter: 40 } - ]; - - global leaf_data_hints = [ - TestLeafDataHint { leaf_slot: 7, value: 70, override_counter: 0 }, - TestLeafDataHint { leaf_slot: 6, value: 60, override_counter: 90 }, - TestLeafDataHint { leaf_slot: 5, value: 50, override_counter: 0 }, - ]; - - fn create_pending_read_requests(data_write_indices: [u32; N]) -> ([PublicDataRead; N], [PendingReadHint; N]) { - let mut read_requests = BoundedVec::new(); - let mut hints = BoundedVec::new(); - let last_counter = 1 + data_writes[data_writes.len() - 1].counter(); - for i in 0..N { - let pending_value_index = data_write_indices[i]; - read_requests.push( - PublicDataRead { - leaf_slot: data_writes[pending_value_index].leaf_slot, - value: data_writes[pending_value_index].new_value, - counter: last_counter + i - } - ); - hints.push(PendingReadHint { read_request_index: i, pending_value_index }); + impl Readable for TestLeafData { + fn assert_match_read_request(self, read_request: PublicDataRead) { + assert_eq(self.leaf_index, read_request.leaf_slot, "leaf_index in TestLeafData does not match"); + assert_eq(self.value, read_request.value, "value in TestLeafData does not match"); } - (read_requests.storage, hints.storage) } - fn create_leaf_data_read_requests(data_hint_indices: [u32; N]) -> ([PublicDataRead; N], [LeafDataReadHint; N]) { - let mut read_requests = BoundedVec::new(); - let mut hints = BoundedVec::new(); - for i in 0..N { - let data_hint_index = data_hint_indices[i]; - read_requests.push( - PublicDataRead { - leaf_slot: leaf_data_hints[data_hint_index].leaf_slot, - value: leaf_data_hints[data_hint_index].value, - counter: 80 + i + global READ_REQUEST_LEN = 10; + + struct TestBuilder< + let PENDING_VALUE_LEN: u32, + let NUM_LEAF_DATA: u32, + let NUM_PENDING_READS: u32, + let NUM_LEAF_DATA_READS: u32 + >{ + read_requests: [PublicDataRead; READ_REQUEST_LEN], + read_request_statuses: [ReadRequestStatus; READ_REQUEST_LEN], + data_writes: [OverridablePublicDataWrite; PENDING_VALUE_LEN], + leaf_data: [TestLeafData; NUM_LEAF_DATA], + pending_read_hints: [ReadIndexHint; NUM_PENDING_READS], + leaf_data_read_hints: [ReadIndexHint; NUM_LEAF_DATA_READS], + num_pending_reads: u32, + num_leaf_data_reads: u32, + counter: u32, + } + + impl< + let PENDING_VALUE_LEN: u32, + let NUM_LEAF_DATA_HINTS: u32, + let NUM_PENDING_READS: u32, + let NUM_LEAF_DATA_READS: u32 + > TestBuilder { + pub fn new() -> TestBuilder<6, 12, 5, 4> { + let read_requests = [PublicDataRead::empty(); 10]; + let read_request_statuses = [ReadRequestStatus::empty(); 10]; + + let leaf_data = pad_end( + [ + TestLeafData { leaf_index: 44, value: 0, override_counter: 40 }, + TestLeafData { leaf_index: 77, value: 700, override_counter: 0 }, + TestLeafData { leaf_index: 11, value: 0, override_counter: 20 }, + TestLeafData { leaf_index: 33, value: 300, override_counter: 30 }, + TestLeafData { leaf_index: 66, value: 600, override_counter: 0 }, + TestLeafData { leaf_index: 22, value: 200, override_counter: 10 }, + TestLeafData { leaf_index: 55, value: 500, override_counter: 0 } + ], + TestLeafData::empty() + ); + + let data_writes = pad_end( + [ + OverridablePublicDataWrite { write: PublicDataUpdateRequest { leaf_slot: 22, new_value: 201, counter: 10 }, override_counter: 40 }, + OverridablePublicDataWrite { write: PublicDataUpdateRequest { leaf_slot: 11, new_value: 100, counter: 20 }, override_counter: 0 }, + OverridablePublicDataWrite { write: PublicDataUpdateRequest { leaf_slot: 33, new_value: 301, counter: 30 }, override_counter: 0 }, + OverridablePublicDataWrite { write: PublicDataUpdateRequest { leaf_slot: 22, new_value: 202, counter: 40 }, override_counter: 0 } + ], + OverridablePublicDataWrite::empty() + ); + + let pending_read_hints = pad_end([], ReadIndexHint::nada(READ_REQUEST_LEN)); + + let leaf_data_read_hints = pad_end([], ReadIndexHint::nada(READ_REQUEST_LEN)); + + TestBuilder { + read_requests, + read_request_statuses, + leaf_data, + data_writes, + pending_read_hints, + leaf_data_read_hints, + num_pending_reads: 0, + num_leaf_data_reads: 0, + counter: 50 } + } + + pub fn add_pending_read(&mut self, data_write_index: u32) { + let write = self.data_writes[data_write_index].write; + let read_request_index = self.num_pending_reads + self.num_leaf_data_reads; + self.read_requests[read_request_index] = PublicDataRead { + leaf_slot: write.leaf_slot, + value: write.new_value, + counter: self.counter + }; + self.pending_read_hints[self.num_pending_reads] = ReadIndexHint { read_request_index, value_index: data_write_index }; + self.read_request_statuses[read_request_index] = ReadRequestStatus::pending(self.num_pending_reads); + self.num_pending_reads += 1; + self.counter += 1; + } + + pub fn add_leaf_data_read(&mut self, data_hint_index: u32) { + let data_hint = self.leaf_data[data_hint_index]; + let read_request_index = self.num_pending_reads + self.num_leaf_data_reads; + self.read_requests[read_request_index] = PublicDataRead { + leaf_slot: data_hint.leaf_index, + value: data_hint.value, + counter: self.counter + }; + self.leaf_data_read_hints[self.num_leaf_data_reads] = ReadIndexHint { read_request_index, value_index: data_hint_index }; + self.read_request_statuses[read_request_index] = ReadRequestStatus::settled(self.num_leaf_data_reads); + self.num_leaf_data_reads += 1; + self.counter += 1; + } + + pub fn validate_pending_read_requests(self) { + validate_pending_read_requests(self.read_requests, self.data_writes, self.pending_read_hints); + } + + pub fn validate_leaf_data_read_requests(self) { + validate_leaf_data_read_requests(self.read_requests, self.leaf_data, self.leaf_data_read_hints) + } + + pub fn ensure_all_read_requests_are_verified(self) { + ensure_all_read_requests_are_verified( + self.read_requests, + self.read_request_statuses, + self.pending_read_hints, + self.leaf_data_read_hints + ) + } + + pub fn reset(self) { + reset_mutable_data_read_requests( + self.read_requests, + self.read_request_statuses, + self.data_writes, + self.leaf_data, + self.pending_read_hints, + self.leaf_data_read_hints ); - hints.push(LeafDataReadHint { read_request_index: i, data_hint_index }); } - (read_requests.storage, hints.storage) } #[test] fn reset_pending_reads_succeeds() { - let (read_requests, hints) = create_pending_read_requests([2, 0, 1, 3]); - validate_pending_read_requests(read_requests, data_writes, hints); + let mut builder = TestBuilder::new(); + + builder.add_pending_read(1); + builder.add_pending_read(2); + builder.add_pending_read(3); + + builder.validate_pending_read_requests(); } #[test] fn reset_pending_reads_repeated_values() { - let (read_requests, hints) = create_pending_read_requests([1, 0, 0, 1]); - validate_pending_read_requests(read_requests, data_writes, hints); + let mut builder = TestBuilder::new(); + + builder.add_pending_read(2); + builder.add_pending_read(2); + builder.add_pending_read(2); + + builder.validate_pending_read_requests(); + } + + #[test(should_fail_with="Read request counter must be less than the counter of the next data write")] + fn reset_pending_reads_overriden_value_fails() { + let mut builder = TestBuilder::new(); + + // 0th write is overriden by the 3rd write. + builder.add_pending_read(0); + + builder.validate_pending_read_requests(); } #[test] - fn reset_pending_reads_skips_nada() { - let read_requests = [PublicDataRead { leaf_slot: 88, value: 9999, counter: 2 }]; - let hints = [PendingReadHint::nada(1)]; - validate_pending_read_requests(read_requests, data_writes, hints); + fn reset_pending_reads_overriden_value_before_next_succeeds() { + let mut builder = TestBuilder::new(); + + // 0th write is overriden by the 3rd write. + builder.add_pending_read(0); + + // Tweak the counter of the read request to be before the next value. + builder.read_requests[0].counter = builder.data_writes[3].write.counter - 1; + + builder.validate_pending_read_requests(); } - #[test(should_fail_with="Hinted slot of data write does not match read request")] + #[test(should_fail_with="leaf_slot in PublicDataUpdateRequest does not match read request")] fn reset_pending_reads_wrong_slot_fails() { - let mut (read_requests, hints) = create_pending_read_requests([1]); - hints[0].pending_value_index = 0; - validate_pending_read_requests(read_requests, data_writes, hints); + let mut builder = TestBuilder::new(); + + builder.add_pending_read(2); + builder.read_requests[0].leaf_slot += 1; + + builder.validate_pending_read_requests(); } - #[test(should_fail_with="Hinted value of data write does not match read request")] + #[test(should_fail_with="value in PublicDataUpdateRequest does not match read request")] fn reset_pending_reads_wrong_value_fails() { - let mut (read_requests, hints) = create_pending_read_requests([1]); - read_requests[0].value += 1; - validate_pending_read_requests(read_requests, data_writes, hints); + let mut builder = TestBuilder::new(); + + builder.add_pending_read(2); + builder.read_requests[0].value += 1; + + builder.validate_pending_read_requests(); } #[test(should_fail_with="Read request counter must be greater than the counter of the data write")] fn reset_pending_reads_value_write_after_fails() { - let mut (read_requests, hints) = create_pending_read_requests([1]); - read_requests[0].counter = data_writes[1].counter - 1; - validate_pending_read_requests(read_requests, data_writes, hints); + let mut builder = TestBuilder::new(); + + builder.add_pending_read(2); + builder.read_requests[0].counter = builder.data_writes[2].write.counter - 1; + + builder.validate_pending_read_requests(); } #[test] fn reset_leaf_data_reads_succeeds() { - let (read_requests, hints) = create_leaf_data_read_requests([2, 1, 0]); - validate_leaf_data_read_requests(read_requests, leaf_data_hints, hints); + let mut builder = TestBuilder::new(); + + builder.add_leaf_data_read(1); + builder.add_leaf_data_read(4); + builder.add_leaf_data_read(6); + + builder.validate_leaf_data_read_requests(); } #[test] fn reset_leaf_data_reads_repeated_values() { - let (read_requests, hints) = create_leaf_data_read_requests([1, 0, 1, 0]); - validate_leaf_data_read_requests(read_requests, leaf_data_hints, hints); + let mut builder = TestBuilder::new(); + + builder.add_leaf_data_read(4); + builder.add_leaf_data_read(4); + builder.add_leaf_data_read(4); + + builder.validate_leaf_data_read_requests(); + } + + #[test(should_fail_with="Hinted leaf is overridden before the read request")] + fn reset_leaf_data_reads_overriden_value_fails() { + let mut builder = TestBuilder::new(); + + // 2nd leaf is overriden by a pending write. + builder.add_leaf_data_read(2); + + builder.validate_leaf_data_read_requests(); } #[test] - fn reset_leaf_data_reads_skips_nada() { - let read_requests = [PublicDataRead { leaf_slot: 88, value: 9999, counter: 2 }]; - let hints = [LeafDataReadHint::nada(1)]; - validate_leaf_data_read_requests(read_requests, leaf_data_hints, hints); + fn reset_leaf_data_reads_overriden_value_before_next_succeeds() { + let mut builder = TestBuilder::new(); + + // 2nd leaf is overriden by a pending write. + builder.add_leaf_data_read(2); + + // Tweak the counter of the read request to be before the pending write. + builder.read_requests[0].counter = builder.leaf_data[2].override_counter - 1; + + builder.validate_leaf_data_read_requests(); } - #[test(should_fail_with=""Hinted slot does not match read request")] + #[test(should_fail_with=""leaf_index in TestLeafData does not match")] fn reset_leaf_reads_wrong_slot_fails() { - let mut (read_requests, hints) = create_leaf_data_read_requests([1]); - hints[0].data_hint_index = 0; - validate_leaf_data_read_requests(read_requests, leaf_data_hints, hints); + let mut builder = TestBuilder::new(); + + builder.add_leaf_data_read(4); + builder.read_requests[0].leaf_slot += 1; + + builder.validate_leaf_data_read_requests(); } - #[test(should_fail_with=""Hinted value does not match read request")] + #[test(should_fail_with=""value in TestLeafData does not match")] fn reset_leaf_reads_wrong_value_fails() { - let mut (read_requests, hints) = create_leaf_data_read_requests([1]); - read_requests[0].value += 1; - validate_leaf_data_read_requests(read_requests, leaf_data_hints, hints); - } + let mut builder = TestBuilder::new(); + + builder.add_leaf_data_read(4); + builder.read_requests[0].value += 1; - #[test(should_fail_with=""Hinted leaf is overridden before the read request")] - fn reset_leaf_reads_overridden_value_fails() { - let mut leaf_data_hints_with_override_counter = leaf_data_hints; - let mut (read_requests, hints) = create_leaf_data_read_requests([1]); - leaf_data_hints_with_override_counter[1].override_counter = read_requests[0].counter - 1; - validate_leaf_data_read_requests(read_requests, leaf_data_hints_with_override_counter, hints); + builder.validate_leaf_data_read_requests(); } #[test] fn ensure_all_read_requests_are_verified_succeeds() { - let mut (pending_read_requests, pending_read_hints) = create_pending_read_requests([1]); - let mut (leaf_read_requests, leaf_data_read_hints) = create_leaf_data_read_requests([0, 1]); - let read_requests = [leaf_read_requests[0], pending_read_requests[0], leaf_read_requests[1]]; - pending_read_hints[0].read_request_index = 1; - leaf_data_read_hints[1].read_request_index = 2; - - let statuses = [ - ReadRequestStatus { state: ReadRequestState.SETTLED, hint_index: 0 }, - ReadRequestStatus { state: ReadRequestState.PENDING, hint_index: 0 }, - ReadRequestStatus { state: ReadRequestState.SETTLED, hint_index: 1 } - ]; - - ensure_all_read_requests_are_verified( - read_requests, - statuses, - pending_read_hints, - leaf_data_read_hints - ); + let mut builder = TestBuilder::new(); + + builder.add_leaf_data_read(4); + builder.add_pending_read(2); + builder.add_pending_read(1); + builder.add_leaf_data_read(1); + + builder.ensure_all_read_requests_are_verified(); } #[test(should_fail_with="Hinted pending read request does not match status")] fn ensure_all_read_requests_are_verified_wrong_pending_hint_index_fails() { - let (read_requests, hints) = create_pending_read_requests([0, 1]); - let statuses = [ - ReadRequestStatus { state: ReadRequestState.PENDING, hint_index: 0 }, - ReadRequestStatus { state: ReadRequestState.PENDING, hint_index: 0 } - ]; - ensure_all_read_requests_are_verified(read_requests, statuses, hints, []); + let mut builder = TestBuilder::new(); + + builder.add_pending_read(2); + builder.read_request_statuses[0].hint_index += 1; + + builder.ensure_all_read_requests_are_verified(); } #[test(should_fail_with="Hinted settled read request does not match status")] fn ensure_all_read_requests_are_verified_wrong_leaf_hint_index_fails() { - let (read_requests, hints) = create_leaf_data_read_requests([0, 1]); - let statuses = [ - ReadRequestStatus { state: ReadRequestState.SETTLED, hint_index: 0 }, - ReadRequestStatus { state: ReadRequestState.SETTLED, hint_index: 0 } - ]; - ensure_all_read_requests_are_verified(read_requests, statuses, [], hints); + let mut builder = TestBuilder::new(); + + builder.add_leaf_data_read(4); + builder.read_request_statuses[0].hint_index += 1; + + builder.ensure_all_read_requests_are_verified(); } - #[test(should_fail_with="Read request status must be PENDING or SETTLED")] + #[test(should_fail_with="Hinted settled read request does not match status")] fn ensure_all_read_requests_are_verified_wrong_status_fails() { - let (read_requests, hints) = create_leaf_data_read_requests([0]); - let statuses = [ReadRequestStatus { state: ReadRequestState.NADA, hint_index: 0 }]; - ensure_all_read_requests_are_verified(read_requests, statuses, [], hints); + let mut builder = TestBuilder::new(); + + builder.add_pending_read(2); + builder.read_request_statuses[0].state = ReadRequestState.NADA; + + builder.ensure_all_read_requests_are_verified(); } #[test] fn reset_mutable_data_read_requests_succeeds() { - let mut (pending_read_requests, pending_read_hints) = create_pending_read_requests([3, 1]); - let mut (leaf_read_requests, leaf_data_read_hints) = create_leaf_data_read_requests([0, 1]); - let read_requests = [ - leaf_read_requests[0], pending_read_requests[0], pending_read_requests[1], leaf_read_requests[1] - ]; - pending_read_hints[0].read_request_index = 1; - pending_read_hints[1].read_request_index = 2; - leaf_data_read_hints[1].read_request_index = 3; - - let statuses = [ - ReadRequestStatus { state: ReadRequestState.SETTLED, hint_index: 0 }, - ReadRequestStatus { state: ReadRequestState.PENDING, hint_index: 0 }, - ReadRequestStatus { state: ReadRequestState.PENDING, hint_index: 1 }, - ReadRequestStatus { state: ReadRequestState.SETTLED, hint_index: 1 } - ]; - - reset_mutable_data_read_requests( - read_requests, - statuses, - data_writes, - leaf_data_hints, - pending_read_hints, - leaf_data_read_hints - ); + let mut builder = TestBuilder::new(); + + builder.add_pending_read(1); + builder.add_leaf_data_read(4); + builder.add_pending_read(2); + builder.add_leaf_data_read(6); + builder.add_leaf_data_read(6); + builder.add_pending_read(1); + + builder.reset(); + } + + #[test] + fn reset_mutable_data_read_requests_no_requests_succeeds() { + let builder = TestBuilder::new(); + builder.reset(); } } diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/read_request.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/read_request.nr index 200dcd37219e..f3922db8f3d9 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/read_request.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/read_request.nr @@ -27,6 +27,16 @@ impl Empty for ReadRequestStatus { } } +impl ReadRequestStatus { + pub fn pending(hint_index: u32) -> Self { + ReadRequestStatus { state: ReadRequestState.PENDING, hint_index } + } + + pub fn settled(hint_index: u32) -> Self { + ReadRequestStatus { state: ReadRequestState.SETTLED, hint_index } + } +} + trait ReadValueHint { fn read_request_index(self) -> u32; } @@ -62,7 +72,7 @@ fn validate_pending_read_requests { for i in 0..NUM_PENDING_READS { let hint = hints[i]; let read_request_index = hint.read_request_index; @@ -80,9 +90,9 @@ fn validate_settled_read_requests + ReadValueHint, - LEAF_PREIMAGE: LeafPreimage + Readable { +) where + H: SettledReadHint + ReadValueHint, + LEAF_PREIMAGE: LeafPreimage + Readable { for i in 0..NUM_SETTLED_READS { let hint = hints[i]; let read_request_index = hint.read_request_index(); @@ -143,10 +153,10 @@ pub fn verify_reset_read_requests< settled_read_hints: [H; NUM_SETTLED_READS], tree_root: Field, propagated_read_requests: [ScopedReadRequest; READ_REQUEST_LEN] -) where - P: Readable, +) where + P: Readable, H: SettledReadHint + ReadValueHint, - LEAF_PREIMAGE: LeafPreimage + Readable { + LEAF_PREIMAGE: LeafPreimage + Readable { validate_pending_read_requests(read_requests, pending_values, pending_read_hints); validate_settled_read_requests(read_requests, settled_read_hints, tree_root); @@ -197,7 +207,7 @@ mod tests { counter: u32, } - impl Readable for TestValue { + impl Readable for TestValue { fn assert_match_read_request(self, read_request: ScopedReadRequest) { let siloed_value = silo_test_value(read_request.value()); assert_eq(self.value, siloed_value, "Hinted test value does not match"); @@ -226,7 +236,7 @@ mod tests { } } - impl Readable for TestLeafPreimage { + impl Readable for TestLeafPreimage { fn assert_match_read_request(self, read_request: ScopedReadRequest) { let siloed_value = silo_test_value(read_request.value()); assert_eq(siloed_value, self.value, "Provided leaf preimage is not for target value"); diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/mod.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/mod.nr index dd0fa37a7cdb..0d94bcd39413 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/mod.nr @@ -1,7 +1,6 @@ mod note_hash_read_request_hints_builder; mod nullifier_non_existent_read_request_hints_builder; mod nullifier_read_request_hints_builder; -mod public_data_read_request_hints_builder; use note_hash_read_request_hints_builder::NoteHashReadRequestHintsBuilder; use nullifier_read_request_hints_builder::NullifierReadRequestHintsBuilder; diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/public_data_read_request_hints_builder.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/public_data_read_request_hints_builder.nr deleted file mode 100644 index d2de7f04cd5f..000000000000 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/public_data_read_request_hints_builder.nr +++ /dev/null @@ -1,29 +0,0 @@ -use crate::{ - public_data_read_request_reset::PublicDataReadRequestHints, - reset::{mutable_data_read_request::LeafDataReadHint, read_request::{PendingReadHint, ReadRequestStatus}} -}; -use dep::types::constants::MAX_PUBLIC_DATA_READS_PER_TX; - -struct PublicDataReadRequestHintsBuilder { - read_request_statuses: [ReadRequestStatus; MAX_PUBLIC_DATA_READS_PER_TX], - pending_read_hints: BoundedVec, - leaf_data_read_hints: BoundedVec, -} - -impl PublicDataReadRequestHintsBuilder { - pub fn new(read_request_len: u32) -> Self { - PublicDataReadRequestHintsBuilder { - read_request_statuses: [ReadRequestStatus::empty(); MAX_PUBLIC_DATA_READS_PER_TX], - pending_read_hints: BoundedVec { storage: [PendingReadHint::nada(read_request_len); MAX_PUBLIC_DATA_READS_PER_TX], len: 0 }, - leaf_data_read_hints: BoundedVec { storage: [LeafDataReadHint::nada(read_request_len); MAX_PUBLIC_DATA_READS_PER_TX], len: 0 } - } - } - - pub fn to_hints(self) -> PublicDataReadRequestHints { - PublicDataReadRequestHints { - read_request_statuses: self.read_request_statuses, - pending_read_hints: self.pending_read_hints.storage, - leaf_data_read_hints: self.leaf_data_read_hints.storage - } - } -} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr index 3c2e588deff4..2eea173b23b5 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr @@ -19,6 +19,7 @@ mod note_hash; mod nullifier; mod public_data_read; mod public_data_update_request; +mod public_data_write; mod accumulated_data; mod validation_requests; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/note_hash.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/note_hash.nr index 9bb1a681298f..2b0504a745b2 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/note_hash.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/note_hash.nr @@ -19,7 +19,7 @@ impl Ordered for NoteHash { impl Eq for NoteHash { fn eq(self, other: NoteHash) -> bool { (self.value == other.value) - & (self.counter == other.counter) + & (self.counter == other.counter) } } @@ -116,7 +116,7 @@ impl Deserialize for ScopedNoteHash { } } -impl Readable for ScopedNoteHash { +impl Readable for ScopedNoteHash { fn assert_match_read_request(self, read_request: ScopedReadRequest) { assert_eq(self.note_hash.value, read_request.value(), "Value of the note hash does not match read request"); assert_eq(self.contract_address, read_request.contract_address, "Contract address of the note hash does not match read request"); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/note_hash_leaf_preimage.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/note_hash_leaf_preimage.nr index e1a933e2644d..2aa6bc09c340 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/note_hash_leaf_preimage.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/note_hash_leaf_preimage.nr @@ -27,7 +27,7 @@ impl LeafPreimage for NoteHashLeafPreimage { } } -impl Readable for NoteHashLeafPreimage { +impl Readable for NoteHashLeafPreimage { fn assert_match_read_request(self, read_request: ScopedReadRequest) { let siloed_value = compute_siloed_note_hash(read_request.contract_address, read_request.value()); assert_eq(self.value, siloed_value, "Value of the note hash leaf does not match read request"); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/nullifier.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/nullifier.nr index 62d44ab9d637..4865a8f744fc 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/nullifier.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/nullifier.nr @@ -30,7 +30,7 @@ impl Eq for Nullifier { fn eq(self, other: Nullifier) -> bool { (self.value == other.value) & (self.counter == other.counter) - & (self.note_hash == other.note_hash) + & (self.note_hash == other.note_hash) } } @@ -60,7 +60,7 @@ impl Deserialize for Nullifier { } } -impl Readable for Nullifier { +impl Readable for Nullifier { fn assert_match_read_request(self, read_request: ScopedReadRequest) { // Public kernels output Nullifier instead of ScopedNullifier. // The nullifier value has been siloed. @@ -110,7 +110,7 @@ impl OrderedValue for ScopedNullifier { impl Eq for ScopedNullifier { fn eq(self, other: ScopedNullifier) -> bool { (self.nullifier == other.nullifier) - & (self.contract_address == other.contract_address) + & (self.contract_address == other.contract_address) } } @@ -141,7 +141,7 @@ impl Deserialize for ScopedNullifier { } } -impl Readable for ScopedNullifier { +impl Readable for ScopedNullifier { fn assert_match_read_request(self, read_request: ScopedReadRequest) { assert_eq(self.nullifier.value, read_request.value(), "Value of the nullifier does not match read request"); assert_eq(self.contract_address, read_request.contract_address, "Contract address of the nullifier does not match read request"); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/nullifier_leaf_preimage.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/nullifier_leaf_preimage.nr index c6ce3f9bad0a..835510584b93 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/nullifier_leaf_preimage.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/nullifier_leaf_preimage.nr @@ -55,7 +55,7 @@ impl IndexedTreeLeafPreimage for NullifierLeafPreimage { } } -impl Readable for NullifierLeafPreimage { +impl Readable for NullifierLeafPreimage { fn assert_match_read_request(self, read_request: ScopedReadRequest) { let siloed_value = compute_siloed_nullifier(read_request.contract_address, read_request.value()); assert_eq(self.nullifier, siloed_value, "Value of the nullifier leaf does not match read request"); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_read.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_read.nr index 4a850c650629..7354cd51fa10 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_read.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_read.nr @@ -1,8 +1,10 @@ -use crate::constants::{GENERATOR_INDEX__PUBLIC_DATA_READ, PUBLIC_DATA_READ_LENGTH}; -use crate::traits::{Empty, Hash, Serialize, Deserialize}; -use crate::contrakt::storage_read::StorageRead; -use crate::data::hash::{compute_public_data_tree_value, compute_public_data_tree_index}; -use crate::address::AztecAddress; +use crate::{ + abis::side_effect::Ordered, address::AztecAddress, + constants::{GENERATOR_INDEX__PUBLIC_DATA_READ, PUBLIC_DATA_READ_LENGTH}, + contrakt::storage_read::StorageRead, + data::hash::{compute_public_data_tree_value, compute_public_data_tree_index}, + traits::{Empty, Hash, Serialize, Deserialize} +}; struct PublicDataRead { leaf_slot: Field, @@ -11,10 +13,7 @@ struct PublicDataRead { } impl PublicDataRead { - pub fn from_contract_storage_read( - contract_address: AztecAddress, - read_request: StorageRead - ) -> PublicDataRead { + pub fn from_contract_storage_read(contract_address: AztecAddress, read_request: StorageRead) -> PublicDataRead { PublicDataRead { leaf_slot: compute_public_data_tree_index(contract_address, read_request.storage_slot), value: compute_public_data_tree_value(read_request.current_value), @@ -39,6 +38,12 @@ impl Empty for PublicDataRead { } } +impl Ordered for PublicDataRead { + fn counter(self) -> u32 { + self.counter + } +} + impl Hash for PublicDataRead { fn hash(self) -> Field { crate::hash::poseidon2_hash_with_separator([ diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_update_request.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_update_request.nr index ac0d0738915d..93c0b9cbab48 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_update_request.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_update_request.nr @@ -1,11 +1,13 @@ -use crate::constants::{PUBLIC_DATA_UPDATE_REQUEST_LENGTH, GENERATOR_INDEX__PUBLIC_DATA_UPDATE_REQUEST}; -use crate::traits::{Empty, Hash, Serialize, Deserialize}; -use crate::public_data_tree_leaf::PublicDataTreeLeaf; -use crate::address::AztecAddress; -use crate::contrakt::storage_update_request::StorageUpdateRequest; -use crate::data::hash::{compute_public_data_tree_value, compute_public_data_tree_index}; -use crate::abis::side_effect::{Ordered, Positioned}; +use crate::{ + abis::{public_data_read::PublicDataRead, side_effect::{Ordered, Positioned, Readable}}, + address::AztecAddress, + constants::{PUBLIC_DATA_UPDATE_REQUEST_LENGTH, GENERATOR_INDEX__PUBLIC_DATA_UPDATE_REQUEST}, + contrakt::storage_update_request::StorageUpdateRequest, + data::hash::{compute_public_data_tree_value, compute_public_data_tree_index}, + public_data_tree_leaf::PublicDataTreeLeaf, traits::{Empty, Hash, Serialize, Deserialize} +}; +// TODO: Rename to PublicDataWrite struct PublicDataUpdateRequest { leaf_slot : Field, new_value : Field, @@ -63,6 +65,13 @@ impl Hash for PublicDataUpdateRequest { } } +impl Readable for PublicDataUpdateRequest { + fn assert_match_read_request(self, read_request: PublicDataRead) { + assert_eq(self.leaf_slot, read_request.leaf_slot, "leaf_slot in PublicDataUpdateRequest does not match read request"); + assert_eq(self.new_value, read_request.value, "value in PublicDataUpdateRequest does not match read request"); + } +} + impl From for PublicDataTreeLeaf { fn from(update_request: PublicDataUpdateRequest) -> PublicDataTreeLeaf { PublicDataTreeLeaf { diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_write.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_write.nr new file mode 100644 index 000000000000..531a453da3e3 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_write.nr @@ -0,0 +1,57 @@ +use crate::{ + abis::{ + public_data_read::PublicDataRead, public_data_update_request::PublicDataUpdateRequest, + side_effect::{Inner, Ordered, Overridable, Positioned, Readable} +}, + traits::Empty +}; + +struct OverridablePublicDataWrite { + write: PublicDataUpdateRequest, + override_counter: u32, +} + +impl Eq for OverridablePublicDataWrite { + fn eq(self, other: OverridablePublicDataWrite) -> bool { + (other.write == self.write) & (other.override_counter == self.override_counter) + } +} + +impl Empty for OverridablePublicDataWrite { + fn empty() -> Self { + Self { + write: PublicDataUpdateRequest::empty(), + override_counter: 0, + } + } +} + +impl Ordered for OverridablePublicDataWrite { + fn counter(self)-> u32{ + self.write.counter() + } +} + +impl Positioned for OverridablePublicDataWrite { + fn position(self) -> Field { + self.write.leaf_slot + } +} + +impl Readable for OverridablePublicDataWrite { + fn assert_match_read_request(self, read_request: PublicDataRead) { + self.write.assert_match_read_request(read_request); + } +} + +impl Overridable for OverridablePublicDataWrite { + fn override_counter(self) -> u32 { + self.override_counter + } +} + +impl Inner for OverridablePublicDataWrite { + fn inner(self) -> PublicDataUpdateRequest { + self.write + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/side_effect.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/side_effect.nr index e888e6a3fbd1..e3637186e104 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/side_effect.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/side_effect.nr @@ -24,7 +24,15 @@ trait Positioned { fn position(self) -> Field; } -trait Readable { - fn assert_match_read_request(self, read_request: ScopedReadRequest); +trait Readable { + fn assert_match_read_request(self, read_request: T); } +trait Overridable { + // The counter of the next side effect that's overriding the current side effect. + fn override_counter(self) -> u32; +} + +trait Inner { + fn inner(self) -> T; +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/contrakt/storage_read.nr b/noir-projects/noir-protocol-circuits/crates/types/src/contrakt/storage_read.nr index 6f44e699eeae..4defafdfae3a 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/contrakt/storage_read.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/contrakt/storage_read.nr @@ -6,7 +6,6 @@ use crate::{ struct StorageRead { storage_slot: Field, current_value: Field, - // TODO(dbanks12): use side effects properly in kernel checks counter: u32, } @@ -16,7 +15,7 @@ impl Eq for StorageRead { } } -impl Empty for StorageRead { +impl Empty for StorageRead { fn empty() -> Self { Self { storage_slot: 0, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/data/leaf_data_hint.nr b/noir-projects/noir-protocol-circuits/crates/types/src/data/leaf_data_hint.nr deleted file mode 100644 index 198dc744ec75..000000000000 --- a/noir-projects/noir-protocol-circuits/crates/types/src/data/leaf_data_hint.nr +++ /dev/null @@ -1,6 +0,0 @@ -trait LeafDataHint { - fn leaf_slot(self) -> Field; - fn value(self) -> Field; - fn override_counter(self) -> u32; -} - diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/data/mod.nr b/noir-projects/noir-protocol-circuits/crates/types/src/data/mod.nr index cf34767d6f94..5cc038d55a50 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/data/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/data/mod.nr @@ -1,3 +1,2 @@ -mod leaf_data_hint; mod public_data_hint; mod hash; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/data/public_data_hint.nr b/noir-projects/noir-protocol-circuits/crates/types/src/data/public_data_hint.nr index 081d677d60f8..90ca69180d44 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/data/public_data_hint.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/data/public_data_hint.nr @@ -1,6 +1,7 @@ use crate::{ - public_data_tree_leaf_preimage::PublicDataTreeLeafPreimage, data::leaf_data_hint::LeafDataHint, - traits::Empty, merkle_tree::{MembershipWitness, conditionally_assert_check_membership}, + abis::{public_data_read::PublicDataRead, side_effect::{Overridable, Readable}}, + public_data_tree_leaf_preimage::PublicDataTreeLeafPreimage, traits::Empty, + merkle_tree::{MembershipWitness, conditionally_assert_check_membership}, constants::PUBLIC_DATA_TREE_HEIGHT }; @@ -8,7 +9,7 @@ struct PublicDataHint { leaf_slot: Field, value: Field, override_counter: u32, - membership_witness: MembershipWitness, // Should be MembershipWitness when we can handle generics when converting to ts types. + membership_witness: MembershipWitness, leaf_preimage: PublicDataTreeLeafPreimage, } @@ -37,20 +38,6 @@ impl PublicDataHint { } } -impl LeafDataHint for PublicDataHint { - fn leaf_slot(self) -> Field { - self.leaf_slot - } - - fn value(self) -> Field { - self.value - } - - fn override_counter(self) -> u32 { - self.override_counter - } -} - impl Empty for PublicDataHint { fn empty() -> Self { PublicDataHint { @@ -62,3 +49,16 @@ impl Empty for PublicDataHint { } } } + +impl Readable for PublicDataHint { + fn assert_match_read_request(self, read_request: PublicDataRead) { + assert_eq(self.leaf_slot, read_request.leaf_slot, "leaf_slot in PublicDataHint does not match read request"); + assert_eq(self.value, read_request.value, "value in PublicDataHint does not match read request"); + } +} + +impl Overridable for PublicDataHint { + fn override_counter(self) -> u32 { + self.override_counter + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr index 8bd06548d490..82a6cefb6d31 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr @@ -789,13 +789,6 @@ impl FixtureBuilder { } } - pub fn add_read_request_for_pending_public_data(&mut self, public_date_update_request_index: u32) -> u32 { - let new_read_request_index = self.public_data_reads.len(); - let public_write = self.public_data_update_requests.get(public_date_update_request_index); - self.add_public_data_read_request(public_write.leaf_slot, public_write.new_value); - new_read_request_index - } - pub fn add_request_for_key_validation(&mut self, pk_m: Point, sk_app: Field, sk_app_generator: Field) -> u32 { let new_request_index = self.scoped_key_validation_requests_and_generators.len(); let request = KeyValidationRequest { pk_m, sk_app }; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr index d2358e05f5a6..2f310a538f40 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr @@ -1,5 +1,6 @@ -mod assert_combined_deduped_array; mod assert_combined_sorted_transformed_value_array; +mod assert_combined_transformed_array; +mod assert_deduped_array; mod assert_exposed_sorted_transformed_value_array; mod assert_sorted_array; mod assert_sorted_transformed_value_array; @@ -11,19 +12,16 @@ mod sort_by; mod sort_by_counter; // Re-exports. -use assert_combined_deduped_array::{ - assert_combined_deduped_array, dedupe_array::dedupe_array, - get_deduped_hints::{DedupedHints, get_deduped_hints}, - sort_by_position_then_counter::sort_by_position_then_counter -}; use assert_combined_sorted_transformed_value_array::{ assert_combined_sorted_transformed_value_array_asc, get_combined_order_hints::{CombinedOrderHint, get_combined_order_hints_asc} }; +use assert_combined_transformed_array::assert_combined_transformed_array; use assert_exposed_sorted_transformed_value_array::{ assert_exposed_sorted_transformed_value_array, get_order_hints::{get_order_hints_asc, get_order_hints_desc, OrderHint} }; +use assert_deduped_array::{assert_deduped_array, dedupe_array}; use assert_sorted_array::assert_sorted_array; use assert_split_sorted_transformed_value_arrays::{ assert_split_sorted_transformed_value_arrays_asc, assert_split_sorted_transformed_value_arrays_desc, @@ -130,7 +128,11 @@ pub fn array_merge(array1: [T; N], array2: [T; N]) -> [T; N] wher result } -pub fn check_permutation(original_array: [T; N], permuted_array: [T; N], original_indexes: [u32; N]) where T: Eq + Empty { +pub fn check_permutation( + original_array: [T; N], + permuted_array: [T; N], + original_indexes: [u32; N] +) where T: Eq + Empty { let mut seen_value = [false; N]; for i in 0..N { let index = original_indexes[i]; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array.nr deleted file mode 100644 index 366ec585a2f3..000000000000 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array.nr +++ /dev/null @@ -1,218 +0,0 @@ -mod assert_combined_permuted_array; -mod assert_deduped_array; -mod dedupe_array; -mod get_deduped_hints; -mod sort_by_position_then_counter; - -use crate::{ - abis::side_effect::{Positioned, Ordered}, traits::{Empty, is_empty}, - utils::arrays::{ - array_length, - assert_combined_deduped_array::{ - assert_combined_permuted_array::assert_combined_permuted_array, - assert_deduped_array::assert_deduped_array, get_deduped_hints::DedupedHints -} -} -}; - -// original_array_(lt/gte) must be valid, i.e. validate_array(original_array) == true -// All non-empty items in the original arrays must be unique, otherwise duplicate values could be merged and -// assert_combined_permuted_array would still pass undetected. -// This is currently used for deduplicating public data writes, where each public data write is unique due to having a unique counter. -pub fn assert_combined_deduped_array( - original_array_lt: [T; N], - original_array_gte: [T; N], - sorted_array: [T; N], - deduped_array: [T; N], - hints: DedupedHints -) where T: Positioned + Ordered + Empty + Eq { - assert_combined_permuted_array( - original_array_lt, - original_array_gte, - sorted_array, - hints.combined_indexes - ); - assert_deduped_array(sorted_array, deduped_array, hints.run_lengths); -} - -mod tests { - use crate::{ - abis::side_effect::{Positioned, Ordered}, tests::utils::pad_end, - utils::arrays::{ - array_merge, - assert_combined_deduped_array::{ - assert_deduped_array::{assert_deduped_array, tests::TestContainer}, - assert_combined_deduped_array, dedupe_array::dedupe_array, - get_deduped_hints::{DedupedHints, get_deduped_hints} - } - } - }; - - fn verify_all( - original_array_lt: [TestContainer; N], - original_array_gte: [TestContainer; N], - sorted_array: [TestContainer; N], - deduped_array: [TestContainer; N], - hints: DedupedHints - ) { - let merged = array_merge(original_array_lt, original_array_gte); - assert_eq(deduped_array, dedupe_array(merged)); - assert_eq(hints, get_deduped_hints(original_array_lt, original_array_gte)); - assert_combined_deduped_array( - original_array_lt, - original_array_gte, - sorted_array, - deduped_array, - hints - ); - } - - #[test] - fn assert_combined_deduped_array_full() { - let original_array_lt = pad_end( - [ - TestContainer { value: 4, position: 3, counter: 2 }, - TestContainer { value: 7, position: 4, counter: 8 }, - TestContainer { value: 3, position: 2, counter: 3 }, - TestContainer { value: 9, position: 5, counter: 7 } - ], - TestContainer::empty() - ); - let original_array_gte = pad_end( - [ - TestContainer { value: 1, position: 1, counter: 1 }, - TestContainer { value: 5, position: 3, counter: 5 }, - TestContainer { value: 6, position: 3, counter: 6 }, - TestContainer { value: 8, position: 4, counter: 9 }, - TestContainer { value: 2, position: 1, counter: 4 } - ], - TestContainer::empty() - ); - let sorted_array = [ - TestContainer { value: 1, position: 1, counter: 1 }, - TestContainer { value: 2, position: 1, counter: 4 }, - TestContainer { value: 3, position: 2, counter: 3 }, - TestContainer { value: 4, position: 3, counter: 2 }, - TestContainer { value: 5, position: 3, counter: 5 }, - TestContainer { value: 6, position: 3, counter: 6 }, - TestContainer { value: 7, position: 4, counter: 8 }, - TestContainer { value: 8, position: 4, counter: 9 }, - TestContainer { value: 9, position: 5, counter: 7 } - ]; - let deduped_array = [ - TestContainer { value: 2, position: 1, counter: 4 }, - TestContainer { value: 3, position: 2, counter: 3 }, - TestContainer { value: 6, position: 3, counter: 6 }, - TestContainer { value: 8, position: 4, counter: 9 }, - TestContainer { value: 9, position: 5, counter: 7 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 } - ]; - let hints = DedupedHints { combined_indexes: [3, 6, 2, 8, 0, 4, 5, 7, 1], run_lengths: [2, 1, 3, 2, 1, 0, 0, 0, 0] }; - verify_all( - original_array_lt, - original_array_gte, - sorted_array, - deduped_array, - hints - ); - } - - #[test] - fn assert_combined_deduped_array_padded_empty() { - let original_array_lt = pad_end( - [ - TestContainer { value: 4, position: 3, counter: 2 }, - TestContainer { value: 7, position: 4, counter: 8 }, - TestContainer { value: 3, position: 2, counter: 3 }, - TestContainer { value: 9, position: 5, counter: 7 } - ], - TestContainer::empty() - ); - let original_array_gte = pad_end( - [ - TestContainer { value: 1, position: 1, counter: 1 }, - TestContainer { value: 5, position: 3, counter: 5 }, - TestContainer { value: 6, position: 3, counter: 6 }, - TestContainer { value: 8, position: 4, counter: 9 }, - TestContainer { value: 2, position: 1, counter: 4 } - ], - TestContainer::empty() - ); - let sorted_array = pad_end( - [ - TestContainer { value: 1, position: 1, counter: 1 }, - TestContainer { value: 2, position: 1, counter: 4 }, - TestContainer { value: 3, position: 2, counter: 3 }, - TestContainer { value: 4, position: 3, counter: 2 }, - TestContainer { value: 5, position: 3, counter: 5 }, - TestContainer { value: 6, position: 3, counter: 6 }, - TestContainer { value: 7, position: 4, counter: 8 }, - TestContainer { value: 8, position: 4, counter: 9 }, - TestContainer { value: 9, position: 5, counter: 7 } - ], - TestContainer::empty() - ); - let deduped_array = pad_end( - [ - TestContainer { value: 2, position: 1, counter: 4 }, - TestContainer { value: 3, position: 2, counter: 3 }, - TestContainer { value: 6, position: 3, counter: 6 }, - TestContainer { value: 8, position: 4, counter: 9 }, - TestContainer { value: 9, position: 5, counter: 7 } - ], - TestContainer::empty() - ); - let hints = DedupedHints { - combined_indexes: [3, 6, 2, 8, 0, 4, 5, 7, 1, 11, 10, 9], - run_lengths: [2, 1, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0] - }; - verify_all( - original_array_lt, - original_array_gte, - sorted_array, - deduped_array, - hints - ); - } - - #[test] - fn assert_combined_deduped_array_no_duplicates() { - let original_array_lt = pad_end( - [ - TestContainer { value: 3, position: 3, counter: 3 }, - TestContainer { value: 1, position: 1, counter: 1 }, - TestContainer { value: 2, position: 2, counter: 2 } - ], - TestContainer::empty() - ); - let original_array_gte = pad_end( - [ - TestContainer { value: 4, position: 4, counter: 4 }, - TestContainer { value: 5, position: 5, counter: 5 } - ], - TestContainer::empty() - ); - let sorted_array = pad_end( - [ - TestContainer { value: 1, position: 1, counter: 1 }, - TestContainer { value: 2, position: 2, counter: 2 }, - TestContainer { value: 3, position: 3, counter: 3 }, - TestContainer { value: 4, position: 4, counter: 4 }, - TestContainer { value: 5, position: 5, counter: 5 } - ], - TestContainer::empty() - ); - let deduped_array = sorted_array; - let hints = DedupedHints { combined_indexes: [2, 0, 1, 3, 4, 7, 6, 5], run_lengths: [1, 1, 1, 1, 1, 0, 0, 0] }; - verify_all( - original_array_lt, - original_array_gte, - sorted_array, - deduped_array, - hints - ); - } -} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_transformed_array.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_transformed_array.nr new file mode 100644 index 000000000000..ab1238524ea9 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_transformed_array.nr @@ -0,0 +1,25 @@ +use crate::{traits::Empty, utils::arrays::array_length}; + +// original_array(_lt/_gte) must be valid, i.e. validate_array(original_array) == true +pub fn assert_combined_transformed_array( + original_array_lt: [T; N], + original_array_gte: [T; N], + combined_array: [S; N], + is_transformed: fn[Env](T, S) -> bool +) where T: Empty + Eq { + let num_lt = array_length(original_array_lt); + let mut is_lt = true; + for i in 0..N { + is_lt &= i != num_lt; + + let from = if is_lt { + original_array_lt[i] + } else { + original_array_gte[i - num_lt] + }; + + let to = combined_array[i]; + + assert(is_transformed(from, to), "hinted item in the commbined array does not match"); + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_deduped_array.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_deduped_array.nr new file mode 100644 index 000000000000..1d28473bb942 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_deduped_array.nr @@ -0,0 +1,30 @@ +use crate::{abis::side_effect::{Inner, Overridable}, traits::{Empty, is_empty}}; + +pub fn assert_deduped_array( + original_array: [S; N], + deduped_array: [T; N] +) where S: Overridable + Inner, T: Eq + Empty { + let mut num_deduped = 0; + for i in 0..original_array.len() { + let original = original_array[i]; + if original.override_counter() == 0 { + assert_eq(original.inner(), deduped_array[num_deduped], "mismatch deduped item"); + num_deduped += 1; + } else { + assert( + is_empty(deduped_array[N - num_deduped]), "empty items must be padded to the deduped array" + ); + } + } +} + +pub fn dedupe_array(original_array: [S; N]) -> [T; N] where S: Overridable + Inner { + let mut deduped = BoundedVec::new(); + for i in 0..original_array.len() { + let original = original_array[i]; + if original.override_counter() == 0 { + deduped.push(original.inner()); + } + } + deduped.storage +} diff --git a/yarn-project/circuits.js/src/hints/build_public_data_read_request_hints.test.ts b/yarn-project/circuits.js/src/hints/build_public_data_read_request_hints.test.ts deleted file mode 100644 index e1e0193decce..000000000000 --- a/yarn-project/circuits.js/src/hints/build_public_data_read_request_hints.test.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { makeTuple } from '@aztec/foundation/array'; -import { padArrayEnd } from '@aztec/foundation/collection'; -import { Fr } from '@aztec/foundation/fields'; -import { type Tuple } from '@aztec/foundation/serialize'; - -import { - MAX_PUBLIC_DATA_HINTS, - MAX_PUBLIC_DATA_READS_PER_TX, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, -} from '../constants.gen.js'; -import { - LeafDataReadHint, - PendingReadHint, - PublicDataHint, - PublicDataRead, - PublicDataUpdateRequest, - ReadRequestStatus, -} from '../structs/index.js'; -import { buildPublicDataReadRequestHints } from './build_public_data_read_request_hints.js'; - -describe('buildPublicDataReadRequestHints', () => { - let publicDataReads: Tuple; - let expectedStatuses: Tuple; - let expectedPendingHints: Tuple; - let expectedLeafDataHints: Tuple; - let counter = 0; - const nextCounter = () => counter++; - - const makePublicDataWrite = (leafSlot: number, value: number) => - new PublicDataUpdateRequest(new Fr(leafSlot), new Fr(value), nextCounter()); - const makePublicDataHint = (slot: number, value: number) => { - const hint = PublicDataHint.empty(); - hint.leafSlot = new Fr(slot); - hint.value = new Fr(value); - return hint; - }; - const makePublicDataRead = (leafSlot: number, value: number) => - new PublicDataRead(new Fr(leafSlot), new Fr(value), nextCounter()); - const makePendingHint = (readRequestIndex: number, hintIndex: number) => - new PendingReadHint(readRequestIndex, hintIndex); - const makeLeafDataHint = (readRequestIndex: number, hintIndex: number) => - new LeafDataReadHint(readRequestIndex, hintIndex); - - const publicDataUpdateRequests = padArrayEnd( - [makePublicDataWrite(55, 5555), makePublicDataWrite(77, 7777), makePublicDataWrite(99, 9999)], - PublicDataUpdateRequest.empty(), - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - ); - - const publicDataHints = padArrayEnd( - [ - makePublicDataHint(11, 100), - makePublicDataHint(22, 200), - makePublicDataHint(33, 300), - makePublicDataHint(55, 500), - makePublicDataHint(77, 0), - makePublicDataHint(99, 900), - ], - PublicDataHint.empty(), - MAX_PUBLIC_DATA_HINTS, - ); - - const buildHints = () => buildPublicDataReadRequestHints(publicDataReads, publicDataUpdateRequests, publicDataHints); - - const buildAndCheckHints = () => { - const hints = buildHints(); - expect(hints.readRequestStatuses).toEqual(expectedStatuses); - expect(hints.pendingReadHints).toEqual(expectedPendingHints); - expect(hints.leafDataReadHints).toEqual(expectedLeafDataHints); - }; - - beforeEach(() => { - publicDataReads = makeTuple(MAX_PUBLIC_DATA_READS_PER_TX, PublicDataRead.empty); - expectedStatuses = makeTuple(MAX_PUBLIC_DATA_READS_PER_TX, ReadRequestStatus.nada); - expectedPendingHints = makeTuple(MAX_PUBLIC_DATA_READS_PER_TX, () => - PendingReadHint.nada(MAX_PUBLIC_DATA_READS_PER_TX), - ); - expectedLeafDataHints = makeTuple(MAX_PUBLIC_DATA_READS_PER_TX, () => - LeafDataReadHint.nada(MAX_PUBLIC_DATA_READS_PER_TX), - ); - }); - - it('returns empty hints', () => { - buildAndCheckHints(); - }); - - it('builds hints for reading pending values', () => { - publicDataReads[0] = makePublicDataRead(77, 7777); - publicDataReads[1] = makePublicDataRead(99, 9999); - publicDataReads[2] = makePublicDataRead(55, 5555); - expectedStatuses[0] = ReadRequestStatus.pending(0); - expectedStatuses[1] = ReadRequestStatus.pending(1); - expectedStatuses[2] = ReadRequestStatus.pending(2); - expectedPendingHints[0] = makePendingHint(0, 1); - expectedPendingHints[1] = makePendingHint(1, 2); - expectedPendingHints[2] = makePendingHint(2, 0); - - buildAndCheckHints(); - }); - - it('builds hints for reading settled or uninitialized values', () => { - publicDataReads[0] = makePublicDataRead(33, 300); - publicDataReads[1] = makePublicDataRead(77, 0); - publicDataReads[2] = makePublicDataRead(55, 500); - publicDataReads[3] = makePublicDataRead(11, 100); - expectedStatuses[0] = ReadRequestStatus.settled(0); - expectedStatuses[1] = ReadRequestStatus.settled(1); - expectedStatuses[2] = ReadRequestStatus.settled(2); - expectedStatuses[3] = ReadRequestStatus.settled(3); - expectedLeafDataHints[0] = makeLeafDataHint(0, 2); - expectedLeafDataHints[1] = makeLeafDataHint(1, 4); - expectedLeafDataHints[2] = makeLeafDataHint(2, 3); - expectedLeafDataHints[3] = makeLeafDataHint(3, 0); - - buildAndCheckHints(); - }); - - it('builds hints for reading pending and settled values', () => { - publicDataReads[0] = makePublicDataRead(55, 500); - publicDataReads[1] = makePublicDataRead(55, 5555); - publicDataReads[2] = makePublicDataRead(77, 0); - publicDataReads[3] = makePublicDataRead(11, 100); - publicDataReads[4] = makePublicDataRead(99, 9999); - publicDataReads[5] = makePublicDataRead(77, 7777); - publicDataReads[6] = makePublicDataRead(11, 100); - expectedStatuses[0] = ReadRequestStatus.settled(0); - expectedStatuses[1] = ReadRequestStatus.pending(0); - expectedStatuses[2] = ReadRequestStatus.settled(1); - expectedStatuses[3] = ReadRequestStatus.settled(2); - expectedStatuses[4] = ReadRequestStatus.pending(1); - expectedStatuses[5] = ReadRequestStatus.pending(2); - expectedStatuses[6] = ReadRequestStatus.settled(3); - expectedPendingHints[0] = makePendingHint(1, 0); - expectedPendingHints[1] = makePendingHint(4, 2); - expectedPendingHints[2] = makePendingHint(5, 1); - expectedLeafDataHints[0] = makeLeafDataHint(0, 3); - expectedLeafDataHints[1] = makeLeafDataHint(2, 4); - expectedLeafDataHints[2] = makeLeafDataHint(3, 0); - expectedLeafDataHints[3] = makeLeafDataHint(6, 0); - - buildAndCheckHints(); - }); - - it('throws if reading unknown slot', () => { - publicDataReads[0] = makePublicDataRead(123, 100); - expect(() => buildHints()).toThrow('Cannot find a pending write or a data hint for the read request.'); - }); - - it('throws if reading unknown value', () => { - publicDataReads[0] = makePublicDataRead(11, 1111); - expect(() => buildHints()).toThrow('Value being read does not match existing public data or pending writes.'); - }); -}); diff --git a/yarn-project/circuits.js/src/hints/build_public_data_read_request_hints.ts b/yarn-project/circuits.js/src/hints/build_public_data_read_request_hints.ts deleted file mode 100644 index 4f7b15f312b8..000000000000 --- a/yarn-project/circuits.js/src/hints/build_public_data_read_request_hints.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { type Tuple } from '@aztec/foundation/serialize'; - -import { - type MAX_PUBLIC_DATA_HINTS, - type MAX_PUBLIC_DATA_READS_PER_TX, - type MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, -} from '../constants.gen.js'; -import { - type PublicDataRead, - PublicDataReadRequestHintsBuilder, - type PublicDataUpdateRequest, -} from '../structs/index.js'; -import { type PublicDataHint } from '../structs/public_data_hint.js'; -import { countAccumulatedItems } from '../utils/index.js'; - -export function buildPublicDataReadRequestHints( - publicDataReads: Tuple, - publicDataUpdateRequests: Tuple, - publicDataHints: Tuple, -) { - const builder = new PublicDataReadRequestHintsBuilder(); - - const numReadRequests = countAccumulatedItems(publicDataReads); - for (let i = 0; i < numReadRequests; ++i) { - const rr = publicDataReads[i]; - // TODO: Add counters to reads and writes. - const writeIndex = publicDataUpdateRequests.findIndex( - w => w.leafSlot.equals(rr.leafSlot) && w.newValue.equals(rr.value), - ); - if (writeIndex !== -1) { - builder.addPendingReadRequest(i, writeIndex); - } else { - const hintIndex = publicDataHints.findIndex(h => h.leafSlot.equals(rr.leafSlot)); - if (hintIndex === -1) { - throw new Error('Cannot find a pending write or a data hint for the read request.'); - } - if (!publicDataHints[hintIndex].value.equals(rr.value)) { - throw new Error('Value being read does not match existing public data or pending writes.'); - } - builder.addLeafDataReadRequest(i, hintIndex); - } - } - - return builder.toHints(); -} diff --git a/yarn-project/circuits.js/src/hints/index.ts b/yarn-project/circuits.js/src/hints/index.ts index 92b57bd25e1e..67970d7bdc41 100644 --- a/yarn-project/circuits.js/src/hints/index.ts +++ b/yarn-project/circuits.js/src/hints/index.ts @@ -2,5 +2,4 @@ export * from './build_note_hash_read_request_hints.js'; export * from './build_nullifier_non_existent_read_request_hints.js'; export * from './build_nullifier_read_request_hints.js'; export * from './build_public_data_hints.js'; -export * from './build_public_data_read_request_hints.js'; export * from './build_transient_data_hints.js'; diff --git a/yarn-project/circuits.js/src/structs/index.ts b/yarn-project/circuits.js/src/structs/index.ts index d5e042313a6f..96f5890100c5 100644 --- a/yarn-project/circuits.js/src/structs/index.ts +++ b/yarn-project/circuits.js/src/structs/index.ts @@ -61,7 +61,6 @@ export * from './public_call_stack_item_compressed.js'; export * from './public_circuit_public_inputs.js'; export * from './public_data_hint.js'; export * from './public_data_read.js'; -export * from './public_data_read_request_hints.js'; export * from './public_data_update_request.js'; export * from './public_validation_requests.js'; export * from './read_request.js'; diff --git a/yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts b/yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts index c6f22d4b5368..cb3a15b87a70 100644 --- a/yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts +++ b/yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts @@ -7,7 +7,6 @@ import { } from '../non_existent_read_request_hints.js'; import { PartialStateReference } from '../partial_state_reference.js'; import { PublicDataHint } from '../public_data_hint.js'; -import { PublicDataReadRequestHints } from '../public_data_read_request_hints.js'; import { type NullifierReadRequestHints, nullifierReadRequestHintsFromBuffer } from '../read_request_hints/index.js'; import { PublicKernelData } from './public_kernel_data.js'; @@ -29,7 +28,6 @@ export class PublicKernelTailCircuitPrivateInputs { */ public readonly nullifierNonExistentReadRequestHints: NullifierNonExistentReadRequestHints, public readonly publicDataHints: Tuple, - public readonly publicDataReadRequestHints: PublicDataReadRequestHints, public readonly startState: PartialStateReference, ) {} @@ -39,7 +37,6 @@ export class PublicKernelTailCircuitPrivateInputs { this.nullifierReadRequestHints, this.nullifierNonExistentReadRequestHints, this.publicDataHints, - this.publicDataReadRequestHints, this.startState, ); } @@ -63,7 +60,6 @@ export class PublicKernelTailCircuitPrivateInputs { ), nullifierNonExistentReadRequestHintsFromBuffer(reader), reader.readArray(MAX_PUBLIC_DATA_HINTS, PublicDataHint), - reader.readObject(PublicDataReadRequestHints), reader.readObject(PartialStateReference), ); } diff --git a/yarn-project/circuits.js/src/structs/public_data_read_request_hints.ts b/yarn-project/circuits.js/src/structs/public_data_read_request_hints.ts deleted file mode 100644 index 4d2cc36b1fde..000000000000 --- a/yarn-project/circuits.js/src/structs/public_data_read_request_hints.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { makeTuple } from '@aztec/foundation/array'; -import { BufferReader, type Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; - -import { MAX_PUBLIC_DATA_READS_PER_TX } from '../constants.gen.js'; -import { PendingReadHint, ReadRequestState, ReadRequestStatus } from './read_request_hints/index.js'; - -export class LeafDataReadHint { - constructor(public readRequestIndex: number, public dataHintIndex: number) {} - - static nada(readRequestLen: number) { - return new LeafDataReadHint(readRequestLen, 0); - } - - static fromBuffer(buffer: Buffer | BufferReader) { - const reader = BufferReader.asReader(buffer); - return new LeafDataReadHint(reader.readNumber(), reader.readNumber()); - } - - toBuffer() { - return serializeToBuffer(this.readRequestIndex, this.dataHintIndex); - } -} - -export class PublicDataReadRequestHints { - constructor( - public readRequestStatuses: Tuple, - public pendingReadHints: Tuple, - public leafDataReadHints: Tuple, - ) {} - - static fromBuffer(buffer: Buffer | BufferReader) { - const reader = BufferReader.asReader(buffer); - return new PublicDataReadRequestHints( - reader.readArray(MAX_PUBLIC_DATA_READS_PER_TX, ReadRequestStatus), - reader.readArray(MAX_PUBLIC_DATA_READS_PER_TX, PendingReadHint), - reader.readArray(MAX_PUBLIC_DATA_READS_PER_TX, LeafDataReadHint), - ); - } - - toBuffer() { - return serializeToBuffer(this.readRequestStatuses, this.pendingReadHints, this.leafDataReadHints); - } -} - -export class PublicDataReadRequestHintsBuilder { - private hints: PublicDataReadRequestHints; - private numPendingReadHints = 0; - private numLeafDataReadHints = 0; - - constructor() { - this.hints = new PublicDataReadRequestHints( - makeTuple(MAX_PUBLIC_DATA_READS_PER_TX, ReadRequestStatus.nada), - makeTuple(MAX_PUBLIC_DATA_READS_PER_TX, () => PendingReadHint.nada(MAX_PUBLIC_DATA_READS_PER_TX)), - makeTuple(MAX_PUBLIC_DATA_READS_PER_TX, () => LeafDataReadHint.nada(MAX_PUBLIC_DATA_READS_PER_TX)), - ); - } - - static empty() { - return new PublicDataReadRequestHintsBuilder().toHints(); - } - - addPendingReadRequest(readRequestIndex: number, publicDataWriteIndex: number) { - this.hints.readRequestStatuses[readRequestIndex] = new ReadRequestStatus( - ReadRequestState.PENDING, - this.numPendingReadHints, - ); - this.hints.pendingReadHints[this.numPendingReadHints] = new PendingReadHint(readRequestIndex, publicDataWriteIndex); - this.numPendingReadHints++; - } - - addLeafDataReadRequest(readRequestIndex: number, leafDataDataHintIndex: number) { - this.hints.readRequestStatuses[readRequestIndex] = new ReadRequestStatus( - ReadRequestState.SETTLED, - this.numLeafDataReadHints, - ); - this.hints.leafDataReadHints[this.numLeafDataReadHints] = new LeafDataReadHint( - readRequestIndex, - leafDataDataHintIndex, - ); - this.numLeafDataReadHints++; - } - - toHints() { - return this.hints; - } -} diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index d543cba90c0d..70882bcd2dc4 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -105,7 +105,6 @@ import { PublicCircuitPublicInputs, PublicDataHint, PublicDataRead, - PublicDataReadRequestHintsBuilder, PublicDataTreeLeaf, PublicDataTreeLeafPreimage, PublicDataUpdateRequest, @@ -679,7 +678,6 @@ export function makePublicKernelTailCircuitPrivateInputs(seed = 1): PublicKernel NullifierReadRequestHintsBuilder.empty(MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX), NullifierNonExistentReadRequestHintsBuilder.empty(), makeTuple(MAX_PUBLIC_DATA_HINTS, PublicDataHint.empty, seed + 0x100), - PublicDataReadRequestHintsBuilder.empty(), makePartialStateReference(seed + 0x200), ); } diff --git a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts index fe629bbdb28e..590f4a554719 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -33,7 +33,6 @@ import { KeyValidationRequest, KeyValidationRequestAndGenerator, L2ToL1Message, - type LeafDataReadHint, LogHash, MAX_ENCRYPTED_LOGS_PER_TX, MAX_KEY_VALIDATION_REQUESTS_PER_TX, @@ -97,7 +96,6 @@ import { type PublicCircuitPublicInputs, type PublicDataHint, PublicDataRead, - type PublicDataReadRequestHints, type PublicDataTreeLeaf, type PublicDataTreeLeafPreimage, PublicDataUpdateRequest, @@ -166,7 +164,6 @@ import type { KeyValidationRequestAndGenerator as KeyValidationRequestAndGeneratorNoir, KeyValidationRequest as KeyValidationRequestsNoir, L2ToL1Message as L2ToL1MessageNoir, - LeafDataReadHint as LeafDataReadHintNoir, LogHash as LogHashNoir, MaxBlockNumber as MaxBlockNumberNoir, MembershipWitness as MembershipWitnessNoir, @@ -216,7 +213,6 @@ import type { PublicCircuitPublicInputs as PublicCircuitPublicInputsNoir, PublicDataHint as PublicDataHintNoir, PublicDataRead as PublicDataReadNoir, - PublicDataReadRequestHints as PublicDataReadRequestHintsNoir, PublicDataTreeLeaf as PublicDataTreeLeafNoir, PublicDataTreeLeafPreimage as PublicDataTreeLeafPreimageNoir, PublicDataUpdateRequest as PublicDataUpdateRequestNoir, @@ -1086,13 +1082,6 @@ function mapPendingReadHintToNoir(hint: PendingReadHint): PendingReadHintNoir { }; } -function mapLeafDataReadHintToNoir(hint: LeafDataReadHint): LeafDataReadHintNoir { - return { - read_request_index: mapNumberToNoir(hint.readRequestIndex), - data_hint_index: mapNumberToNoir(hint.dataHintIndex), - }; -} - function mapNoteHashSettledReadHintToNoir( hint: SettledReadHint, ): NoteHashSettledReadHintNoir { @@ -1176,14 +1165,6 @@ function mapPublicDataHintToNoir(hint: PublicDataHint): PublicDataHintNoir { }; } -function mapPublicDataReadRequestHintsToNoir(hints: PublicDataReadRequestHints): PublicDataReadRequestHintsNoir { - return { - read_request_statuses: mapTuple(hints.readRequestStatuses, mapReadRequestStatusToNoir), - pending_read_hints: mapTuple(hints.pendingReadHints, mapPendingReadHintToNoir), - leaf_data_read_hints: mapTuple(hints.leafDataReadHints, mapLeafDataReadHintToNoir), - }; -} - function mapOptionalNumberToNoir(option: OptionalNumber): OptionalNumberNoir { return { _is_some: option.isSome, @@ -1760,7 +1741,6 @@ export function mapPublicKernelTailCircuitPrivateInputsToNoir( inputs.nullifierNonExistentReadRequestHints, ), public_data_hints: mapTuple(inputs.publicDataHints, mapPublicDataHintToNoir), - public_data_read_request_hints: mapPublicDataReadRequestHintsToNoir(inputs.publicDataReadRequestHints), start_state: mapPartialStateReferenceToNoir(inputs.startState), }; } diff --git a/yarn-project/simulator/src/public/hints_builder.ts b/yarn-project/simulator/src/public/hints_builder.ts index 5b0ad19713e7..d8488426cfd3 100644 --- a/yarn-project/simulator/src/public/hints_builder.ts +++ b/yarn-project/simulator/src/public/hints_builder.ts @@ -4,14 +4,12 @@ import { type MAX_NULLIFIERS_PER_TX, type MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX, - type MAX_PUBLIC_DATA_HINTS, type MAX_PUBLIC_DATA_READS_PER_TX, type MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MembershipWitness, NULLIFIER_TREE_HEIGHT, type Nullifier, PUBLIC_DATA_TREE_HEIGHT, - type PublicDataHint, type PublicDataRead, type PublicDataTreeLeafPreimage, type PublicDataUpdateRequest, @@ -19,7 +17,6 @@ import { buildNullifierNonExistentReadRequestHints, buildPublicDataHint, buildPublicDataHints, - buildPublicDataReadRequestHints, buildSiloedNullifierReadRequestHints, } from '@aztec/circuits.js'; import { type Tuple } from '@aztec/foundation/serialize'; @@ -62,14 +59,6 @@ export class HintsBuilder { return buildPublicDataHint(this, slot); } - getPublicDataReadRequestHints( - publicDataReads: Tuple, - publicDataUpdateRequests: Tuple, - publicDataHints: Tuple, - ) { - return buildPublicDataReadRequestHints(publicDataReads, publicDataUpdateRequests, publicDataHints); - } - async getNullifierMembershipWitness(nullifier: Fr) { const index = await this.db.findLeafIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBuffer()); if (index === undefined) { diff --git a/yarn-project/simulator/src/public/tail_phase_manager.ts b/yarn-project/simulator/src/public/tail_phase_manager.ts index 5b55af3e250b..73ad72be1ac6 100644 --- a/yarn-project/simulator/src/public/tail_phase_manager.ts +++ b/yarn-project/simulator/src/public/tail_phase_manager.ts @@ -104,12 +104,6 @@ export class TailPhaseManager extends AbstractPhaseManager { pendingPublicDataWrites, ); - const publicDataReadRequestHints = this.hintsBuilder.getPublicDataReadRequestHints( - validationRequests.publicDataReads, - pendingPublicDataWrites, - publicDataHints, - ); - const currentState = await this.db.getStateReference(); return new PublicKernelTailCircuitPrivateInputs( @@ -117,7 +111,6 @@ export class TailPhaseManager extends AbstractPhaseManager { nullifierReadRequestHints, nullifierNonExistentReadRequestHints, publicDataHints, - publicDataReadRequestHints, currentState.partial, ); } From 249f683aae98e993661a404850ac4c04345d3117 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Fri, 30 Aug 2024 14:12:56 +0000 Subject: [PATCH 03/14] Remove unnecessary Hash trait implementations. --- .../crates/types/src/abis/public_data_read.nr | 15 ++------------- .../types/src/abis/public_data_update_request.nr | 14 ++------------ .../crates/types/src/contrakt/storage_read.nr | 11 +---------- .../types/src/contrakt/storage_update_request.nr | 11 +---------- 4 files changed, 6 insertions(+), 45 deletions(-) diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_read.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_read.nr index 7354cd51fa10..3cceed1ad156 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_read.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_read.nr @@ -1,9 +1,8 @@ use crate::{ - abis::side_effect::Ordered, address::AztecAddress, - constants::{GENERATOR_INDEX__PUBLIC_DATA_READ, PUBLIC_DATA_READ_LENGTH}, + abis::side_effect::Ordered, address::AztecAddress, constants::PUBLIC_DATA_READ_LENGTH, contrakt::storage_read::StorageRead, data::hash::{compute_public_data_tree_value, compute_public_data_tree_index}, - traits::{Empty, Hash, Serialize, Deserialize} + traits::{Empty, Serialize, Deserialize} }; struct PublicDataRead { @@ -44,16 +43,6 @@ impl Ordered for PublicDataRead { } } -impl Hash for PublicDataRead { - fn hash(self) -> Field { - crate::hash::poseidon2_hash_with_separator([ - self.leaf_slot, - self.value, - self.counter as Field, - ], GENERATOR_INDEX__PUBLIC_DATA_READ) - } -} - impl PublicDataRead { pub fn is_empty(self) -> bool { (self.leaf_slot == 0) & (self.value == 0) & (self.counter == 0) diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_update_request.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_update_request.nr index 93c0b9cbab48..98c50a5125d6 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_update_request.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_update_request.nr @@ -1,10 +1,9 @@ use crate::{ abis::{public_data_read::PublicDataRead, side_effect::{Ordered, Positioned, Readable}}, - address::AztecAddress, - constants::{PUBLIC_DATA_UPDATE_REQUEST_LENGTH, GENERATOR_INDEX__PUBLIC_DATA_UPDATE_REQUEST}, + address::AztecAddress, constants::PUBLIC_DATA_UPDATE_REQUEST_LENGTH, contrakt::storage_update_request::StorageUpdateRequest, data::hash::{compute_public_data_tree_value, compute_public_data_tree_index}, - public_data_tree_leaf::PublicDataTreeLeaf, traits::{Empty, Hash, Serialize, Deserialize} + public_data_tree_leaf::PublicDataTreeLeaf, traits::{Empty, Serialize, Deserialize} }; // TODO: Rename to PublicDataWrite @@ -56,15 +55,6 @@ impl Empty for PublicDataUpdateRequest { } } -impl Hash for PublicDataUpdateRequest { - fn hash(self) -> Field { - crate::hash::poseidon2_hash_with_separator([ - self.leaf_slot, - self.new_value - ], GENERATOR_INDEX__PUBLIC_DATA_UPDATE_REQUEST) - } -} - impl Readable for PublicDataUpdateRequest { fn assert_match_read_request(self, read_request: PublicDataRead) { assert_eq(self.leaf_slot, read_request.leaf_slot, "leaf_slot in PublicDataUpdateRequest does not match read request"); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/contrakt/storage_read.nr b/noir-projects/noir-protocol-circuits/crates/types/src/contrakt/storage_read.nr index 4defafdfae3a..6ed6251c8e37 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/contrakt/storage_read.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/contrakt/storage_read.nr @@ -1,7 +1,4 @@ -use crate::{ - constants::{CONTRACT_STORAGE_READ_LENGTH, GENERATOR_INDEX__PUBLIC_DATA_READ}, - hash::poseidon2_hash_with_separator, traits::{Deserialize, Hash, Empty, Serialize} -}; +use crate::{constants::CONTRACT_STORAGE_READ_LENGTH, traits::{Deserialize, Empty, Serialize}}; struct StorageRead { storage_slot: Field, @@ -25,12 +22,6 @@ impl Empty for StorageRead { } } -impl Hash for StorageRead { - fn hash(self) -> Field { - poseidon2_hash_with_separator(self.serialize(), GENERATOR_INDEX__PUBLIC_DATA_READ) - } -} - impl Serialize for StorageRead { fn serialize(self) -> [Field; CONTRACT_STORAGE_READ_LENGTH] { [self.storage_slot, self.current_value, self.counter as Field] diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/contrakt/storage_update_request.nr b/noir-projects/noir-protocol-circuits/crates/types/src/contrakt/storage_update_request.nr index 569ff8f03e9a..767c22f5116a 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/contrakt/storage_update_request.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/contrakt/storage_update_request.nr @@ -1,7 +1,4 @@ -use crate::{ - constants::{CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH, GENERATOR_INDEX__PUBLIC_DATA_UPDATE_REQUEST}, - hash::poseidon2_hash_with_separator, traits::{Deserialize, Hash, Empty, Serialize} -}; +use crate::{constants::CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH, traits::{Deserialize, Empty, Serialize}}; struct StorageUpdateRequest { storage_slot : Field, @@ -26,12 +23,6 @@ impl Empty for StorageUpdateRequest { } } -impl Hash for StorageUpdateRequest { - fn hash(self) -> Field { - poseidon2_hash_with_separator(self.serialize(), GENERATOR_INDEX__PUBLIC_DATA_UPDATE_REQUEST) - } -} - impl Serialize for StorageUpdateRequest { fn serialize(self) -> [Field; CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH] { [self.storage_slot, self.new_value, self.counter as Field] From 2ee3107292d9bfe3a75865c3e9336802bb0d3590 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Fri, 30 Aug 2024 15:46:32 +0000 Subject: [PATCH 04/14] Update hint builder. --- .../src/utils/arrays/assert_deduped_array.nr | 2 +- .../src/hints/build_public_data_hints.ts | 34 +++++++++++++++---- .../simulator/src/public/hints_builder.ts | 2 +- .../src/public/public_processor.test.ts | 14 ++++---- 4 files changed, 36 insertions(+), 16 deletions(-) diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_deduped_array.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_deduped_array.nr index 1d28473bb942..1debebe520d5 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_deduped_array.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_deduped_array.nr @@ -12,7 +12,7 @@ pub fn assert_deduped_array( num_deduped += 1; } else { assert( - is_empty(deduped_array[N - num_deduped]), "empty items must be padded to the deduped array" + is_empty(deduped_array[N - num_deduped - 1]), "empty items must be padded to the deduped array" ); } } diff --git a/yarn-project/circuits.js/src/hints/build_public_data_hints.ts b/yarn-project/circuits.js/src/hints/build_public_data_hints.ts index 940848ae82e6..341c7f3f0dfe 100644 --- a/yarn-project/circuits.js/src/hints/build_public_data_hints.ts +++ b/yarn-project/circuits.js/src/hints/build_public_data_hints.ts @@ -30,18 +30,40 @@ export async function buildPublicDataHints( publicDataReads: Tuple, publicDataUpdateRequests: Tuple, ): Promise> { - const publicDataLeafSlots = [...publicDataReads, ...publicDataUpdateRequests] + const slotCounterMap = new Map(); + publicDataReads .filter(r => !r.isEmpty()) - .map(r => r.leafSlot.toBigInt()); - const uniquePublicDataLeafSlots = [...new Set(publicDataLeafSlots)]; + .forEach(r => { + if (!r.isEmpty()) { + slotCounterMap.set(r.leafSlot.toBigInt(), 0); + } + }); + publicDataUpdateRequests + .filter(w => !w.isEmpty()) + .forEach(w => { + if (!w.isEmpty()) { + let overrideCounter = slotCounterMap.get(w.leafSlot.toBigInt()) || 0; + if (!overrideCounter || overrideCounter > w.counter) { + overrideCounter = w.counter; + } + slotCounterMap.set(w.leafSlot.toBigInt(), overrideCounter); + } + }); + const uniquePublicDataLeafSlots = [...slotCounterMap.keys()]; - const hints = await Promise.all(uniquePublicDataLeafSlots.map(slot => buildPublicDataHint(oracle, slot))); + const hints = await Promise.all( + uniquePublicDataLeafSlots.map(slot => buildPublicDataHint(oracle, slot, slotCounterMap.get(slot)!)), + ); return padArrayEnd(hints, PublicDataHint.empty(), MAX_PUBLIC_DATA_HINTS); } -export async function buildPublicDataHint(oracle: PublicDataMembershipWitnessOracle, leafSlot: bigint) { +export async function buildPublicDataHint( + oracle: PublicDataMembershipWitnessOracle, + leafSlot: bigint, + overrideCounter: number, +) { const { membershipWitness, leafPreimage } = await oracle.getMatchOrLowPublicDataMembershipWitness(leafSlot); const exists = leafPreimage.slot.toBigInt() === leafSlot; const value = exists ? leafPreimage.value : Fr.ZERO; - return new PublicDataHint(new Fr(leafSlot), value, 0, membershipWitness, leafPreimage); + return new PublicDataHint(new Fr(leafSlot), value, overrideCounter, membershipWitness, leafPreimage); } diff --git a/yarn-project/simulator/src/public/hints_builder.ts b/yarn-project/simulator/src/public/hints_builder.ts index d8488426cfd3..caaed1965e70 100644 --- a/yarn-project/simulator/src/public/hints_builder.ts +++ b/yarn-project/simulator/src/public/hints_builder.ts @@ -56,7 +56,7 @@ export class HintsBuilder { getPublicDataHint(dataAction: PublicDataRead | PublicDataUpdateRequest | bigint) { const slot = typeof dataAction === 'bigint' ? dataAction : dataAction.leafSlot.toBigInt(); - return buildPublicDataHint(this, slot); + return buildPublicDataHint(this, slot, 0); } async getNullifierMembershipWitness(nullifier: Fr) { diff --git a/yarn-project/simulator/src/public/public_processor.test.ts b/yarn-project/simulator/src/public/public_processor.test.ts index 1b79581195b4..7778147a3ef0 100644 --- a/yarn-project/simulator/src/public/public_processor.test.ts +++ b/yarn-project/simulator/src/public/public_processor.test.ts @@ -396,13 +396,11 @@ describe('public_processor', () => { computePublicDataTreeLeafSlot(nonRevertibleRequests[0].callContext.storageContractAddress, contractSlotA), fr(0x101), ), - new PublicDataWrite(computePublicDataTreeLeafSlot(nestedContractAddress, contractSlotE), fr(0x301)), new PublicDataWrite(computePublicDataTreeLeafSlot(nestedContractAddress, contractSlotC), fr(0x201)), - new PublicDataWrite(computePublicDataTreeLeafSlot(nestedContractAddress, contractSlotF), fr(0x351)), new PublicDataWrite(computePublicDataTreeLeafSlot(nestedContractAddress, contractSlotD), fr(0x251)), + new PublicDataWrite(computePublicDataTreeLeafSlot(nestedContractAddress, contractSlotE), fr(0x301)), + new PublicDataWrite(computePublicDataTreeLeafSlot(nestedContractAddress, contractSlotF), fr(0x351)), ]; - // sort increasing by leafIndex - expectedWrites.sort((a, b) => (a.leafIndex.lt(b.leafIndex) ? -1 : 1)); expect(txEffect.publicDataWrites.slice(0, numPublicDataWrites)).toEqual(expectedWrites); // we keep the non-revertible logs @@ -559,7 +557,7 @@ describe('public_processor', () => { PublicExecutionResultBuilder.fromFunctionCall({ from: teardownRequest.callContext.storageContractAddress, tx: makeFunctionCall('', nestedContractAddress, makeSelector(5)), - contractStorageUpdateRequests: [new ContractStorageUpdateRequest(contractSlotC, fr(0x202), 16)], + contractStorageUpdateRequests: [new ContractStorageUpdateRequest(contractSlotC, fr(0x202), 17)], revertReason: new SimulationError('Simulation Failed', []), }).build(teardownResultSettings), ], @@ -598,12 +596,12 @@ describe('public_processor', () => { const numPublicDataWrites = 3; expect(arrayNonEmptyLength(txEffect.publicDataWrites, PublicDataWrite.isEmpty)).toBe(numPublicDataWrites); expect(txEffect.publicDataWrites.slice(0, numPublicDataWrites)).toEqual([ - new PublicDataWrite(computePublicDataTreeLeafSlot(nestedContractAddress, contractSlotB), fr(0x151)), new PublicDataWrite( computePublicDataTreeLeafSlot(nonRevertibleRequests[0].callContext.storageContractAddress, contractSlotA), fr(0x101), ), new PublicDataWrite(computePublicDataTreeLeafSlot(nestedContractAddress, contractSlotA), fr(0x102)), + new PublicDataWrite(computePublicDataTreeLeafSlot(nestedContractAddress, contractSlotB), fr(0x151)), ]); // we keep the non-revertible logs @@ -714,12 +712,12 @@ describe('public_processor', () => { const numPublicDataWrites = 3; expect(arrayNonEmptyLength(txEffect.publicDataWrites, PublicDataWrite.isEmpty)).toBe(numPublicDataWrites); expect(txEffect.publicDataWrites.slice(0, numPublicDataWrites)).toEqual([ - new PublicDataWrite(computePublicDataTreeLeafSlot(nestedContractAddress, contractSlotB), fr(0x151)), new PublicDataWrite( computePublicDataTreeLeafSlot(nonRevertibleRequests[0].callContext.storageContractAddress, contractSlotA), fr(0x101), ), new PublicDataWrite(computePublicDataTreeLeafSlot(nestedContractAddress, contractSlotA), fr(0x102)), + new PublicDataWrite(computePublicDataTreeLeafSlot(nestedContractAddress, contractSlotB), fr(0x151)), ]); // we keep the non-revertible logs @@ -892,8 +890,8 @@ describe('public_processor', () => { expect(arrayNonEmptyLength(txEffect.publicDataWrites, PublicDataWrite.isEmpty)).toEqual(numPublicDataWrites); expect(txEffect.publicDataWrites.slice(0, numPublicDataWrites)).toEqual([ new PublicDataWrite(computePublicDataTreeLeafSlot(nestedContractAddress, contractSlotB), fr(0x152)), - new PublicDataWrite(computePublicDataTreeLeafSlot(nestedContractAddress, contractSlotC), fr(0x201)), new PublicDataWrite(computePublicDataTreeLeafSlot(nestedContractAddress, contractSlotA), fr(0x103)), + new PublicDataWrite(computePublicDataTreeLeafSlot(nestedContractAddress, contractSlotC), fr(0x201)), ]); expect(txEffect.encryptedLogs.getTotalLogCount()).toBe(0); expect(txEffect.unencryptedLogs.getTotalLogCount()).toBe(0); From 2b0c7082e47ec27e61d9e6d299a5b4613717ab4e Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Mon, 2 Sep 2024 14:59:48 +0000 Subject: [PATCH 05/14] Build public data hints in noir. --- .../src/oracle/get_public_data_witness.nr | 2 +- .../components/public_tail_output_composer.nr | 33 ++-- .../combine_data.nr | 4 +- ...combined_overridable_public_data_writes.nr | 29 ---- ...generate_overridable_public_data_writes.nr | 61 +++++++ .../generate_public_data_leaves.nr | 74 +++++++++ .../public_tail_output_validator.nr | 53 ++++-- .../validate_linked_public_data_writes.nr | 80 ++------- .../validate_public_data_hints.nr | 63 ------- .../validate_public_data_leaves.nr | 93 +++++++++++ .../src/public_kernel_tail.nr | 155 ++++++++---------- .../src/public_data_read_request_hints.nr | 11 +- .../public_validation_request_processor.nr | 17 +- .../src/reset/mutable_data_read_request.nr | 4 +- .../rollup-lib/src/base/base_rollup_inputs.nr | 7 +- .../src/abis/public_data_update_request.nr | 23 +-- .../types/src/abis/public_data_write.nr | 11 +- .../crates/types/src/data/mod.nr | 8 + .../crates/types/src/data/public_data_hint.nr | 17 +- .../types/src/data/public_data_leaf_hint.nr | 18 ++ .../types/src/data/public_data_tree_leaf.nr | 55 +++++++ .../public_data_tree_leaf_preimage.nr | 0 .../crates/types/src/lib.nr | 6 +- .../crates/types/src/public_data_tree_leaf.nr | 27 --- .../crates/types/src/tests/utils.nr | 16 +- .../crates/types/src/utils/arrays.nr | 15 +- .../src/hints/build_public_data_hints.test.ts | 119 ++++++-------- .../src/hints/build_public_data_hints.ts | 47 ++---- .../hints/build_transient_data_hints.test.ts | 8 +- yarn-project/circuits.js/src/structs/index.ts | 1 + ...blic_kernel_tail_circuit_private_inputs.ts | 6 +- .../src/structs/public_data_leaf_hint.ts | 28 ++++ .../circuits.js/src/tests/factories.ts | 4 +- .../src/type_conversion.ts | 11 +- .../simulator/src/public/hints_builder.ts | 2 +- 35 files changed, 609 insertions(+), 499 deletions(-) delete mode 100644 noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_combined_overridable_public_data_writes.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_overridable_public_data_writes.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_public_data_leaves.nr delete mode 100644 noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_hints.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_leaves.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/data/public_data_leaf_hint.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/data/public_data_tree_leaf.nr rename noir-projects/noir-protocol-circuits/crates/types/src/{ => data}/public_data_tree_leaf_preimage.nr (100%) delete mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/public_data_tree_leaf.nr create mode 100644 yarn-project/circuits.js/src/structs/public_data_leaf_hint.ts diff --git a/noir-projects/aztec-nr/aztec/src/oracle/get_public_data_witness.nr b/noir-projects/aztec-nr/aztec/src/oracle/get_public_data_witness.nr index 89ac1f843647..6ac80d267dc4 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/get_public_data_witness.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/get_public_data_witness.nr @@ -1,5 +1,5 @@ use dep::protocol_types::{ - constants::PUBLIC_DATA_TREE_HEIGHT, public_data_tree_leaf_preimage::PublicDataTreeLeafPreimage, + constants::PUBLIC_DATA_TREE_HEIGHT, data::PublicDataTreeLeafPreimage, utils::arr_copy_slice }; diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer.nr index 9b369e5cd011..c9641eef4d2a 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer.nr @@ -1,41 +1,54 @@ mod combine_data; -mod generate_combined_overridable_public_data_writes; +mod generate_overridable_public_data_writes; +mod generate_public_data_leaves; use crate::components::public_tail_output_composer::{ combine_data::combine_data, - generate_combined_overridable_public_data_writes::generate_combined_overridable_public_data_writes + generate_overridable_public_data_writes::{generate_overridable_public_data_writes, LinkedIndexHint}, + generate_public_data_leaves::generate_public_data_leaves }; use dep::types::{ abis::{ kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PublicKernelCircuitPublicInputs}, public_data_write::OverridablePublicDataWrite }, - constants::MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, partial_state_reference::PartialStateReference + constants::MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + data::{OverridablePublicDataTreeLeaf, PublicDataLeafHint}, + partial_state_reference::PartialStateReference, utils::arrays::array_merge }; -struct PublicTailOutputComposer { +struct PublicTailOutputComposer { previous_kernel: PublicKernelCircuitPublicInputs, start_state: PartialStateReference, - overridable_public_data_writes: [OverridablePublicDataWrite; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + public_data_writes: [OverridablePublicDataWrite; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + public_data_leaves: [OverridablePublicDataTreeLeaf; NUM_HINTS], + public_data_linked_index_hints: [LinkedIndexHint; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], } -impl PublicTailOutputComposer { +impl PublicTailOutputComposer { pub fn new( previous_kernel: PublicKernelCircuitPublicInputs, - start_state: PartialStateReference + start_state: PartialStateReference, + public_data_leaf_hints: [PublicDataLeafHint; NUM_HINTS] ) -> Self { - let overridable_public_data_writes = generate_combined_overridable_public_data_writes( + let combined_writes = array_merge( previous_kernel.end_non_revertible.public_data_update_requests, previous_kernel.end.public_data_update_requests ); - PublicTailOutputComposer { previous_kernel, start_state, overridable_public_data_writes } + let public_data_leaves = generate_public_data_leaves( + previous_kernel.validation_requests.public_data_reads, + combined_writes, + public_data_leaf_hints + ); + let (public_data_writes, public_data_linked_index_hints) = generate_overridable_public_data_writes(combined_writes, public_data_leaves); + PublicTailOutputComposer { previous_kernel, start_state, public_data_writes, public_data_leaves, public_data_linked_index_hints } } pub fn finish(self) -> KernelCircuitPublicInputs { let end = combine_data( self.previous_kernel.end_non_revertible, self.previous_kernel.end, - self.overridable_public_data_writes + self.public_data_writes ); KernelCircuitPublicInputs { diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/combine_data.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/combine_data.nr index 3fab33176475..1831921d2ab1 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/combine_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/combine_data.nr @@ -11,7 +11,7 @@ use dep::types::{ unconstrained pub fn combine_data( non_revertible: PublicAccumulatedData, revertible: PublicAccumulatedData, - overridable_public_data_writes: [OverridablePublicDataWrite; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] + public_data_writes: [OverridablePublicDataWrite; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] ) -> CombinedAccumulatedData { let mut note_hashes = [0; MAX_NOTE_HASHES_PER_TX]; let sorted_unsiloed_note_hashes = sort_by_counter_asc(array_merge(non_revertible.note_hashes, revertible.note_hashes)); @@ -30,7 +30,7 @@ unconstrained pub fn combine_data( let l2_to_l1_msgs = sort_by_counter_asc(array_merge(non_revertible.l2_to_l1_msgs, revertible.l2_to_l1_msgs)); - let public_data_update_requests = dedupe_array(overridable_public_data_writes); + let public_data_update_requests = dedupe_array(public_data_writes); let note_encrypted_logs_hashes = sort_by_counter_asc( array_merge( diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_combined_overridable_public_data_writes.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_combined_overridable_public_data_writes.nr deleted file mode 100644 index 277ab9a9f1a6..000000000000 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_combined_overridable_public_data_writes.nr +++ /dev/null @@ -1,29 +0,0 @@ -use dep::types::{ - abis::{public_data_update_request::PublicDataUpdateRequest, public_data_write::OverridablePublicDataWrite}, - utils::arrays::array_merge -}; - -unconstrained pub fn generate_combined_overridable_public_data_writes( - non_revertible: [PublicDataUpdateRequest; N], - revertible: [PublicDataUpdateRequest; N] -) -> [OverridablePublicDataWrite; N] { - let mut overridable_public_data_writes = array_merge(non_revertible, revertible).map(|write| OverridablePublicDataWrite { write, override_counter: 0 }); - - for i in 0..overridable_public_data_writes.len() { - let write = overridable_public_data_writes[i].write; - if write.counter != 0 { - let mut override_counter = 0; - for j in 0..overridable_public_data_writes.len() { - let other = overridable_public_data_writes[j].write; - if (other.leaf_slot == write.leaf_slot) - & (other.counter > write.counter) - & ((override_counter == 0) | (other.counter < override_counter)) { - override_counter = other.counter; - } - } - overridable_public_data_writes[i].override_counter = override_counter; - } - } - - overridable_public_data_writes -} diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_overridable_public_data_writes.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_overridable_public_data_writes.nr new file mode 100644 index 000000000000..4a68320f4a52 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_overridable_public_data_writes.nr @@ -0,0 +1,61 @@ +use dep::types::{ + abis::{public_data_update_request::PublicDataUpdateRequest, public_data_write::OverridablePublicDataWrite}, + data::OverridablePublicDataTreeLeaf, traits::Empty, utils::arrays::{array_length, find_index_hint} +}; + +struct LinkedIndexHint { + is_first_write: bool, + prev_index: u32, + next_index: u32, +} + +impl Empty for LinkedIndexHint { + fn empty() -> Self { + LinkedIndexHint { is_first_write: false, prev_index: 0, next_index: 0 } + } +} + +unconstrained pub fn generate_overridable_public_data_writes( + public_data_writes: [PublicDataUpdateRequest; NUM_WRITES], + public_data_leaves: [OverridablePublicDataTreeLeaf; NUM_LEAVES] +) -> ([OverridablePublicDataWrite; NUM_WRITES], [LinkedIndexHint; NUM_WRITES]) { + let mut overridable_public_data_writes = [OverridablePublicDataWrite::empty(); NUM_WRITES]; + let mut hints = [LinkedIndexHint::empty(); NUM_WRITES]; + + let writes_len = array_length(public_data_writes); + for i in 0..writes_len { + let write = public_data_writes[i]; + let mut override_counter = 0; + let mut is_first_write = false; + let mut prev_index = 0; + let mut prev_counter = 0; + let mut next_index = 0; + + for j in 0..writes_len { + let other = public_data_writes[j]; + if (j != i) & (other.leaf_slot == write.leaf_slot) { + if other.counter > write.counter { + if (override_counter == 0) | (other.counter < override_counter) { + override_counter = other.counter; + next_index = j; + } + } else if other.counter < write.counter { + if other.counter > prev_counter { + prev_counter = other.counter; + prev_index = j; + } + } + } + } + + if prev_counter == 0 { + is_first_write = true; + prev_index = find_index_hint(public_data_leaves, |leaf: OverridablePublicDataTreeLeaf| leaf.leaf.slot == write.leaf_slot); + } + + overridable_public_data_writes[i] = OverridablePublicDataWrite { write, override_counter }; + hints[i] = LinkedIndexHint { is_first_write, prev_index, next_index }; + } + + (overridable_public_data_writes, hints) +} diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_public_data_leaves.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_public_data_leaves.nr new file mode 100644 index 000000000000..c1bdc0fe7cee --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_public_data_leaves.nr @@ -0,0 +1,74 @@ +use dep::types::{ + abis::{public_data_read::PublicDataRead, public_data_update_request::PublicDataUpdateRequest}, + data::{OverridablePublicDataTreeLeaf, PublicDataLeafHint, PublicDataTreeLeaf}, + utils::arrays::get_sorted_tuple +}; + +struct SlotIndex { + slot: Field, + index: u32, + counter: u32, +} + +fn compare_by_slot_then_index(a: SlotIndex, b: SlotIndex) -> bool { + if a.slot == b.slot { + (a.index == 0) | (b.index > a.index) + } else { + (b.slot == 0) | ((a.slot != 0) & a.slot.lt(b.slot)) + } +} + +fn compare_by_index(a: SlotIndex, b: SlotIndex) -> bool { + (b.slot == 0) | ((a.slot != 0) & (a.index < b.index)) +} + +unconstrained pub fn generate_public_data_leaves( + reads: [PublicDataRead; NUM_READS], + writes: [PublicDataUpdateRequest; NUM_WRITES], + hints: [PublicDataLeafHint; NUM_HINTS] +) -> [OverridablePublicDataTreeLeaf; NUM_HINTS] { + let mut slot_indexes: BoundedVec = BoundedVec::new(); + for i in 0..reads.len() { + let read = reads[i]; + if read.leaf_slot != 0 { + slot_indexes.push(SlotIndex { slot: read.leaf_slot, index: i, counter: 0 }); + } + } + for i in 0..writes.len() { + let write = writes[i]; + if write.leaf_slot != 0 { + slot_indexes.push(SlotIndex { slot: write.leaf_slot, index: i + NUM_READS, counter: write.counter }); + } + } + let sorted_slot_indexes = slot_indexes.storage.sort_via(compare_by_slot_then_index); + + let mut prev_slot = 0; + let mut override_counter = 0; + let mut unique_slot_indexes: BoundedVec = BoundedVec::new(); + for sorted in sorted_slot_indexes { + if sorted.slot != 0 { + if sorted.slot != prev_slot { + unique_slot_indexes.push(sorted); + prev_slot = sorted.slot; + override_counter = 0; + } + if (sorted.index >= NUM_READS) + & ((override_counter == 0) | (sorted.counter < override_counter)) { + override_counter = sorted.counter; + unique_slot_indexes.storage[unique_slot_indexes.len() - 1].counter = override_counter; + } + } + } + + let sorted_unique_slot_indexes = unique_slot_indexes.storage.sort_via(compare_by_index); + let mut leaves = [OverridablePublicDataTreeLeaf::empty(); NUM_HINTS]; + for i in 0..unique_slot_indexes.len() { + let sorted = sorted_unique_slot_indexes[i]; + let hint = hints[i]; + let exists = hint.preimage.slot == sorted.slot; + let value = if exists { hint.preimage.value } else { 0 }; + leaves[i] = OverridablePublicDataTreeLeaf { leaf: PublicDataTreeLeaf { slot: sorted.slot, value }, override_counter: sorted.counter }; + } + + leaves +} diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator.nr index 46e609498960..15e309aad69d 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator.nr @@ -1,11 +1,14 @@ mod output_hints; mod validate_linked_public_data_writes; -mod validate_public_data_hints; +mod validate_public_data_leaves; -use crate::components::public_tail_output_validator::{ +use crate::components::{ + public_tail_output_validator::{ output_hints::{generate_output_hints, OutputHints, SiloedNoteHashHint}, validate_linked_public_data_writes::validate_linked_public_data_writes, - validate_public_data_hints::validate_public_data_hints + validate_public_data_leaves::validate_public_data_leaves +}, + public_tail_output_composer::LinkedIndexHint }; use dep::types::{ abis::{ @@ -13,9 +16,9 @@ use dep::types::{ log_hash::{LogHash, ScopedLogHash}, note_hash::{NoteHash, ScopedNoteHash}, nullifier::Nullifier, public_data_update_request::PublicDataUpdateRequest, public_data_write::OverridablePublicDataWrite }, - constants::MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, data::public_data_hint::PublicDataHint, - hash::silo_note_hash, messaging::l2_to_l1_message::ScopedL2ToL1Message, - partial_state_reference::PartialStateReference, + constants::MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + data::{OverridablePublicDataTreeLeaf, PublicDataLeafHint}, hash::silo_note_hash, + messaging::l2_to_l1_message::ScopedL2ToL1Message, partial_state_reference::PartialStateReference, utils::arrays::{ array_length, assert_combined_sorted_transformed_value_array_asc, assert_combined_transformed_array, assert_deduped_array @@ -26,8 +29,10 @@ struct PublicTailOutputValidator { output: KernelCircuitPublicInputs, previous_kernel: PublicKernelCircuitPublicInputs, start_state: PartialStateReference, - overridable_public_data_writes: [OverridablePublicDataWrite; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - public_data_hints: [PublicDataHint; N], + public_data_writes: [OverridablePublicDataWrite; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + public_data_leaves: [OverridablePublicDataTreeLeaf; N], + public_data_linked_index_hints: [LinkedIndexHint; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + public_data_leaf_hints: [PublicDataLeafHint; N], hints: OutputHints } @@ -36,11 +41,22 @@ impl PublicTailOutputValidator { output: KernelCircuitPublicInputs, previous_kernel: PublicKernelCircuitPublicInputs, start_state: PartialStateReference, - overridable_public_data_writes: [OverridablePublicDataWrite; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - public_data_hints: [PublicDataHint; N] + public_data_writes: [OverridablePublicDataWrite; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + public_data_leaves: [OverridablePublicDataTreeLeaf; N], + public_data_linked_index_hints: [LinkedIndexHint; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + public_data_leaf_hints: [PublicDataLeafHint; N] ) -> Self { let hints = generate_output_hints(previous_kernel, output); - PublicTailOutputValidator { output, previous_kernel, start_state, overridable_public_data_writes, public_data_hints, hints } + PublicTailOutputValidator { + output, + previous_kernel, + start_state, + public_data_writes, + public_data_leaves, + public_data_linked_index_hints, + public_data_leaf_hints, + hints + } } pub fn validate(self) { @@ -154,22 +170,27 @@ impl PublicTailOutputValidator { } fn validate_deduped_public_data_writes(self) { - validate_public_data_hints( - self.public_data_hints, + validate_public_data_leaves( + self.public_data_leaves, + self.public_data_leaf_hints, self.start_state.public_data_tree.root ); assert_combined_transformed_array( self.previous_kernel.end_non_revertible.public_data_update_requests, self.previous_kernel.end.public_data_update_requests, - self.overridable_public_data_writes, + self.public_data_writes, |from: PublicDataUpdateRequest, to: OverridablePublicDataWrite| to.write == from // override_counter is checked in validate_linked_public_data_writes. ); - validate_linked_public_data_writes(self.overridable_public_data_writes, self.public_data_hints); + validate_linked_public_data_writes( + self.public_data_writes, + self.public_data_leaves, + self.public_data_linked_index_hints + ); assert_deduped_array( - self.overridable_public_data_writes, + self.public_data_writes, self.output.end.public_data_update_requests ); } diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_linked_public_data_writes.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_linked_public_data_writes.nr index 0f9235318b81..62757b84abd7 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_linked_public_data_writes.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_linked_public_data_writes.nr @@ -1,75 +1,25 @@ -use dep::types::{ - abis::public_data_write::OverridablePublicDataWrite, data::public_data_hint::PublicDataHint, - traits::Empty, utils::arrays::find_index_hint -}; +use crate::components::public_tail_output_composer::LinkedIndexHint; +use dep::types::{abis::public_data_write::OverridablePublicDataWrite, data::OverridablePublicDataTreeLeaf}; -struct LinkedIndexHint { - is_first_write: bool, - prev_index: u32, - next_index: u32, -} - -impl Empty for LinkedIndexHint { - fn empty() -> Self { - LinkedIndexHint { is_first_write: false, prev_index: 0, next_index: 0 } - } -} - -unconstrained fn generate_linked_index_hints( - overridable_public_data_writes: [OverridablePublicDataWrite; NUM_WRITES], - public_leaf_data: [PublicDataHint; NUM_DATA] -) -> [LinkedIndexHint; NUM_WRITES] { - let mut hints = [LinkedIndexHint::empty(); NUM_WRITES]; - - for i in 0..overridable_public_data_writes.len() { - let write = overridable_public_data_writes[i]; - if write.counter() != 0 { - let mut is_first_write = false; - let mut prev_index = 0; - let mut prev_counter = 0; - let mut next_index = 0; - for j in 0..overridable_public_data_writes.len() { - let other = overridable_public_data_writes[j].write; - if (j != i) & (other.leaf_slot == write.inner().leaf_slot) { - if other.counter == write.override_counter { - next_index = j; - } else if ((other.counter < write.counter()) - & ((prev_counter == 0) | (other.counter > prev_counter))) { - prev_counter = other.counter; - prev_index = j; - } - } - } - if prev_counter == 0 { - is_first_write = true; - prev_index = find_index_hint(public_leaf_data, |leaf: PublicDataHint| leaf.leaf_slot == write.inner().leaf_slot); - } - hints[i] = LinkedIndexHint { is_first_write, prev_index, next_index }; - } - } - - hints -} - -fn validate_linked_public_data_writes_with_hints( - overridable_public_data_writes: [OverridablePublicDataWrite; NUM_WRITES], - public_leaf_data: [PublicDataHint; NUM_DATA], +fn validate_linked_public_data_writes( + public_data_writes: [OverridablePublicDataWrite; NUM_WRITES], + public_data_leaves: [OverridablePublicDataTreeLeaf; NUM_LEAVES], hints: [LinkedIndexHint; NUM_WRITES] ) { - for i in 0..overridable_public_data_writes.len() { - let write = overridable_public_data_writes[i]; + for i in 0..public_data_writes.len() { + let write = public_data_writes[i]; let hint = hints[i]; if write.counter() != 0 { if hint.is_first_write { // It's linked to a leaf in the tree. - let data_hint = public_leaf_data[hint.prev_index]; - assert_eq(data_hint.leaf_slot, write.inner().leaf_slot, "hinted data has different leaf_slot"); + let data_hint = public_data_leaves[hint.prev_index]; + assert_eq(data_hint.leaf.slot, write.inner().leaf_slot, "hinted data has different leaf_slot"); assert_eq( data_hint.override_counter, write.counter(), "hinted data does not link to current write" ); } else { // It's linked to another write for the same leaf slot. - let prev = overridable_public_data_writes[hint.prev_index]; + let prev = public_data_writes[hint.prev_index]; assert_eq( prev.inner().leaf_slot, write.inner().leaf_slot, "hinted write has different leaf_slot" ); @@ -81,7 +31,7 @@ fn validate_linked_public_data_writes_with_hints( - overridable_public_data_writes: [OverridablePublicDataWrite; NUM_WRITES], - public_leaf_data: [PublicDataHint; NUM_DATA] -) { - let hints = generate_linked_index_hints(overridable_public_data_writes, public_leaf_data); - validate_linked_public_data_writes_with_hints(overridable_public_data_writes, public_leaf_data, hints); -} diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_hints.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_hints.nr deleted file mode 100644 index b5035269a243..000000000000 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_hints.nr +++ /dev/null @@ -1,63 +0,0 @@ -use dep::types::{ - abis::public_data_read::PublicDataRead, data::public_data_hint::PublicDataHint, - utils::arrays::get_sorted_tuple::get_sorted_tuple -}; - -fn sort_by_leaf_slot(a: PublicDataHint, b: PublicDataHint) -> bool { - (b.leaf_slot == 0) | ((a.leaf_slot != 0) & a.leaf_slot.lt(b.leaf_slot)) -} - -unconstrained fn generate_unique_leaf_slot_hints(public_data_hints: [PublicDataHint; N]) -> ([Field; N], [u32; N]) { - let mut sorted_leaf_slots = [0; N]; - let mut sorted_leaf_slot_indexes = [N - 1; N]; - - let sorted_tuples = get_sorted_tuple(public_data_hints, sort_by_leaf_slot); - for i in 0..N { - let elem = sorted_tuples[i].elem; - sorted_leaf_slots[i] = elem.leaf_slot; - - let original_index = sorted_tuples[i].original_index; - sorted_leaf_slot_indexes[original_index] = i; - } - - (sorted_leaf_slots, sorted_leaf_slot_indexes) -} - -// All non-zero leaf slots in public_data_hints must be unique, ensuring that writes with the same leaf slot are grouped into a single group. -fn validate_unique_leaf_slots( - public_data_hints: [PublicDataHint; N], - sorted_leaf_slots: [Field; N], - sorted_leaf_slot_indexes: [u32; N] -) { - let mut prev_slot = 0; - let mut num_non_zeros = 0; - for i in 0..N { - let data = public_data_hints[i]; - let sorted_index = if data.leaf_slot != 0 { - sorted_leaf_slot_indexes[i] - } else { - num_non_zeros += 1; - N - num_non_zeros - }; - let hinted_leaf_slot = sorted_leaf_slots[sorted_index]; - assert_eq(hinted_leaf_slot, data.leaf_slot); - - let curr_leaf_alot = sorted_leaf_slots[i]; - if data.leaf_slot != 0 { - assert(prev_slot.lt(curr_leaf_alot)); - } else { - assert_eq(curr_leaf_alot, 0); - } - prev_slot = curr_leaf_alot; - } -} - -pub fn validate_public_data_hints(public_data_hints: [PublicDataHint; N], tree_root: Field) { - let (sorted_leaf_slots, sorted_leaf_slot_indexes) = generate_unique_leaf_slot_hints(public_data_hints); - validate_unique_leaf_slots(public_data_hints, sorted_leaf_slots, sorted_leaf_slot_indexes); - - // validate membership - for i in 0..public_data_hints.len() { - public_data_hints[i].validate(tree_root); - } -} diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_leaves.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_leaves.nr new file mode 100644 index 000000000000..2fb6387bce6a --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_leaves.nr @@ -0,0 +1,93 @@ +use dep::types::{ + abis::public_data_read::PublicDataRead, data::{OverridablePublicDataTreeLeaf, PublicDataLeafHint}, + merkle_tree::conditionally_assert_check_membership, + utils::arrays::get_sorted_tuple::get_sorted_tuple +}; + +fn sort_by_leaf_slot(a: OverridablePublicDataTreeLeaf, b: OverridablePublicDataTreeLeaf) -> bool { + (b.leaf.slot == 0) | ((a.leaf.slot != 0) & a.leaf.slot.lt(b.leaf.slot)) +} + +unconstrained fn generate_unique_leaf_slot_hints(leaves: [OverridablePublicDataTreeLeaf; N]) -> ([Field; N], [u32; N]) { + let mut sorted_leaf_slots = [0; N]; + let mut sorted_leaf_slot_indexes = [N - 1; N]; + + let sorted_tuples = get_sorted_tuple(leaves, sort_by_leaf_slot); + for i in 0..N { + let elem = sorted_tuples[i].elem; + sorted_leaf_slots[i] = elem.leaf.slot; + + let original_index = sorted_tuples[i].original_index; + sorted_leaf_slot_indexes[original_index] = i; + } + + (sorted_leaf_slots, sorted_leaf_slot_indexes) +} + +// All non-zero leaf slots in leaves must be unique, ensuring that writes with the same leaf slot are grouped into a single group. +fn validate_unique_leaf_slots( + leaves: [OverridablePublicDataTreeLeaf; N], + sorted_leaf_slots: [Field; N], + sorted_leaf_slot_indexes: [u32; N] +) { + let mut prev_slot = 0; + let mut num_non_zeros = 0; + for i in 0..N { + let leaf = leaves[i].leaf; + let sorted_index = if leaf.slot != 0 { + sorted_leaf_slot_indexes[i] + } else { + num_non_zeros += 1; + N - num_non_zeros + }; + let hinted_leaf_slot = sorted_leaf_slots[sorted_index]; + assert_eq(hinted_leaf_slot, leaf.slot); + + let curr_leaf_alot = sorted_leaf_slots[i]; + if leaf.slot != 0 { + assert(prev_slot.lt(curr_leaf_alot)); + } else { + assert_eq(curr_leaf_alot, 0); + } + prev_slot = curr_leaf_alot; + } +} + +fn validate_memberships( + leaves: [OverridablePublicDataTreeLeaf; NUM_LEAVES], + leaf_hints: [PublicDataLeafHint; NUM_HINTS], + tree_root: Field +) { + for i in 0..leaves.len() { + let leaf = leaves[i].leaf; + let hint = leaf_hints[i]; + if leaf.slot != 0 { + let exists_in_tree = leaf.slot == hint.preimage.slot; + if exists_in_tree { + assert( + leaf.value == hint.preimage.value, "Hinted public data value does not match the value in leaf preimage" + ); + } else { + assert(leaf.value == 0, "Value must be 0 for non-existent public data"); + } + + conditionally_assert_check_membership( + leaf.slot, + exists_in_tree, + hint.preimage, + hint.membership_witness, + tree_root + ); + } + } +} + +pub fn validate_public_data_leaves( + leaves: [OverridablePublicDataTreeLeaf; NUM_LEAVES], + leaf_hints: [PublicDataLeafHint; NUM_HINTS], + tree_root: Field +) { + let (sorted_leaf_slots, sorted_leaf_slot_indexes) = generate_unique_leaf_slot_hints(leaves); + validate_unique_leaf_slots(leaves, sorted_leaf_slots, sorted_leaf_slot_indexes); + validate_memberships(leaves, leaf_hints, tree_root); +} diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr index 3702be63a560..2d3db2c79910 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr @@ -1,7 +1,7 @@ use crate::{ components::{ previous_kernel_validator::PreviousKernelValidator, - public_tail_output_composer::PublicTailOutputComposer, + public_tail_output_composer::{LinkedIndexHint, PublicTailOutputComposer}, public_tail_output_validator::PublicTailOutputValidator }, public_kernel_phase::PublicKernelPhase @@ -16,7 +16,8 @@ use dep::types::{ MAX_PUBLIC_DATA_HINTS, MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, PUBLIC_KERNEL_SETUP_INDEX, PUBLIC_KERNEL_APP_LOGIC_INDEX, PUBLIC_KERNEL_TEARDOWN_INDEX }, - data::public_data_hint::PublicDataHint, partial_state_reference::PartialStateReference + data::{OverridablePublicDataTreeLeaf, PublicDataLeafHint}, + partial_state_reference::PartialStateReference }; global ALLOWED_PREVIOUS_CIRCUITS = [ @@ -29,14 +30,20 @@ struct PublicKernelTailCircuitPrivateInputs { previous_kernel: PublicKernelData, nullifier_read_request_hints: NullifierReadRequestHints, nullifier_non_existent_read_request_hints: NullifierNonExistentReadRequestHints, - public_data_hints: [PublicDataHint; MAX_PUBLIC_DATA_HINTS], + public_data_hints: [PublicDataLeafHint; MAX_PUBLIC_DATA_HINTS], start_state: PartialStateReference, } impl PublicKernelTailCircuitPrivateInputs { - unconstrained fn generate_output(self) -> (KernelCircuitPublicInputs, [OverridablePublicDataWrite; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]) { - let composer = PublicTailOutputComposer::new(self.previous_kernel.public_inputs, self.start_state); - (composer.finish(), composer.overridable_public_data_writes) + unconstrained fn generate_output(self) -> (KernelCircuitPublicInputs, [OverridablePublicDataWrite; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], [OverridablePublicDataTreeLeaf; MAX_PUBLIC_DATA_HINTS], [LinkedIndexHint; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]) { + let composer = PublicTailOutputComposer::new( + self.previous_kernel.public_inputs, + self.start_state, + self.public_data_hints + ); + ( + composer.finish(), composer.public_data_writes, composer.public_data_leaves, composer.public_data_linked_index_hints + ) } pub fn execute(self) -> KernelCircuitPublicInputs { @@ -44,22 +51,24 @@ impl PublicKernelTailCircuitPrivateInputs { previous_kernel_validator.validate_phase(PublicKernelPhase.TAIL); previous_kernel_validator.validate_proof(ALLOWED_PREVIOUS_CIRCUITS); - let (output, overridable_public_data_writes) = self.generate_output(); + let (output, public_data_writes, public_data_leaves, public_data_linked_index_hints) = self.generate_output(); PublicValidationRequestProcessor::new( self.previous_kernel.public_inputs, self.nullifier_read_request_hints, self.nullifier_non_existent_read_request_hints, self.start_state.nullifier_tree.root, - overridable_public_data_writes, - self.public_data_hints + public_data_writes, + public_data_leaves ).validate(); PublicTailOutputValidator::new( output, self.previous_kernel.public_inputs, self.start_state, - overridable_public_data_writes, + public_data_writes, + public_data_leaves, + public_data_linked_index_hints, self.public_data_hints ).validate(); @@ -92,8 +101,8 @@ mod tests { PUBLIC_KERNEL_APP_LOGIC_INDEX, BASE_ROLLUP_INDEX, PUBLIC_KERNEL_SETUP_INDEX, PUBLIC_KERNEL_TEARDOWN_INDEX }, - data::public_data_hint::PublicDataHint, hash::{compute_siloed_nullifier, silo_note_hash}, - public_data_tree_leaf_preimage::PublicDataTreeLeafPreimage, + data::{PublicDataLeafHint, PublicDataTreeLeafPreimage}, + hash::{compute_siloed_nullifier, silo_note_hash}, tests::{ fixture_builder::FixtureBuilder, merkle_tree_utils::NonEmptyMerkleTree, utils::{assert_array_eq, pad_end, swap_items} @@ -114,10 +123,10 @@ mod tests { ) } - fn build_public_data_tree(public_data_leaves: [PublicDataTreeLeafPreimage; N]) -> NonEmptyMerkleTree { + fn build_public_data_tree(leaf_preimages: [PublicDataTreeLeafPreimage; N]) -> NonEmptyMerkleTree { NonEmptyMerkleTree::new( pad_end( - public_data_leaves.map(|preimage: PublicDataTreeLeafPreimage| preimage.hash()), + leaf_preimages.map(|preimage: PublicDataTreeLeafPreimage| preimage.hash()), 0 ), [0; PUBLIC_DATA_TREE_HEIGHT], @@ -131,9 +140,8 @@ mod tests { previous_revertible: FixtureBuilder, nullifier_read_request_hints_builder: NullifierReadRequestHintsBuilder, nullifier_non_existent_read_request_hints_builder: NullifierNonExistentReadRequestHintsBuilder, - public_data_hints: BoundedVec, public_data_tree: NonEmptyMerkleTree, - public_data_leaves: [PublicDataTreeLeafPreimage; N], + public_data_leaf_preimages: [PublicDataTreeLeafPreimage; N], start_state: PartialStateReference, } @@ -148,10 +156,9 @@ mod tests { previous_revertible, nullifier_read_request_hints_builder: NullifierReadRequestHintsBuilder::new(), nullifier_non_existent_read_request_hints_builder, - public_data_hints: BoundedVec::new(), public_data_tree: NonEmptyMerkleTree::empty(), - start_state: PartialStateReference::empty(), - public_data_leaves: pad_end([], PublicDataTreeLeafPreimage::empty()) + public_data_leaf_preimages: pad_end([], PublicDataTreeLeafPreimage::empty()), + start_state: PartialStateReference::empty() } } @@ -165,14 +172,14 @@ mod tests { } pub fn with_public_data_tree(&mut self) -> Self { - let public_data_leaves = [ + let public_data_leaf_preimages = [ PublicDataTreeLeafPreimage { slot: 0, value: 0, next_slot: 1111, next_index: 3 }, PublicDataTreeLeafPreimage { slot: 2222, value: 200, next_slot: 3333, next_index: 2 }, PublicDataTreeLeafPreimage { slot: 3333, value: 300, next_slot: 0, next_index: 0 }, PublicDataTreeLeafPreimage { slot: 1111, value: 100, next_slot: 2222, next_index: 1 } ]; - let public_data_tree = build_public_data_tree(public_data_leaves); - self.public_data_leaves = pad_end(public_data_leaves, PublicDataTreeLeafPreimage::empty()); + let public_data_tree = build_public_data_tree(public_data_leaf_preimages); + self.public_data_leaf_preimages = pad_end(public_data_leaf_preimages, PublicDataTreeLeafPreimage::empty()); self.public_data_tree = public_data_tree; self.start_state.public_data_tree.root = public_data_tree.get_root(); *self @@ -233,60 +240,42 @@ mod tests { self.nullifier_non_existent_read_request_hints_builder.add_value_read(siloed_nullifier); } - pub fn add_public_data_write(&mut self, leaf_slot: Field, value: Field) { - let counter = self.previous_kernel.counter; - self.previous_kernel.add_public_data_update_request(leaf_slot, value); - self.add_public_data_hint(leaf_slot, counter); - } + fn create_public_data_leaf_hint(&mut self, leaf_slot: Field) -> PublicDataLeafHint { + let low_leaf_index = find_index_hint( + self.public_data_leaf_preimages, + |p: PublicDataTreeLeafPreimage| !leaf_slot.lt(p.slot) & (p.next_slot.eq(0) | leaf_slot.lt(p.next_slot)) + ); - pub fn read_public_write(&mut self, leaf_slot: Field, value: Field) { - self.previous_kernel.add_public_data_read_request(leaf_slot, value); - self.add_public_data_hint(leaf_slot, 0); - } + let preimage = self.public_data_leaf_preimages[low_leaf_index]; + let membership_witness = MembershipWitness { + leaf_index: low_leaf_index as Field, + sibling_path: self.public_data_tree.get_sibling_path(low_leaf_index) + }; - pub fn add_read_request_for_empty_slot(&mut self, leaf_slot: Field) { - self.previous_kernel.add_public_data_read_request(leaf_slot, 0); - self.add_public_data_hint(leaf_slot, 0); + PublicDataLeafHint { preimage, membership_witness } } - fn add_public_data_hint(&mut self, leaf_slot: Field, override_counter: u32) { - let existing_index = find_index_hint( - self.public_data_hints.storage, - |d: PublicDataHint| d.leaf_slot == leaf_slot - ); - if existing_index != self.public_data_hints.storage.len() { - let data = self.public_data_hints.storage[existing_index]; - if data.override_counter == 0 { - self.public_data_hints.storage[existing_index].override_counter = override_counter; - } - } else { - let mut low_leaf_index = 0; - let mut low_leaf_slot = 0; - for i in 0..self.public_data_leaves.len() { - let leaf = self.public_data_leaves[i]; - if !leaf_slot.lt(leaf.slot) & low_leaf_slot.lt(leaf.slot) { - low_leaf_index = i; - low_leaf_slot = leaf.slot; - } + fn generate_public_data_leaf_hints(&mut self) -> [PublicDataLeafHint; MAX_PUBLIC_DATA_HINTS] { + let mut public_data_hints = BoundedVec::new(); + let mut unique_slots: BoundedVec = BoundedVec::new(); + + for i in 0..self.previous_kernel.public_data_reads.len() { + let read = self.previous_kernel.public_data_reads.get(i); + if !unique_slots.any(|s| s == read.leaf_slot) { + public_data_hints.push(self.create_public_data_leaf_hint(read.leaf_slot)); + unique_slots.push(read.leaf_slot); } + } - let leaf_preimage = self.public_data_leaves[low_leaf_index]; - let membership_witness = MembershipWitness { - leaf_index: low_leaf_index as Field, - sibling_path: self.public_data_tree.get_sibling_path(low_leaf_index) - }; - - let exists_in_tree = low_leaf_slot == leaf_slot; - let read_value = if exists_in_tree { - leaf_preimage.value - } else { - 0 - }; - - self.public_data_hints.push( - PublicDataHint { leaf_slot, value: read_value, override_counter, membership_witness, leaf_preimage } - ); + for i in 0..self.previous_kernel.public_data_update_requests.len() { + let write = self.previous_kernel.public_data_update_requests.get(i); + if !unique_slots.any(|s| s == write.leaf_slot) { + public_data_hints.push(self.create_public_data_leaf_hint(write.leaf_slot)); + unique_slots.push(write.leaf_slot); + } } + + public_data_hints.storage } fn sync_counters(&mut self) { @@ -304,12 +293,13 @@ mod tests { previous_kernel.public_inputs.end = self.previous_revertible.to_public_accumulated_data(); self.set_nullifiers_for_non_existent_read_request_hints(); + let public_data_hints = self.generate_public_data_leaf_hints(); let kernel = PublicKernelTailCircuitPrivateInputs { previous_kernel, nullifier_read_request_hints: self.nullifier_read_request_hints_builder.to_hints(), nullifier_non_existent_read_request_hints: self.nullifier_non_existent_read_request_hints_builder.to_hints(), - public_data_hints: self.public_data_hints.storage, + public_data_hints, start_state: self.start_state }; @@ -450,24 +440,24 @@ mod tests { unconstrained fn public_data_reads_and_writes_succeeds() { let mut builder = PublicKernelTailCircuitPrivateInputsBuilder::new().with_public_data_tree(); - builder.read_public_write(22, 0); + builder.previous_kernel.add_public_data_read_request(22, 0); - builder.add_public_data_write(11, 500); - builder.add_public_data_write(22, 700); + builder.previous_kernel.add_public_data_update_request(11, 500); + builder.previous_kernel.add_public_data_update_request(22, 700); - builder.read_public_write(22, 700); - builder.read_public_write(11, 500); - builder.read_public_write(3333, 300); + builder.previous_kernel.add_public_data_read_request(22, 700); + builder.previous_kernel.add_public_data_read_request(11, 500); + builder.previous_kernel.add_public_data_read_request(3333, 300); // Override the previous value at leaf slot 22. - builder.add_public_data_write(22, 701); + builder.previous_kernel.add_public_data_update_request(22, 701); // Override the value of the leaf. - builder.add_public_data_write(3333, 301); + builder.previous_kernel.add_public_data_update_request(3333, 301); // Read the new values. - builder.read_public_write(22, 701); - builder.read_public_write(3333, 301); + builder.previous_kernel.add_public_data_read_request(22, 701); + builder.previous_kernel.add_public_data_read_request(3333, 301); let prev_writes = builder.previous_kernel.public_data_update_requests.storage; @@ -478,7 +468,6 @@ mod tests { swap_items(&mut builder.previous_kernel.public_data_reads, 0, 3); let public_inputs = builder.execute(); - assert_array_eq( public_inputs.end.public_data_update_requests, [ @@ -489,11 +478,11 @@ mod tests { ); } - #[test(should_fail_with="value in PublicDataHint does not match read request")] + #[test(should_fail_with="value in OverridablePublicDataTreeLeaf does not match read request")] unconstrained fn reading_uninitialized_public_data_non_zero_value_fails() { let mut builder = PublicKernelTailCircuitPrivateInputsBuilder::new().with_public_data_tree(); - builder.read_public_write(1234, 1); + builder.previous_kernel.add_public_data_read_request(1234, 1); builder.failed(); } diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_data_read_request_hints.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_data_read_request_hints.nr index c9dbbd36276c..3f1eebc06cc4 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_data_read_request_hints.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_data_read_request_hints.nr @@ -1,7 +1,7 @@ use crate::reset::{mutable_data_read_request::ReadIndexHint, read_request::ReadRequestStatus}; use dep::types::{ abis::{public_data_read::PublicDataRead, public_data_write::OverridablePublicDataWrite}, - data::public_data_hint::PublicDataHint, utils::arrays::find_index_hint + data::OverridablePublicDataTreeLeaf, utils::arrays::find_index_hint }; struct PublicDataReadRequestHints { @@ -10,10 +10,10 @@ struct PublicDataReadRequestHints { leaf_data_read_hints: [ReadIndexHint; NUM_READS], } -unconstrained pub fn build_public_data_read_request_hints( +unconstrained pub fn build_public_data_read_request_hints( reads: [PublicDataRead; NUM_READS], writes: [OverridablePublicDataWrite; NUM_WRITES], - leaf_data: [PublicDataHint; NUM_PUBLIC_DATA_HINTS] + leaf_data: [OverridablePublicDataTreeLeaf; NUM_LEAVES] ) -> PublicDataReadRequestHints { let mut read_request_statuses = [ReadRequestStatus::empty(); NUM_READS]; let mut pending_read_hints = [ReadIndexHint::nada(NUM_READS); NUM_READS]; @@ -32,7 +32,10 @@ unconstrained pub fn build_public_data_read_request_hints { +struct PublicValidationRequestProcessor { validation_requests: PublicValidationRequests, pending_nullifiers: [Nullifier; MAX_NULLIFIERS_PER_TX], nullifier_read_request_hints: NullifierReadRequestHints, @@ -28,17 +27,17 @@ struct PublicValidationRequestProcessor, - public_data_hints: [PublicDataHint; NUM_PUBLIC_DATA_HINTS], + public_data_leaves: [OverridablePublicDataTreeLeaf; NUM_PUBLIC_DATA_LEAVES], } -impl PublicValidationRequestProcessor { +impl PublicValidationRequestProcessor { pub fn new( public_inputs: PublicKernelCircuitPublicInputs, nullifier_read_request_hints: NullifierReadRequestHints, nullifier_non_existent_read_request_hints: NullifierNonExistentReadRequestHints, nullifier_tree_root: Field, pending_public_data_writes: [OverridablePublicDataWrite; NUN_PUBLIC_DATA_WRITES], - public_data_hints: [PublicDataHint; NUM_PUBLIC_DATA_HINTS] + public_data_leaves: [OverridablePublicDataTreeLeaf; NUM_PUBLIC_DATA_LEAVES] ) -> Self { let validation_requests = public_inputs.validation_requests; @@ -50,7 +49,7 @@ impl PublicVali let public_data_read_request_hints = build_public_data_read_request_hints( validation_requests.public_data_reads, pending_public_data_writes, - public_data_hints + public_data_leaves ); PublicValidationRequestProcessor { @@ -61,7 +60,7 @@ impl PublicVali nullifier_tree_root, pending_public_data_writes, public_data_read_request_hints, - public_data_hints + public_data_leaves } } @@ -123,7 +122,7 @@ impl PublicVali self.validation_requests.public_data_reads, hints.read_request_statuses, self.pending_public_data_writes, - self.public_data_hints, + self.public_data_leaves, hints.pending_read_hints, hints.leaf_data_read_hints ); diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/mutable_data_read_request.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/mutable_data_read_request.nr index 47df4c7f8a8c..af5b57c1848a 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/mutable_data_read_request.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/mutable_data_read_request.nr @@ -318,7 +318,7 @@ mod tests { builder.validate_pending_read_requests(); } - #[test(should_fail_with="leaf_slot in PublicDataUpdateRequest does not match read request")] + #[test(should_fail_with="leaf_slot in OverridablePublicDataWrite does not match read request")] fn reset_pending_reads_wrong_slot_fails() { let mut builder = TestBuilder::new(); @@ -328,7 +328,7 @@ mod tests { builder.validate_pending_read_requests(); } - #[test(should_fail_with="value in PublicDataUpdateRequest does not match read request")] + #[test(should_fail_with="value in OverridablePublicDataWrite does not match read request")] fn reset_pending_reads_wrong_value_fails() { let mut builder = TestBuilder::new(); diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr index 43345b48155a..48b9e9b4882d 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr @@ -32,8 +32,8 @@ use dep::types::{ append_only_tree, assert_check_membership, calculate_empty_tree_root, calculate_subtree_root, indexed_tree, MembershipWitness }, - partial_state_reference::PartialStateReference, public_data_tree_leaf::PublicDataTreeLeaf, - public_data_tree_leaf_preimage::PublicDataTreeLeafPreimage, traits::is_empty, + partial_state_reference::PartialStateReference, + data::{PublicDataTreeLeaf, PublicDataTreeLeafPreimage}, traits::is_empty, utils::{field::{full_field_less_than, full_field_greater_than}, uint256::U256} }; @@ -484,8 +484,7 @@ mod tests { PUBLIC_KERNEL_TAIL_INDEX }, contract_class_id::ContractClassId, partial_state_reference::PartialStateReference, - public_data_tree_leaf::PublicDataTreeLeaf, - public_data_tree_leaf_preimage::PublicDataTreeLeafPreimage, + data::{PublicDataTreeLeaf, PublicDataTreeLeafPreimage}, tests::{ fixtures, fixture_builder::FixtureBuilder, merkle_tree_utils::{NonEmptyMerkleTree, compute_zero_hashes} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_update_request.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_update_request.nr index 98c50a5125d6..367e8a9db1f7 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_update_request.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_update_request.nr @@ -1,9 +1,11 @@ use crate::{ - abis::{public_data_read::PublicDataRead, side_effect::{Ordered, Positioned, Readable}}, - address::AztecAddress, constants::PUBLIC_DATA_UPDATE_REQUEST_LENGTH, + abis::{side_effect::Ordered}, address::AztecAddress, constants::PUBLIC_DATA_UPDATE_REQUEST_LENGTH, contrakt::storage_update_request::StorageUpdateRequest, - data::hash::{compute_public_data_tree_value, compute_public_data_tree_index}, - public_data_tree_leaf::PublicDataTreeLeaf, traits::{Empty, Serialize, Deserialize} + data::{ + hash::{compute_public_data_tree_value, compute_public_data_tree_index}, + public_data_tree_leaf::PublicDataTreeLeaf +}, + traits::{Empty, Serialize, Deserialize} }; // TODO: Rename to PublicDataWrite @@ -26,12 +28,6 @@ impl PublicDataUpdateRequest { } } -impl Positioned for PublicDataUpdateRequest { - fn position(self) -> Field { - self.leaf_slot - } -} - impl Ordered for PublicDataUpdateRequest { fn counter(self)-> u32{ self.counter @@ -55,13 +51,6 @@ impl Empty for PublicDataUpdateRequest { } } -impl Readable for PublicDataUpdateRequest { - fn assert_match_read_request(self, read_request: PublicDataRead) { - assert_eq(self.leaf_slot, read_request.leaf_slot, "leaf_slot in PublicDataUpdateRequest does not match read request"); - assert_eq(self.new_value, read_request.value, "value in PublicDataUpdateRequest does not match read request"); - } -} - impl From for PublicDataTreeLeaf { fn from(update_request: PublicDataUpdateRequest) -> PublicDataTreeLeaf { PublicDataTreeLeaf { diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_write.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_write.nr index 531a453da3e3..251d31a1af67 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_write.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_write.nr @@ -1,7 +1,7 @@ use crate::{ abis::{ public_data_read::PublicDataRead, public_data_update_request::PublicDataUpdateRequest, - side_effect::{Inner, Ordered, Overridable, Positioned, Readable} + side_effect::{Inner, Ordered, Overridable, Readable} }, traits::Empty }; @@ -32,15 +32,10 @@ impl Ordered for OverridablePublicDataWrite { } } -impl Positioned for OverridablePublicDataWrite { - fn position(self) -> Field { - self.write.leaf_slot - } -} - impl Readable for OverridablePublicDataWrite { fn assert_match_read_request(self, read_request: PublicDataRead) { - self.write.assert_match_read_request(read_request); + assert_eq(self.write.leaf_slot, read_request.leaf_slot, "leaf_slot in OverridablePublicDataWrite does not match read request"); + assert_eq(self.write.new_value, read_request.value, "value in OverridablePublicDataWrite does not match read request"); } } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/data/mod.nr b/noir-projects/noir-protocol-circuits/crates/types/src/data/mod.nr index 5cc038d55a50..44c32c7e867e 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/data/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/data/mod.nr @@ -1,2 +1,10 @@ mod public_data_hint; +mod public_data_leaf_hint; +mod public_data_tree_leaf; +mod public_data_tree_leaf_preimage; mod hash; + +use public_data_hint::PublicDataHint; +use public_data_leaf_hint::PublicDataLeafHint; +use public_data_tree_leaf::{OverridablePublicDataTreeLeaf, PublicDataTreeLeaf}; +use public_data_tree_leaf_preimage::PublicDataTreeLeafPreimage; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/data/public_data_hint.nr b/noir-projects/noir-protocol-circuits/crates/types/src/data/public_data_hint.nr index 90ca69180d44..a544965afa8d 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/data/public_data_hint.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/data/public_data_hint.nr @@ -1,6 +1,6 @@ use crate::{ - abis::{public_data_read::PublicDataRead, side_effect::{Overridable, Readable}}, - public_data_tree_leaf_preimage::PublicDataTreeLeafPreimage, traits::Empty, + abis::public_data_read::PublicDataRead, + data::public_data_tree_leaf_preimage::PublicDataTreeLeafPreimage, traits::Empty, merkle_tree::{MembershipWitness, conditionally_assert_check_membership}, constants::PUBLIC_DATA_TREE_HEIGHT }; @@ -49,16 +49,3 @@ impl Empty for PublicDataHint { } } } - -impl Readable for PublicDataHint { - fn assert_match_read_request(self, read_request: PublicDataRead) { - assert_eq(self.leaf_slot, read_request.leaf_slot, "leaf_slot in PublicDataHint does not match read request"); - assert_eq(self.value, read_request.value, "value in PublicDataHint does not match read request"); - } -} - -impl Overridable for PublicDataHint { - fn override_counter(self) -> u32 { - self.override_counter - } -} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/data/public_data_leaf_hint.nr b/noir-projects/noir-protocol-circuits/crates/types/src/data/public_data_leaf_hint.nr new file mode 100644 index 000000000000..2d2c0becd9b4 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/data/public_data_leaf_hint.nr @@ -0,0 +1,18 @@ +use crate::{ + data::public_data_tree_leaf_preimage::PublicDataTreeLeafPreimage, traits::Empty, + merkle_tree::MembershipWitness, constants::PUBLIC_DATA_TREE_HEIGHT +}; + +struct PublicDataLeafHint { + preimage: PublicDataTreeLeafPreimage, + membership_witness: MembershipWitness, +} + +impl Empty for PublicDataLeafHint { + fn empty() -> Self { + PublicDataLeafHint { + preimage: PublicDataTreeLeafPreimage::empty(), + membership_witness: MembershipWitness::empty(), + } + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/data/public_data_tree_leaf.nr b/noir-projects/noir-protocol-circuits/crates/types/src/data/public_data_tree_leaf.nr new file mode 100644 index 000000000000..d1149066df9a --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/data/public_data_tree_leaf.nr @@ -0,0 +1,55 @@ +use crate::{abis::{public_data_read::PublicDataRead, side_effect::{Overridable, Readable}}, traits::Empty}; + +struct PublicDataTreeLeaf { + slot: Field, + value: Field, +} + +impl Eq for PublicDataTreeLeaf { + fn eq(self, other: Self) -> bool { + (self.slot == other.slot) & (self.value == other.value) + } +} + +impl Empty for PublicDataTreeLeaf { + fn empty() -> Self { + Self { + slot: 0, + value: 0, + } + } +} + +impl PublicDataTreeLeaf { + pub fn is_empty(self) -> bool { + (self.slot == 0) & (self.value == 0) + } +} + +struct OverridablePublicDataTreeLeaf { + leaf: PublicDataTreeLeaf, + override_counter: u32, +} + +impl Empty for OverridablePublicDataTreeLeaf { + fn empty() -> Self { + OverridablePublicDataTreeLeaf { + leaf: PublicDataTreeLeaf::empty(), + override_counter: 0, + } + } +} + +impl Readable for OverridablePublicDataTreeLeaf { + fn assert_match_read_request(self, read_request: PublicDataRead) { + assert_eq(self.leaf.slot, read_request.leaf_slot, "leaf_slot in OverridablePublicDataTreeLeaf does not match read request"); + assert_eq(self.leaf.value, read_request.value, "value in OverridablePublicDataTreeLeaf does not match read request"); + } +} + +impl Overridable for OverridablePublicDataTreeLeaf { + fn override_counter(self) -> u32 { + self.override_counter + } +} + diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/public_data_tree_leaf_preimage.nr b/noir-projects/noir-protocol-circuits/crates/types/src/data/public_data_tree_leaf_preimage.nr similarity index 100% rename from noir-projects/noir-protocol-circuits/crates/types/src/public_data_tree_leaf_preimage.nr rename to noir-projects/noir-protocol-circuits/crates/types/src/data/public_data_tree_leaf_preimage.nr diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/lib.nr b/noir-projects/noir-protocol-circuits/crates/types/src/lib.nr index 7e36c8455122..3d0cb047ca9e 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/lib.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/lib.nr @@ -3,9 +3,7 @@ mod address; mod debug_log; mod point; mod scalar; -// This is intentionally spelled like this -// since contract is a reserved keyword, so it cannot -// be used as an ident. +// This is intentionally spelled like this since contract is a reserved keyword, so it cannot be used as an ident. mod contrakt; mod transaction; mod abis; @@ -26,8 +24,6 @@ mod tests; mod state_reference; mod partial_state_reference; -mod public_data_tree_leaf; -mod public_data_tree_leaf_preimage; use abis::kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PrivateKernelCircuitPublicInputs, PublicKernelCircuitPublicInputs}; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/public_data_tree_leaf.nr b/noir-projects/noir-protocol-circuits/crates/types/src/public_data_tree_leaf.nr deleted file mode 100644 index 79f21b9734ad..000000000000 --- a/noir-projects/noir-protocol-circuits/crates/types/src/public_data_tree_leaf.nr +++ /dev/null @@ -1,27 +0,0 @@ -use crate::traits::Empty; - -struct PublicDataTreeLeaf { - slot: Field, - value: Field, -} - -impl Eq for PublicDataTreeLeaf { - fn eq(self, other: Self) -> bool { - (self.slot == other.slot) & (self.value == other.value) - } -} - -impl Empty for PublicDataTreeLeaf { - fn empty() -> Self { - Self { - slot: 0, - value: 0, - } - } -} - -impl PublicDataTreeLeaf { - pub fn is_empty(self) -> bool { - (self.slot == 0) & (self.value == 0) - } -} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/utils.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/utils.nr index 9890994c0516..ae5861a65fe1 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/utils.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/utils.nr @@ -1,18 +1,8 @@ -use crate::traits::{Empty, is_empty}; - -fn count_non_empty_elements(array: [T; N]) -> u32 where T: Empty + Eq { - let mut length = 0; - for elem in array { - if !is_empty(elem) { - length += 1; - } - } - length -} +use crate::{traits::{Empty, is_empty}, utils::arrays::validate_array}; pub fn assert_array_eq(array: [T; N], expected: [T; S]) where T: Empty + Eq { - assert_eq(count_non_empty_elements(expected), S, "cannot expect empty element in the result"); - assert_eq(count_non_empty_elements(array), S, "mismatch array lengths"); + assert(expected.all(|elem: T| !is_empty(elem)), "cannot expect empty element in the result"); + assert_eq(validate_array(array), S, "mismatch array lengths"); for i in 0..S { assert_eq(array[i], expected[i], "mismatch array elements"); } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr index 2f310a538f40..7f5ddaae3b1c 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr @@ -72,23 +72,10 @@ pub fn validate_array(array: [T; N]) -> u32 where T: Empty + Eq { length } -unconstrained fn count_non_empty_elements(array: [T; N]) -> u32 where T: Empty + Eq { - let mut length = 0; - let mut seen_empty = false; - for elem in array { - if is_empty(elem) { - seen_empty = true; - } else if !seen_empty { - length += 1; - } - } - length -} - // Helper function to count the number of non-empty elements in a validated array. // Important: Only use it for validated arrays: validate_array(array) should be true. pub fn array_length(array: [T; N]) -> u32 where T: Empty + Eq { - let length = count_non_empty_elements(array); + let length = find_index_hint(array, |elem: T| is_empty(elem)); if length != 0 { assert(!is_empty(array[length - 1])); } diff --git a/yarn-project/circuits.js/src/hints/build_public_data_hints.test.ts b/yarn-project/circuits.js/src/hints/build_public_data_hints.test.ts index 74a91f38d244..d88703a1d754 100644 --- a/yarn-project/circuits.js/src/hints/build_public_data_hints.test.ts +++ b/yarn-project/circuits.js/src/hints/build_public_data_hints.test.ts @@ -1,4 +1,5 @@ import { makeTuple } from '@aztec/foundation/array'; +import { padArrayEnd } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/fields'; import { type Tuple } from '@aztec/foundation/serialize'; @@ -10,118 +11,106 @@ import { import { PublicDataRead, PublicDataTreeLeafPreimage, PublicDataUpdateRequest } from '../structs/index.js'; import { buildPublicDataHints } from './build_public_data_hints.js'; -class ExpectedHint { - constructor(public leafSlot: number, public value: number, public matchOrLowLeafSlot: number) {} - - static empty() { - return new ExpectedHint(0, 0, 0); - } - - toExpectedObject() { - return expect.objectContaining({ - leafSlot: new Fr(this.leafSlot), - value: new Fr(this.value), - leafPreimage: expect.objectContaining({ slot: new Fr(this.matchOrLowLeafSlot) }), - }); - } -} - describe('buildPublicDataHints', () => { let publicDataReads: Tuple; let publicDataUpdateRequests: Tuple; - let expectedHints: Tuple; - let sideEffectCounter = 0; - - const nextSideEffectCounter = () => sideEffectCounter++; const publicDataLeaves = [ - new PublicDataTreeLeafPreimage(new Fr(22), new Fr(200), new Fr(33), 0n), - new PublicDataTreeLeafPreimage(new Fr(11), new Fr(100), new Fr(22), 0n), - new PublicDataTreeLeafPreimage(new Fr(0), new Fr(0), new Fr(11), 0n), + new PublicDataTreeLeafPreimage(new Fr(0), new Fr(0), new Fr(11), 2n), + new PublicDataTreeLeafPreimage(new Fr(22), new Fr(200), new Fr(0), 0n), + new PublicDataTreeLeafPreimage(new Fr(11), new Fr(100), new Fr(22), 1n), ]; const makePublicDataRead = (leafSlot: number, value: number) => - new PublicDataRead(new Fr(leafSlot), new Fr(value), nextSideEffectCounter()); + new PublicDataRead(new Fr(leafSlot), new Fr(value), 0); const makePublicDataWrite = (leafSlot: number, value: number) => - new PublicDataUpdateRequest(new Fr(leafSlot), new Fr(value), nextSideEffectCounter()); + new PublicDataUpdateRequest(new Fr(leafSlot), new Fr(value), 0); const oracle = { getMatchOrLowPublicDataMembershipWitness: (leafSlot: bigint) => { - const leafPreimage = publicDataLeaves.find(l => l.slot.toBigInt() <= leafSlot); + const leafPreimage = publicDataLeaves.find( + l => l.slot.toBigInt() <= leafSlot && (l.nextSlot.isZero() || l.nextSlot.toBigInt() > leafSlot), + ); return { membershipWitness: {}, leafPreimage } as any; }, }; - const buildHints = () => buildPublicDataHints(oracle, publicDataReads, publicDataUpdateRequests); - - const buildAndCheckHints = async () => { - const hints = await buildHints(); - const partialHints = expectedHints.map(h => h.toExpectedObject()); - expect(hints).toEqual(partialHints); + const buildAndCheckHints = async (expectedSlots: number[]) => { + const hints = await buildPublicDataHints(oracle, publicDataReads, publicDataUpdateRequests); + const partialHints = expectedSlots.map(s => + expect.objectContaining({ + preimage: publicDataLeaves.find(l => l.slot.equals(new Fr(s))), + }), + ); + const emptyPartialHint = expect.objectContaining({ preimage: PublicDataTreeLeafPreimage.empty() }); + expect(hints).toEqual(padArrayEnd(partialHints, emptyPartialHint, MAX_PUBLIC_DATA_HINTS)); }; beforeEach(() => { publicDataReads = makeTuple(MAX_PUBLIC_DATA_READS_PER_TX, PublicDataRead.empty); publicDataUpdateRequests = makeTuple(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, PublicDataUpdateRequest.empty); - expectedHints = makeTuple(MAX_PUBLIC_DATA_HINTS, ExpectedHint.empty); }); it('returns empty hints', async () => { - await buildAndCheckHints(); + await buildAndCheckHints([]); }); it('builds hints for reads for uninitialized slots', async () => { publicDataReads[0] = makePublicDataRead(12, 0); publicDataReads[1] = makePublicDataRead(39, 0); - expectedHints[0] = new ExpectedHint(12, 0, 11); - expectedHints[1] = new ExpectedHint(39, 0, 22); - - await buildAndCheckHints(); + await buildAndCheckHints([11, 22]); }); it('builds hints for reads for initialized slots', async () => { publicDataReads[0] = makePublicDataRead(22, 200); publicDataReads[1] = makePublicDataRead(11, 100); - expectedHints[0] = new ExpectedHint(22, 200, 22); - expectedHints[1] = new ExpectedHint(11, 100, 11); - - await buildAndCheckHints(); + await buildAndCheckHints([22, 11]); }); it('builds hints for writes to uninitialized slots', async () => { publicDataUpdateRequests[0] = makePublicDataWrite(5, 500); publicDataUpdateRequests[1] = makePublicDataWrite(17, 700); - expectedHints[0] = new ExpectedHint(5, 0, 0); - expectedHints[1] = new ExpectedHint(17, 0, 11); - - await buildAndCheckHints(); + await buildAndCheckHints([0, 11]); }); it('builds hints for writes to initialized slots', async () => { publicDataUpdateRequests[0] = makePublicDataWrite(11, 111); publicDataUpdateRequests[1] = makePublicDataWrite(22, 222); - expectedHints[0] = new ExpectedHint(11, 100, 11); - expectedHints[1] = new ExpectedHint(22, 200, 22); + await buildAndCheckHints([11, 22]); + }); - await buildAndCheckHints(); + it('skip hints for repeated reads', async () => { + publicDataReads[0] = makePublicDataRead(22, 200); // 22 + publicDataReads[1] = makePublicDataRead(39, 0); // 22 + publicDataReads[2] = makePublicDataRead(22, 200); // No hint needed because slot 22 was read. + publicDataReads[3] = makePublicDataRead(39, 0); // No hint needed because slot 39 was read. + publicDataReads[4] = makePublicDataRead(12, 0); // 11 + publicDataReads[5] = makePublicDataRead(39, 0); // // No hint needed because slot 39 was read. + await buildAndCheckHints([22, 22, 11]); }); - it('builds hints for mixed reads and writes', async () => { - publicDataReads[0] = makePublicDataRead(22, 200); - publicDataReads[1] = makePublicDataRead(12, 0); - publicDataReads[2] = makePublicDataRead(39, 0); - publicDataReads[3] = makePublicDataRead(11, 100); - publicDataUpdateRequests[0] = makePublicDataWrite(11, 111); - publicDataUpdateRequests[1] = makePublicDataWrite(5, 500); - publicDataUpdateRequests[2] = makePublicDataWrite(17, 700); - publicDataUpdateRequests[3] = makePublicDataWrite(22, 222); - expectedHints[0] = new ExpectedHint(22, 200, 22); - expectedHints[1] = new ExpectedHint(12, 0, 11); - expectedHints[2] = new ExpectedHint(39, 0, 22); - expectedHints[3] = new ExpectedHint(11, 100, 11); - expectedHints[4] = new ExpectedHint(5, 0, 0); - expectedHints[5] = new ExpectedHint(17, 0, 11); + it('skip hints for repeated writes', async () => { + publicDataUpdateRequests[0] = makePublicDataWrite(11, 111); // 11 + publicDataUpdateRequests[1] = makePublicDataWrite(5, 500); // 0 + publicDataUpdateRequests[2] = makePublicDataWrite(11, 112); // No hint needed because slot 11 was written. + publicDataUpdateRequests[3] = makePublicDataWrite(17, 700); // 11 + publicDataUpdateRequests[4] = makePublicDataWrite(11, 113); // No hint needed because slot 11 was written. + publicDataUpdateRequests[5] = makePublicDataWrite(5, 222); // No hint needed because slot 5 was written. + publicDataUpdateRequests[6] = makePublicDataWrite(37, 700); // 22 + await buildAndCheckHints([11, 0, 11, 22]); + }); - await buildAndCheckHints(); + it('builds hints for mixed reads and writes', async () => { + publicDataReads[0] = makePublicDataRead(22, 200); // 22 + publicDataReads[1] = makePublicDataRead(7, 0); // 0 + publicDataReads[2] = makePublicDataRead(41, 0); // 22 + publicDataReads[3] = makePublicDataRead(11, 100); // 11 + publicDataReads[4] = makePublicDataRead(39, 0); // 22 + publicDataUpdateRequests[0] = makePublicDataWrite(11, 111); // No hint needed because slot 11 was read. + publicDataUpdateRequests[1] = makePublicDataWrite(5, 500); // 0 + publicDataUpdateRequests[2] = makePublicDataWrite(17, 700); // 11 + publicDataUpdateRequests[3] = makePublicDataWrite(22, 222); // No hint needed because slot 22 was read. + publicDataUpdateRequests[4] = makePublicDataWrite(39, 700); // No hint needed because slot 39 was read. + await buildAndCheckHints([22, 0, 22, 11, 22, 0, 11]); }); }); diff --git a/yarn-project/circuits.js/src/hints/build_public_data_hints.ts b/yarn-project/circuits.js/src/hints/build_public_data_hints.ts index 341c7f3f0dfe..53200bab3640 100644 --- a/yarn-project/circuits.js/src/hints/build_public_data_hints.ts +++ b/yarn-project/circuits.js/src/hints/build_public_data_hints.ts @@ -9,6 +9,7 @@ import { type PUBLIC_DATA_TREE_HEIGHT, } from '../constants.gen.js'; import { + PublicDataLeafHint, type PublicDataRead, type PublicDataTreeLeafPreimage, type PublicDataUpdateRequest, @@ -25,45 +26,27 @@ type PublicDataMembershipWitnessOracle = { getMatchOrLowPublicDataMembershipWitness(leafSlot: bigint): Promise; }; +async function buildPublicDataLeafHint(oracle: PublicDataMembershipWitnessOracle, leafSlot: bigint) { + const { membershipWitness, leafPreimage } = await oracle.getMatchOrLowPublicDataMembershipWitness(leafSlot); + return new PublicDataLeafHint(leafPreimage, membershipWitness); +} + export async function buildPublicDataHints( oracle: PublicDataMembershipWitnessOracle, publicDataReads: Tuple, publicDataUpdateRequests: Tuple, -): Promise> { - const slotCounterMap = new Map(); - publicDataReads - .filter(r => !r.isEmpty()) - .forEach(r => { - if (!r.isEmpty()) { - slotCounterMap.set(r.leafSlot.toBigInt(), 0); - } - }); - publicDataUpdateRequests - .filter(w => !w.isEmpty()) - .forEach(w => { - if (!w.isEmpty()) { - let overrideCounter = slotCounterMap.get(w.leafSlot.toBigInt()) || 0; - if (!overrideCounter || overrideCounter > w.counter) { - overrideCounter = w.counter; - } - slotCounterMap.set(w.leafSlot.toBigInt(), overrideCounter); - } - }); - const uniquePublicDataLeafSlots = [...slotCounterMap.keys()]; - - const hints = await Promise.all( - uniquePublicDataLeafSlots.map(slot => buildPublicDataHint(oracle, slot, slotCounterMap.get(slot)!)), - ); - return padArrayEnd(hints, PublicDataHint.empty(), MAX_PUBLIC_DATA_HINTS); +): Promise> { + const leafSlots = [...publicDataReads.map(r => r.leafSlot), ...publicDataUpdateRequests.map(w => w.leafSlot)] + .filter(s => !s.isZero()) + .map(s => s.toBigInt()); + const uniqueLeafSlots = [...new Set(leafSlots)]; + const hints = await Promise.all(uniqueLeafSlots.map(slot => buildPublicDataLeafHint(oracle, slot))); + return padArrayEnd(hints, PublicDataLeafHint.empty(), MAX_PUBLIC_DATA_HINTS); } -export async function buildPublicDataHint( - oracle: PublicDataMembershipWitnessOracle, - leafSlot: bigint, - overrideCounter: number, -) { +export async function buildPublicDataHint(oracle: PublicDataMembershipWitnessOracle, leafSlot: bigint) { const { membershipWitness, leafPreimage } = await oracle.getMatchOrLowPublicDataMembershipWitness(leafSlot); const exists = leafPreimage.slot.toBigInt() === leafSlot; const value = exists ? leafPreimage.value : Fr.ZERO; - return new PublicDataHint(new Fr(leafSlot), value, overrideCounter, membershipWitness, leafPreimage); + return new PublicDataHint(new Fr(leafSlot), value, 0, membershipWitness, leafPreimage); } diff --git a/yarn-project/circuits.js/src/hints/build_transient_data_hints.test.ts b/yarn-project/circuits.js/src/hints/build_transient_data_hints.test.ts index ab4a63ba807a..a4c0c921638e 100644 --- a/yarn-project/circuits.js/src/hints/build_transient_data_hints.test.ts +++ b/yarn-project/circuits.js/src/hints/build_transient_data_hints.test.ts @@ -1,6 +1,7 @@ +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { Fr } from '@aztec/foundation/fields'; + import { - AztecAddress, - Fr, NoteHash, Nullifier, ReadRequest, @@ -8,8 +9,7 @@ import { type ScopedNullifier, ScopedReadRequest, TransientDataIndexHint, -} from '@aztec/circuits.js'; - +} from '../structs/index.js'; import { buildTransientDataHints } from './build_transient_data_hints.js'; describe('buildTransientDataHints', () => { diff --git a/yarn-project/circuits.js/src/structs/index.ts b/yarn-project/circuits.js/src/structs/index.ts index 96f5890100c5..06c2543d772a 100644 --- a/yarn-project/circuits.js/src/structs/index.ts +++ b/yarn-project/circuits.js/src/structs/index.ts @@ -60,6 +60,7 @@ export * from './public_call_stack_item.js'; export * from './public_call_stack_item_compressed.js'; export * from './public_circuit_public_inputs.js'; export * from './public_data_hint.js'; +export * from './public_data_leaf_hint.js'; export * from './public_data_read.js'; export * from './public_data_update_request.js'; export * from './public_validation_requests.js'; diff --git a/yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts b/yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts index cb3a15b87a70..76226108321d 100644 --- a/yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts +++ b/yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts @@ -6,7 +6,7 @@ import { nullifierNonExistentReadRequestHintsFromBuffer, } from '../non_existent_read_request_hints.js'; import { PartialStateReference } from '../partial_state_reference.js'; -import { PublicDataHint } from '../public_data_hint.js'; +import { PublicDataLeafHint } from '../public_data_leaf_hint.js'; import { type NullifierReadRequestHints, nullifierReadRequestHintsFromBuffer } from '../read_request_hints/index.js'; import { PublicKernelData } from './public_kernel_data.js'; @@ -27,7 +27,7 @@ export class PublicKernelTailCircuitPrivateInputs { * Contains hints for the nullifier non existent read requests. */ public readonly nullifierNonExistentReadRequestHints: NullifierNonExistentReadRequestHints, - public readonly publicDataHints: Tuple, + public readonly publicDataHints: Tuple, public readonly startState: PartialStateReference, ) {} @@ -59,7 +59,7 @@ export class PublicKernelTailCircuitPrivateInputs { MAX_NULLIFIER_READ_REQUESTS_PER_TX, ), nullifierNonExistentReadRequestHintsFromBuffer(reader), - reader.readArray(MAX_PUBLIC_DATA_HINTS, PublicDataHint), + reader.readArray(MAX_PUBLIC_DATA_HINTS, PublicDataLeafHint), reader.readObject(PartialStateReference), ); } diff --git a/yarn-project/circuits.js/src/structs/public_data_leaf_hint.ts b/yarn-project/circuits.js/src/structs/public_data_leaf_hint.ts new file mode 100644 index 000000000000..aba7c583e37d --- /dev/null +++ b/yarn-project/circuits.js/src/structs/public_data_leaf_hint.ts @@ -0,0 +1,28 @@ +import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; + +import { PUBLIC_DATA_TREE_HEIGHT } from '../constants.gen.js'; +import { MembershipWitness } from './membership_witness.js'; +import { PublicDataTreeLeafPreimage } from './trees/index.js'; + +export class PublicDataLeafHint { + constructor( + public preimage: PublicDataTreeLeafPreimage, + public membershipWitness: MembershipWitness, + ) {} + + static empty() { + return new PublicDataLeafHint(PublicDataTreeLeafPreimage.empty(), MembershipWitness.empty(PUBLIC_DATA_TREE_HEIGHT)); + } + + static fromBuffer(buffer: Buffer | BufferReader) { + const reader = BufferReader.asReader(buffer); + return new PublicDataLeafHint( + reader.readObject(PublicDataTreeLeafPreimage), + MembershipWitness.fromBuffer(reader, PUBLIC_DATA_TREE_HEIGHT), + ); + } + + toBuffer() { + return serializeToBuffer(this.preimage, this.membershipWitness); + } +} diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index 70882bcd2dc4..dbf63a155f57 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -143,7 +143,7 @@ import { GasFees } from '../structs/gas_fees.js'; import { GasSettings } from '../structs/gas_settings.js'; import { GlobalVariables } from '../structs/global_variables.js'; import { Header } from '../structs/header.js'; -import { PublicValidationRequests, ScopedL2ToL1Message, ScopedNoteHash } from '../structs/index.js'; +import { PublicDataLeafHint, PublicValidationRequests, ScopedL2ToL1Message, ScopedNoteHash } from '../structs/index.js'; import { KernelCircuitPublicInputs } from '../structs/kernel/kernel_circuit_public_inputs.js'; import { KernelData } from '../structs/kernel/kernel_data.js'; import { BlockMergeRollupInputs } from '../structs/rollup/block_merge_rollup.js'; @@ -677,7 +677,7 @@ export function makePublicKernelTailCircuitPrivateInputs(seed = 1): PublicKernel makePublicKernelData(seed), NullifierReadRequestHintsBuilder.empty(MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX), NullifierNonExistentReadRequestHintsBuilder.empty(), - makeTuple(MAX_PUBLIC_DATA_HINTS, PublicDataHint.empty, seed + 0x100), + makeTuple(MAX_PUBLIC_DATA_HINTS, PublicDataLeafHint.empty), makePartialStateReference(seed + 0x200), ); } diff --git a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts index 590f4a554719..4d62cc2beea3 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -95,6 +95,7 @@ import { PublicCallStackItemCompressed, type PublicCircuitPublicInputs, type PublicDataHint, + type PublicDataLeafHint, PublicDataRead, type PublicDataTreeLeaf, type PublicDataTreeLeafPreimage, @@ -212,6 +213,7 @@ import type { PublicCallStackItem as PublicCallStackItemNoir, PublicCircuitPublicInputs as PublicCircuitPublicInputsNoir, PublicDataHint as PublicDataHintNoir, + PublicDataLeafHint as PublicDataLeafHintNoir, PublicDataRead as PublicDataReadNoir, PublicDataTreeLeaf as PublicDataTreeLeafNoir, PublicDataTreeLeafPreimage as PublicDataTreeLeafPreimageNoir, @@ -1165,6 +1167,13 @@ function mapPublicDataHintToNoir(hint: PublicDataHint): PublicDataHintNoir { }; } +function mapPublicDataLeafHintToNoir(hint: PublicDataLeafHint): PublicDataLeafHintNoir { + return { + preimage: mapPublicDataTreePreimageToNoir(hint.preimage), + membership_witness: mapMembershipWitnessToNoir(hint.membershipWitness), + }; +} + function mapOptionalNumberToNoir(option: OptionalNumber): OptionalNumberNoir { return { _is_some: option.isSome, @@ -1740,7 +1749,7 @@ export function mapPublicKernelTailCircuitPrivateInputsToNoir( nullifier_non_existent_read_request_hints: mapNullifierNonExistentReadRequestHintsToNoir( inputs.nullifierNonExistentReadRequestHints, ), - public_data_hints: mapTuple(inputs.publicDataHints, mapPublicDataHintToNoir), + public_data_hints: mapTuple(inputs.publicDataHints, mapPublicDataLeafHintToNoir), start_state: mapPartialStateReferenceToNoir(inputs.startState), }; } diff --git a/yarn-project/simulator/src/public/hints_builder.ts b/yarn-project/simulator/src/public/hints_builder.ts index caaed1965e70..d8488426cfd3 100644 --- a/yarn-project/simulator/src/public/hints_builder.ts +++ b/yarn-project/simulator/src/public/hints_builder.ts @@ -56,7 +56,7 @@ export class HintsBuilder { getPublicDataHint(dataAction: PublicDataRead | PublicDataUpdateRequest | bigint) { const slot = typeof dataAction === 'bigint' ? dataAction : dataAction.leafSlot.toBigInt(); - return buildPublicDataHint(this, slot, 0); + return buildPublicDataHint(this, slot); } async getNullifierMembershipWitness(nullifier: Fr) { From 50d5cfd1048fece30368c548ade04001dd8e9cca Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Mon, 2 Sep 2024 15:56:06 +0000 Subject: [PATCH 06/14] Remove unused files. --- .../crates/types/src/abis/side_effect.nr | 5 - .../assert_combined_permuted_array.nr | 131 ------- .../assert_deduped_array.nr | 343 ------------------ .../dedupe_array.nr | 131 ------- .../get_deduped_hints.nr | 49 --- .../sort_by_position_then_counter.nr | 53 --- 6 files changed, 712 deletions(-) delete mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array/assert_combined_permuted_array.nr delete mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array/assert_deduped_array.nr delete mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array/dedupe_array.nr delete mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array/get_deduped_hints.nr delete mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array/sort_by_position_then_counter.nr diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/side_effect.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/side_effect.nr index e3637186e104..d09391a8cd93 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/side_effect.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/side_effect.nr @@ -19,11 +19,6 @@ trait Scoped where T: Eq { fn inner(self) -> T; } -trait Positioned { - // Like a storage slot - fn position(self) -> Field; -} - trait Readable { fn assert_match_read_request(self, read_request: T); } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array/assert_combined_permuted_array.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array/assert_combined_permuted_array.nr deleted file mode 100644 index bc37a1320aa2..000000000000 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array/assert_combined_permuted_array.nr +++ /dev/null @@ -1,131 +0,0 @@ -use crate::{traits::{Empty, is_empty}, utils::arrays::array_length}; - -pub fn assert_combined_permuted_array( - original_array_lt: [T; N], - original_array_gte: [T; N], - permuted_array: [T; N], - combined_indexes: [u32; N] -) where T: Empty + Eq { - let num_lt = array_length(original_array_lt); - let num_gte = array_length(original_array_gte); - let total_num = num_lt + num_gte; - - let mut is_lt = true; - let mut should_be_empty = false; - for i in 0..N { - is_lt &= i != num_lt; - should_be_empty |= i == total_num; - - let from = if is_lt { - original_array_lt[i] - } else { - original_array_gte[i - num_lt] - }; - - let combined_index = combined_indexes[i]; - let to = permuted_array[combined_index]; - assert_eq(from, to, "hinted item in the permuted array does not match"); - - if should_be_empty { - assert(is_empty(permuted_array[i]), "permuted array must be padded with empty items"); - } - } -} - -mod tests { - use crate::utils::arrays::assert_combined_deduped_array::assert_combined_permuted_array::assert_combined_permuted_array; - - struct TestBuilder { - original_array_lt: [Field; N], - original_array_gte: [Field; N], - permuted_array: [Field; N], - combined_indexes: [u32; N] - } - - impl TestBuilder { - pub fn new() -> TestBuilder<7> { - let original_array_lt = [6, 3, 8, 1, 0, 0, 0]; - let original_array_gte = [4, 9, 5, 0, 0, 0, 0]; - let permuted_array = [5, 8, 9, 3, 1, 6, 4]; - let combined_indexes = [5, 3, 1, 4, 6, 2, 0]; - TestBuilder { original_array_lt, original_array_gte, permuted_array, combined_indexes } - } - - pub fn new_with_padded_zeros() -> TestBuilder<10> { - let original_array_lt = [6, 3, 8, 1, 0, 0, 0, 0, 0, 0]; - let original_array_gte = [4, 9, 5, 0, 0, 0, 0, 0, 0, 0]; - let permuted_array = [5, 8, 9, 3, 1, 6, 4, 0, 0, 0]; - let combined_indexes = [5, 3, 1, 4, 6, 2, 0, 7, 8, 9]; - TestBuilder { original_array_lt, original_array_gte, permuted_array, combined_indexes } - } - - pub fn verify(self) { - assert_combined_permuted_array( - self.original_array_lt, - self.original_array_gte, - self.permuted_array, - self.combined_indexes - ); - } - } - - #[test] - fn assert_combined_permuted_array_full() { - let builder = TestBuilder::new(); - builder.verify() - } - - #[test] - fn assert_combined_permuted_array_padded_empty() { - let builder = TestBuilder::new_with_padded_zeros(); - builder.verify() - } - - #[test(should_fail_with="hinted item in the permuted array does not match")] - fn assert_combined_permuted_array_missing_value_fails() { - let mut builder = TestBuilder::new_with_padded_zeros(); - - // Clear a value. - builder.permuted_array[6] = 0; - - builder.verify() - } - - #[test(should_fail_with="hinted item in the permuted array does not match")] - fn assert_combined_permuted_array_duplicated_item_fails() { - let mut builder = TestBuilder::new_with_padded_zeros(); - - // Duplicate a value. - builder.permuted_array[3] = builder.permuted_array[2]; - - builder.verify() - } - - #[test(should_fail_with="permuted array must be padded with empty items")] - fn assert_combined_permuted_array_extra_item_fails() { - let mut builder = TestBuilder::new_with_padded_zeros(); - - // Overwrite the first empty value with a non-empty value. - builder.permuted_array[8] = builder.permuted_array[1]; - // Update the index to point to an empty value. - builder.combined_indexes[8] = 9; - - builder.verify() - } - - #[test(should_fail_with="permuted array must be padded with empty items")] - fn assert_combined_permuted_array_mixed_empty_item_fails() { - let mut builder = TestBuilder::new_with_padded_zeros(); - - // Swap the positions of a non-empty item and an empty item. - builder.permuted_array[8] = builder.permuted_array[4]; - builder.permuted_array[4] = 0; - // Update the index to point to the value. - let original_index = 3; - assert_eq(builder.original_array_lt[original_index], builder.permuted_array[8]); - builder.combined_indexes[original_index] = 8; - builder.combined_indexes[8] = 4; - - builder.verify() - } -} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array/assert_deduped_array.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array/assert_deduped_array.nr deleted file mode 100644 index c58e0f66b83d..000000000000 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array/assert_deduped_array.nr +++ /dev/null @@ -1,343 +0,0 @@ -use crate::{abis::side_effect::{Positioned, Ordered}, traits::Empty, utils::arrays::{array_length, validate_array}}; - -/* - The sorted_array here needs to be sorted based on the `position` field of the container, - *and* a secondary sort based on the `counter` field of the container. - - For example, the storage slot in the case of public data update requests. - The run_lengths array should contain the length of each run of the sorted_array. - The deduped_array should contain the deduplicated array. - - For example, if the original array is writing `(position,value,counter)`s: - [ (1,1,1), (1,2,4), (2,3,3), (3,4,2), (3,5,5), (3,6,6), (4,7,8), (4,8,9), (5,9,7), (0,0,0), ... padding with zeros ] - then run_lengths array is: - [ - 2, // run of 1s - 1, // run of 2 - 3, // run of 3s - 2, // run of 4s - 1, // run of 5 - 0, - 0, - ... padding with zeros - ] - - then the deduped_array should be: - [ (1,2,4), (2,3,3), (3,6,6), (4,8,9), (5,9,7), (0,0,0), ... padding with zeros ] -*/ -pub fn assert_deduped_array( - sorted_array: [T; N], - deduped_array: [T; N], - run_lengths: [u32; N] -) where T: Positioned + Ordered + Empty + Eq { - let num_non_empty_items = array_length(sorted_array); - let deduped_len = validate_array(deduped_array); // This makes sure that the array is padded with empty items. - - // container at the start of the current run - let mut start_run_container = sorted_array[0]; - // the index we are collapsing into - let mut deduped_index = 0; - // the length of the current run we are collapsing - let mut run_counter = 0; - let mut should_check = true; - for i in 0..N { - should_check &= i != num_non_empty_items; - if should_check { - let current_container = sorted_array[i]; - - if run_counter == 0 { - // Start a new run. - run_counter = run_lengths[deduped_index]; - if i != 0 { - assert( - start_run_container.position().lt(current_container.position()), "Containers in a run must be sorted by position" - ); - } - start_run_container = current_container; - } - - assert(run_counter != 0, "Invalid run length"); - run_counter -= 1; - - assert_eq( - current_container.position(), start_run_container.position(), "The position of the current container must match the start of the run" - ); - - if run_counter == 0 { - // End of the run. - assert_eq( - deduped_array[deduped_index], current_container, "The container we are collapsing into must match the current container" - ); - deduped_index += 1; - } else { - // We're in a run, so this container must have a lower counter. - // Note we don't check for overflow here, as the run_lengths array must be correct. - assert( - current_container.counter() < sorted_array[i + 1].counter(), "Containers in a run must be sorted by counter" - ); - } - } - } - - assert_eq(deduped_index, deduped_len, "Final deduped index does not match deduped array length"); -} - -mod tests { - use crate::{ - abis::side_effect::{Positioned, Ordered}, traits::Empty, tests::utils::pad_end, - utils::arrays::assert_combined_deduped_array::{assert_deduped_array::assert_deduped_array} - }; - - struct TestContainer { - value: Field, - position: Field, - counter: u32, - } - - impl Positioned for TestContainer { - fn position(self) -> Field { - self.position - } - } - - impl Ordered for TestContainer { - fn counter(self) -> u32 { - self.counter - } - } - - impl Empty for TestContainer { - fn empty() -> Self { - TestContainer { value: 0, position: 0, counter: 0 } - } - } - - impl Eq for TestContainer { - fn eq(self, other: Self) -> bool { - self.value.eq(other.value) & self.position.eq(other.position) & self.counter.eq(other.counter) - } - } - - #[test] - fn assert_deduped_array_basic_test() { - let sorted_array = [ - TestContainer { value: 1, position: 1, counter: 1 }, - TestContainer { value: 2, position: 1, counter: 4 }, - TestContainer { value: 3, position: 2, counter: 3 }, - TestContainer { value: 4, position: 3, counter: 2 }, - TestContainer { value: 5, position: 3, counter: 5 }, - TestContainer { value: 6, position: 3, counter: 6 }, - TestContainer { value: 7, position: 4, counter: 8 }, - TestContainer { value: 8, position: 4, counter: 9 }, - TestContainer { value: 9, position: 5, counter: 7 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 } - ]; - let deduped_array = [ - TestContainer { value: 2, position: 1, counter: 4 }, - TestContainer { value: 3, position: 2, counter: 3 }, - TestContainer { value: 6, position: 3, counter: 6 }, - TestContainer { value: 8, position: 4, counter: 9 }, - TestContainer { value: 9, position: 5, counter: 7 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 } - ]; - let run_lengths = [2, 1, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0]; - assert_deduped_array(sorted_array, deduped_array, run_lengths); - } - - #[test] - fn assert_deduped_array_empty_arrays() { - let sorted_array = [TestContainer::empty(); 12]; - let deduped_array = [TestContainer::empty(); 12]; - let run_lengths = [0; 12]; - assert_deduped_array(sorted_array, deduped_array, run_lengths); - } - - #[test] - fn assert_deduped_array_no_duplicates() { - let sorted_array = [ - TestContainer { value: 1, position: 1, counter: 1 }, - TestContainer { value: 2, position: 2, counter: 2 }, - TestContainer { value: 3, position: 3, counter: 3 }, - TestContainer { value: 4, position: 4, counter: 4 }, - TestContainer { value: 5, position: 5, counter: 5 } - ]; - let deduped_array = sorted_array; - let run_lengths = [1; 5]; - assert_deduped_array(sorted_array, deduped_array, run_lengths); - } - - #[test] - fn assert_deduped_array_no_duplicates_padded_empty() { - let sorted_array = pad_end( - [ - TestContainer { value: 1, position: 1, counter: 1 }, - TestContainer { value: 2, position: 2, counter: 2 }, - TestContainer { value: 3, position: 3, counter: 3 }, - TestContainer { value: 4, position: 4, counter: 4 }, - TestContainer { value: 5, position: 5, counter: 5 } - ], - TestContainer::empty() - ); - let deduped_array = sorted_array; - let run_lengths = [1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0]; - assert_deduped_array(sorted_array, deduped_array, run_lengths); - } - - #[test] - fn assert_deduped_array_single_run_at_end() { - let sorted_array = [ - TestContainer { value: 1, position: 1, counter: 1 }, - TestContainer { value: 2, position: 2, counter: 2 }, - TestContainer { value: 3, position: 3, counter: 3 }, - TestContainer { value: 4, position: 4, counter: 4 }, - TestContainer { value: 5, position: 5, counter: 5 }, - TestContainer { value: 6, position: 6, counter: 7 }, - TestContainer { value: 7, position: 6, counter: 8 }, - TestContainer { value: 8, position: 6, counter: 9 } - ]; - let deduped_array = [ - TestContainer { value: 1, position: 1, counter: 1 }, - TestContainer { value: 2, position: 2, counter: 2 }, - TestContainer { value: 3, position: 3, counter: 3 }, - TestContainer { value: 4, position: 4, counter: 4 }, - TestContainer { value: 5, position: 5, counter: 5 }, - TestContainer { value: 8, position: 6, counter: 9 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 } - ]; - let run_lengths = [1, 1, 1, 1, 1, 3, 0, 0]; - assert_deduped_array(sorted_array, deduped_array, run_lengths); - } - - #[test] - fn assert_deduped_array_all_duplicates() { - let sorted_array = [ - TestContainer { value: 1, position: 1, counter: 1 }, - TestContainer { value: 2, position: 1, counter: 2 }, - TestContainer { value: 3, position: 1, counter: 3 }, - TestContainer { value: 4, position: 1, counter: 4 }, - TestContainer { value: 5, position: 1, counter: 5 }, - TestContainer { value: 6, position: 1, counter: 6 }, - TestContainer { value: 7, position: 1, counter: 7 }, - TestContainer { value: 8, position: 1, counter: 8 }, - TestContainer { value: 9, position: 1, counter: 9 } - ]; - let deduped_array = pad_end( - [TestContainer { value: 9, position: 1, counter: 9 }], - TestContainer::empty() - ); - let run_lengths = [9, 0, 0, 0, 0, 0, 0, 0, 0]; - assert_deduped_array(sorted_array, deduped_array, run_lengths); - } - - #[test(should_fail_with="The position of the current container must match the start of the run")] - fn assert_deduped_array_mismatched_position_in_run() { - let sorted_array = [ - TestContainer { value: 1, position: 1, counter: 1 }, - TestContainer { value: 2, position: 1, counter: 2 }, - TestContainer { value: 3, position: 2, counter: 3 }, - TestContainer { value: 4, position: 3, counter: 4 } - ]; - let deduped_array = [ - TestContainer { value: 2, position: 1, counter: 2 }, - TestContainer { value: 3, position: 2, counter: 3 }, - TestContainer { value: 4, position: 3, counter: 4 }, - TestContainer { value: 0, position: 0, counter: 0 } - ]; - let run_lengths = [3, 1, 1, 0]; - assert_deduped_array(sorted_array, deduped_array, run_lengths); - } - - #[test(should_fail_with="The container we are collapsing into must match the current container")] - fn assert_deduped_array_mismatched_deduped_value() { - let sorted_array = [ - TestContainer { value: 1, position: 1, counter: 1 }, - TestContainer { value: 2, position: 1, counter: 2 }, - TestContainer { value: 3, position: 2, counter: 3 }, - TestContainer { value: 4, position: 3, counter: 4 } - ]; - let deduped_array = [ - TestContainer { value: 1, position: 1, counter: 1 }, - TestContainer { value: 3, position: 2, counter: 3 }, - TestContainer { value: 4, position: 3, counter: 4 }, - TestContainer { value: 0, position: 0, counter: 0 } - ]; - let run_lengths = [2, 1, 1, 0]; - assert_deduped_array(sorted_array, deduped_array, run_lengths); - } - - #[test(should_fail_with="Containers in a run must be sorted by position")] - fn assert_deduped_array_missed_deduped_value() { - let sorted_array = [ - TestContainer { value: 1, position: 1, counter: 1 },// This should've been deduped. - TestContainer { value: 2, position: 1, counter: 2 }, - TestContainer { value: 3, position: 2, counter: 3 }, - TestContainer { value: 4, position: 3, counter: 4 } - ]; - let deduped_array = sorted_array; - let run_lengths = [1, 1, 1, 1]; - assert_deduped_array(sorted_array, deduped_array, run_lengths); - } - - #[test(should_fail_with="Containers in a run must be sorted by position")] - fn assert_deduped_array_unsorted_array() { - let sorted_array = [ - TestContainer { value: 1, position: 1, counter: 1 }, - TestContainer { value: 3, position: 2, counter: 3 }, - TestContainer { value: 2, position: 1, counter: 2 },// Not sorted by position. - TestContainer { value: 4, position: 3, counter: 4 } - ]; - let deduped_array = [ - TestContainer { value: 1, position: 1, counter: 1 }, - TestContainer { value: 3, position: 2, counter: 3 }, - TestContainer { value: 2, position: 1, counter: 2 }, - TestContainer { value: 4, position: 3, counter: 4 } - ]; - let run_lengths = [1, 1, 1, 1]; - assert_deduped_array(sorted_array, deduped_array, run_lengths); - } - - #[test(should_fail_with="Final deduped index does not match deduped array length")] - fn assert_deduped_array_extra_value() { - let sorted_array = [ - TestContainer { value: 1, position: 1, counter: 1 }, - TestContainer { value: 2, position: 1, counter: 2 }, - TestContainer { value: 3, position: 2, counter: 3 }, - TestContainer { value: 4, position: 3, counter: 4 } - ]; - let deduped_array = [ - TestContainer { value: 2, position: 1, counter: 2 }, - TestContainer { value: 3, position: 2, counter: 3 }, - TestContainer { value: 4, position: 3, counter: 4 }, - TestContainer { value: 1, position: 1, counter: 1 }// This should be empty. - ]; - let run_lengths = [2, 1, 1, 0]; - assert_deduped_array(sorted_array, deduped_array, run_lengths); - } - - #[test(should_fail_with="invalid array")] - fn assert_deduped_array_padded_non_empty_value() { - let sorted_array = [ - TestContainer { value: 1, position: 1, counter: 1 }, - TestContainer { value: 2, position: 1, counter: 2 }, - TestContainer { value: 3, position: 2, counter: 3 }, - TestContainer::empty() - ]; - let deduped_array = [ - TestContainer { value: 2, position: 1, counter: 2 }, - TestContainer { value: 3, position: 2, counter: 3 }, - TestContainer::empty(), - TestContainer { value: 1, position: 1, counter: 1 }// This should be empty. - ]; - let run_lengths = [2, 1, 0, 0]; - assert_deduped_array(sorted_array, deduped_array, run_lengths); - } -} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array/dedupe_array.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array/dedupe_array.nr deleted file mode 100644 index 3aa86e380c88..000000000000 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array/dedupe_array.nr +++ /dev/null @@ -1,131 +0,0 @@ -use crate::{ - abis::side_effect::{Ordered, Positioned}, traits::Empty, - utils::arrays::assert_combined_deduped_array::sort_by_position_then_counter::sort_by_position_then_counter -}; - -pub fn dedupe_array(array: [T; N]) -> [T; N] where T: Positioned + Ordered + Empty + Eq { - let sorted = sort_by_position_then_counter(array); - let mut deduped = [T::empty(); N]; - let mut num_deduped = 0; - let mut prev_position = sorted[0].position(); - for item in sorted { - let position = item.position(); - if position != prev_position { - num_deduped += 1; - } - deduped[num_deduped] = item; - prev_position = position; - } - deduped -} - -mod tests { - use crate::{ - tests::utils::pad_end, - utils::arrays::assert_combined_deduped_array::{assert_deduped_array::tests::TestContainer, dedupe_array::dedupe_array} - }; - - #[test] - fn dedupe_array_padded_empty() { - let original_array = [ - TestContainer { value: 11, position: 3, counter: 2 }, - TestContainer { value: 55, position: 4, counter: 9 }, - TestContainer { value: 99, position: 3, counter: 5 }, - TestContainer { value: 66, position: 1, counter: 1 }, - TestContainer { value: 44, position: 4, counter: 8 }, - TestContainer { value: 77, position: 5, counter: 7 }, - TestContainer { value: 33, position: 1, counter: 4 }, - TestContainer { value: 22, position: 3, counter: 6 }, - TestContainer { value: 88, position: 2, counter: 3 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 } - ]; - let deduped_array = [ - TestContainer { value: 33, position: 1, counter: 4 }, - TestContainer { value: 88, position: 2, counter: 3 }, - TestContainer { value: 22, position: 3, counter: 6 }, - TestContainer { value: 55, position: 4, counter: 9 }, - TestContainer { value: 77, position: 5, counter: 7 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 } - ]; - assert_eq(dedupe_array(original_array), deduped_array); - } - - #[test] - fn dedupe_array_empty_arrays() { - let original_array = [TestContainer::empty(); 12]; - let deduped_array = [TestContainer::empty(); 12]; - assert_eq(dedupe_array(original_array), deduped_array); - } - - #[test] - fn dedupe_array_no_duplicates() { - let original_array = [ - TestContainer { value: 88, position: 3, counter: 3 }, - TestContainer { value: 11, position: 4, counter: 4 }, - TestContainer { value: 33, position: 2, counter: 2 }, - TestContainer { value: 99, position: 5, counter: 5 }, - TestContainer { value: 66, position: 1, counter: 1 } - ]; - let deduped_array = [ - TestContainer { value: 66, position: 1, counter: 1 }, - TestContainer { value: 33, position: 2, counter: 2 }, - TestContainer { value: 88, position: 3, counter: 3 }, - TestContainer { value: 11, position: 4, counter: 4 }, - TestContainer { value: 99, position: 5, counter: 5 } - ]; - assert_eq(dedupe_array(original_array), deduped_array); - } - - #[test] - fn dedupe_array_no_duplicates_padded_empty() { - let original_array = [ - TestContainer { value: 88, position: 3, counter: 3 }, - TestContainer { value: 11, position: 4, counter: 4 }, - TestContainer { value: 33, position: 2, counter: 2 }, - TestContainer { value: 99, position: 5, counter: 5 }, - TestContainer { value: 66, position: 1, counter: 1 }, - TestContainer::empty(), - TestContainer::empty(), - TestContainer::empty() - ]; - let deduped_array = [ - TestContainer { value: 66, position: 1, counter: 1 }, - TestContainer { value: 33, position: 2, counter: 2 }, - TestContainer { value: 88, position: 3, counter: 3 }, - TestContainer { value: 11, position: 4, counter: 4 }, - TestContainer { value: 99, position: 5, counter: 5 }, - TestContainer::empty(), - TestContainer::empty(), - TestContainer::empty() - ]; - assert_eq(dedupe_array(original_array), deduped_array); - } - - #[test] - fn dedupe_array_all_duplicates() { - let original_array = [ - TestContainer { value: 55, position: 1, counter: 8 }, - TestContainer { value: 33, position: 1, counter: 2 }, - TestContainer { value: 11, position: 1, counter: 4 }, - TestContainer { value: 88, position: 1, counter: 3 }, - TestContainer { value: 99, position: 1, counter: 5 }, - TestContainer { value: 77, position: 1, counter: 9 }, - TestContainer { value: 66, position: 1, counter: 1 }, - TestContainer { value: 44, position: 1, counter: 7 }, - TestContainer { value: 22, position: 1, counter: 6 } - ]; - let deduped_array = pad_end( - [TestContainer { value: 77, position: 1, counter: 9 }], - TestContainer::empty() - ); - assert_eq(dedupe_array(original_array), deduped_array); - } -} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array/get_deduped_hints.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array/get_deduped_hints.nr deleted file mode 100644 index 2f88b95392e9..000000000000 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array/get_deduped_hints.nr +++ /dev/null @@ -1,49 +0,0 @@ -use crate::{ - abis::side_effect::{Ordered, Positioned}, traits::Empty, - utils::arrays::{ - array_merge, - assert_combined_deduped_array::sort_by_position_then_counter::compare_by_position_then_counter, - get_sorted_tuple::get_sorted_tuple -} -}; - -struct DedupedHints { - combined_indexes: [u32; N], - run_lengths: [u32; N], -} - -impl Eq for DedupedHints { - fn eq(self, other: Self) -> bool { - (self.combined_indexes == other.combined_indexes) & (self.run_lengths == other.run_lengths) - } -} - -pub fn get_deduped_hints( - original_array_lt: [T; N], - original_array_gte: [T; N] -) -> DedupedHints where T: Positioned + Ordered + Empty + Eq { - let mut combined_indexes = [0; N]; - let mut run_lengths = BoundedVec::new(); - - let merged = array_merge(original_array_lt, original_array_gte); - let sorted = get_sorted_tuple(merged, compare_by_position_then_counter); - let mut prev_position = sorted[0].elem.position(); - let mut run_length = 0; - for i in 0..sorted.len() { - combined_indexes[sorted[i].original_index] = i; - - let position = sorted[i].elem.position(); - if position != 0 { - if position != prev_position { - run_lengths.push(run_length); - run_length = 1; - } else { - run_length += 1; - } - prev_position = position; - } - } - run_lengths.push(run_length); - - DedupedHints { combined_indexes, run_lengths: run_lengths.storage } -} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array/sort_by_position_then_counter.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array/sort_by_position_then_counter.nr deleted file mode 100644 index b017e68ec0f4..000000000000 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_deduped_array/sort_by_position_then_counter.nr +++ /dev/null @@ -1,53 +0,0 @@ -use crate::{abis::side_effect::{Positioned, Ordered}}; - -pub fn compare_by_position_then_counter(a: T, b: T) -> bool where T: Positioned + Ordered { - if a.position() == b.position() { - (a.counter() == 0) | (b.counter() > a.counter()) - } else { - (b.position() == 0) | ((a.position() != 0) & a.position().lt(b.position())) - } -} - -pub fn sort_by_position_then_counter(array: [T; N]) -> [T; N] where T: Positioned + Ordered { - array.sort_via(|a, b| compare_by_position_then_counter(a, b)) -} - -mod tests { - use crate::utils::arrays::assert_combined_deduped_array::{ - assert_deduped_array::tests::TestContainer, - sort_by_position_then_counter::sort_by_position_then_counter - }; - - #[test] - fn sort_by_position_then_counter_empty_padded() { - let original_array = [ - TestContainer { value: 55, position: 4, counter: 8 }, - TestContainer { value: 11, position: 3, counter: 5 }, - TestContainer { value: 88, position: 1, counter: 4 }, - TestContainer { value: 44, position: 3, counter: 2 }, - TestContainer { value: 33, position: 1, counter: 1 }, - TestContainer { value: 66, position: 5, counter: 7 }, - TestContainer { value: 99, position: 4, counter: 9 }, - TestContainer { value: 77, position: 2, counter: 3 }, - TestContainer { value: 22, position: 3, counter: 6 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 } - ]; - let expected = [ - TestContainer { value: 33, position: 1, counter: 1 }, - TestContainer { value: 88, position: 1, counter: 4 }, - TestContainer { value: 77, position: 2, counter: 3 }, - TestContainer { value: 44, position: 3, counter: 2 }, - TestContainer { value: 11, position: 3, counter: 5 }, - TestContainer { value: 22, position: 3, counter: 6 }, - TestContainer { value: 55, position: 4, counter: 8 }, - TestContainer { value: 99, position: 4, counter: 9 }, - TestContainer { value: 66, position: 5, counter: 7 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 }, - TestContainer { value: 0, position: 0, counter: 0 } - ]; - assert_eq(sort_by_position_then_counter(original_array), expected); - } -} From 80cc48673f853691e880ee36b9e025ed2f4ef695 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Mon, 2 Sep 2024 19:04:43 +0000 Subject: [PATCH 07/14] Generate all hints before validating. --- .../components/public_tail_output_composer.nr | 50 +++----- .../combine_data.nr | 26 +--- .../generate_output_hints.nr | 112 ++++++++++++++++++ .../generate_public_data_leaves.nr | 70 +++++++---- .../public_tail_output_validator.nr | 46 +++---- .../output_hints.nr | 70 ----------- .../validate_public_data_leaves.nr | 47 +++----- .../src/public_kernel_tail.nr | 43 +++---- .../public_validation_request_processor.nr | 17 +-- .../crates/types/src/utils/arrays.nr | 2 +- 10 files changed, 241 insertions(+), 242 deletions(-) create mode 100644 noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_output_hints.nr delete mode 100644 noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/output_hints.nr diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer.nr index c9641eef4d2a..a34644c7d31c 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer.nr @@ -1,63 +1,47 @@ mod combine_data; +mod generate_output_hints; mod generate_overridable_public_data_writes; mod generate_public_data_leaves; -use crate::components::public_tail_output_composer::{ - combine_data::combine_data, - generate_overridable_public_data_writes::{generate_overridable_public_data_writes, LinkedIndexHint}, - generate_public_data_leaves::generate_public_data_leaves -}; +use generate_output_hints::{LinkedIndexHint, OutputHints, SiloedNoteHashHint}; + +use crate::components::public_tail_output_composer::{combine_data::combine_data, generate_output_hints::generate_output_hints}; use dep::types::{ - abis::{ - kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PublicKernelCircuitPublicInputs}, - public_data_write::OverridablePublicDataWrite -}, - constants::MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - data::{OverridablePublicDataTreeLeaf, PublicDataLeafHint}, - partial_state_reference::PartialStateReference, utils::arrays::array_merge + abis::{kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PublicKernelCircuitPublicInputs}}, + data::PublicDataLeafHint, partial_state_reference::PartialStateReference }; -struct PublicTailOutputComposer { +struct PublicTailOutputComposer { previous_kernel: PublicKernelCircuitPublicInputs, start_state: PartialStateReference, - public_data_writes: [OverridablePublicDataWrite; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - public_data_leaves: [OverridablePublicDataTreeLeaf; NUM_HINTS], - public_data_linked_index_hints: [LinkedIndexHint; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + public_data_leaf_hints: [PublicDataLeafHint; NUM_PUBLIC_DATA_LEAVES], } -impl PublicTailOutputComposer { +impl PublicTailOutputComposer { pub fn new( previous_kernel: PublicKernelCircuitPublicInputs, start_state: PartialStateReference, - public_data_leaf_hints: [PublicDataLeafHint; NUM_HINTS] + public_data_leaf_hints: [PublicDataLeafHint; NUM_PUBLIC_DATA_LEAVES] ) -> Self { - let combined_writes = array_merge( - previous_kernel.end_non_revertible.public_data_update_requests, - previous_kernel.end.public_data_update_requests - ); - let public_data_leaves = generate_public_data_leaves( - previous_kernel.validation_requests.public_data_reads, - combined_writes, - public_data_leaf_hints - ); - let (public_data_writes, public_data_linked_index_hints) = generate_overridable_public_data_writes(combined_writes, public_data_leaves); - PublicTailOutputComposer { previous_kernel, start_state, public_data_writes, public_data_leaves, public_data_linked_index_hints } + PublicTailOutputComposer { previous_kernel, start_state, public_data_leaf_hints } } - pub fn finish(self) -> KernelCircuitPublicInputs { + pub fn finish(self) -> (KernelCircuitPublicInputs, OutputHints) { + let output_hints = generate_output_hints(self.previous_kernel, self.public_data_leaf_hints); + let end = combine_data( self.previous_kernel.end_non_revertible, self.previous_kernel.end, - self.public_data_writes + output_hints ); - KernelCircuitPublicInputs { + (KernelCircuitPublicInputs { rollup_validation_requests: self.previous_kernel.validation_requests.for_rollup, end, constants: self.previous_kernel.constants, start_state: self.start_state, revert_code: self.previous_kernel.revert_code, fee_payer: self.previous_kernel.fee_payer - } + }, output_hints) } } diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/combine_data.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/combine_data.nr index 1831921d2ab1..e8e93c8a02db 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/combine_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/combine_data.nr @@ -1,36 +1,22 @@ +use crate::components::public_tail_output_composer::generate_output_hints::OutputHints; use dep::types::{ abis::{ accumulated_data::{CombinedAccumulatedData, PublicAccumulatedData}, - log_hash::{LogHash, ScopedLogHash}, nullifier::Nullifier, - public_data_write::OverridablePublicDataWrite + log_hash::{LogHash, ScopedLogHash}, nullifier::Nullifier }, - constants::{MAX_NOTE_HASHES_PER_TX, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX}, hash::silo_note_hash, utils::arrays::{array_merge, dedupe_array, sort_by_counter_asc} }; -unconstrained pub fn combine_data( +unconstrained pub fn combine_data( non_revertible: PublicAccumulatedData, revertible: PublicAccumulatedData, - public_data_writes: [OverridablePublicDataWrite; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] + output_hints: OutputHints ) -> CombinedAccumulatedData { - let mut note_hashes = [0; MAX_NOTE_HASHES_PER_TX]; - let sorted_unsiloed_note_hashes = sort_by_counter_asc(array_merge(non_revertible.note_hashes, revertible.note_hashes)); - let tx_hash = non_revertible.nullifiers[0].value; - for i in 0..sorted_unsiloed_note_hashes.len() { - let note_hash = sorted_unsiloed_note_hashes[i]; - note_hashes[i] = if note_hash.counter() == 0 { - // If counter is zero, the note hash is either empty or is emitted from private and has been siloed in private_kernel_tail_to_public. - note_hash.value() - } else { - silo_note_hash(note_hash, tx_hash, i) - }; - } - let nullifiers = sort_by_counter_asc(array_merge(non_revertible.nullifiers, revertible.nullifiers)).map(|n: Nullifier| n.value); let l2_to_l1_msgs = sort_by_counter_asc(array_merge(non_revertible.l2_to_l1_msgs, revertible.l2_to_l1_msgs)); - let public_data_update_requests = dedupe_array(public_data_writes); + let public_data_update_requests = dedupe_array(output_hints.public_data_writes); let note_encrypted_logs_hashes = sort_by_counter_asc( array_merge( @@ -57,7 +43,7 @@ unconstrained pub fn combine_data( let unencrypted_log_preimages_length = unencrypted_logs_hashes.fold(0, |a, b: ScopedLogHash| a + b.log_hash.length); CombinedAccumulatedData { - note_hashes, + note_hashes: output_hints.siloed_note_hashes, nullifiers, l2_to_l1_msgs, note_encrypted_logs_hashes, diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_output_hints.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_output_hints.nr new file mode 100644 index 000000000000..8938c0e166c4 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_output_hints.nr @@ -0,0 +1,112 @@ +use crate::components::public_tail_output_composer::{ + generate_overridable_public_data_writes::{generate_overridable_public_data_writes, LinkedIndexHint}, + generate_public_data_leaves::generate_public_data_leaves +}; +use dep::types::{ + abis::{ + kernel_circuit_public_inputs::PublicKernelCircuitPublicInputs, + public_data_write::OverridablePublicDataWrite +}, + constants::{ + MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, MAX_L2_TO_L1_MSGS_PER_TX, + MAX_UNENCRYPTED_LOGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX, + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX +}, + data::{OverridablePublicDataTreeLeaf, PublicDataLeafHint}, hash::silo_note_hash, traits::Empty, + utils::arrays::{array_merge, CombinedOrderHint, get_combined_order_hints_asc, sort_by_counter_asc, SortedResult} +}; + +struct SiloedNoteHashHint { + siloed_note_hash: Field, + index: u32, +} + +impl Empty for SiloedNoteHashHint { + fn empty() -> Self { + SiloedNoteHashHint { siloed_note_hash: 0, index: 0 } + } +} + +impl Eq for SiloedNoteHashHint { + fn eq(self, other: Self) -> bool { + (self.siloed_note_hash == other.siloed_note_hash) & (self.index == other.index) + } +} + +struct OutputHints { + siloed_note_hashes: [Field; MAX_NOTE_HASHES_PER_TX], + siloed_note_hash_hints: [SiloedNoteHashHint; MAX_NOTE_HASHES_PER_TX], + sorted_note_hash_hints: [CombinedOrderHint; MAX_NOTE_HASHES_PER_TX], + sorted_nullifier_hints: [CombinedOrderHint; MAX_NULLIFIERS_PER_TX], + sorted_l2_to_l1_msg_hints: [CombinedOrderHint; MAX_L2_TO_L1_MSGS_PER_TX], + sorted_note_encrypted_log_hash_hints: [CombinedOrderHint; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], + sorted_encrypted_log_hash_hints: [CombinedOrderHint; MAX_ENCRYPTED_LOGS_PER_TX], + sorted_unencrypted_log_hash_hints: [CombinedOrderHint; MAX_UNENCRYPTED_LOGS_PER_TX], + public_data_writes: [OverridablePublicDataWrite; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + public_data_leaves: [OverridablePublicDataTreeLeaf; NUM_PUBLIC_DATA_LEAVES], + unique_slot_index_hints: SortedResult, + public_data_linked_index_hints: [LinkedIndexHint; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], +} + +unconstrained pub fn generate_output_hints( + previous_kernel: PublicKernelCircuitPublicInputs, + public_data_leaf_hints: [PublicDataLeafHint; NUM_PUBLIC_DATA_LEAVES] +) -> OutputHints { + let non_revertible = previous_kernel.end_non_revertible; + let revertible = previous_kernel.end; + + // Note hashes. + let mut siloed_note_hashes = [0; MAX_NOTE_HASHES_PER_TX]; + let mut siloed_note_hash_hints = [SiloedNoteHashHint::empty(); MAX_NOTE_HASHES_PER_TX]; + let sorted_unsiloed_note_hashes = sort_by_counter_asc(array_merge(non_revertible.note_hashes, revertible.note_hashes)); + let tx_hash = non_revertible.nullifiers[0].value; + for i in 0..sorted_unsiloed_note_hashes.len() { + let note_hash = sorted_unsiloed_note_hashes[i]; + let siloed_note_hash = if note_hash.counter() == 0 { + // If counter is zero, the note hash is either empty or is emitted from private and has been siloed in private_kernel_tail_to_public. + note_hash.value() + } else { + silo_note_hash(note_hash, tx_hash, i) + }; + siloed_note_hashes[i] = siloed_note_hash; + if siloed_note_hash != 0 { + siloed_note_hash_hints[i] = SiloedNoteHashHint { siloed_note_hash, index: i }; + } + } + + // Public data. + let combined_writes = array_merge( + previous_kernel.end_non_revertible.public_data_update_requests, + previous_kernel.end.public_data_update_requests + ); + let (public_data_leaves, unique_slot_index_hints) = generate_public_data_leaves( + previous_kernel.validation_requests.public_data_reads, + combined_writes, + public_data_leaf_hints + ); + let (public_data_writes, public_data_linked_index_hints) = generate_overridable_public_data_writes(combined_writes, public_data_leaves); + + OutputHints { + siloed_note_hashes, + siloed_note_hash_hints, + sorted_note_hash_hints: get_combined_order_hints_asc(non_revertible.note_hashes, revertible.note_hashes), + sorted_nullifier_hints: get_combined_order_hints_asc(non_revertible.nullifiers, revertible.nullifiers), + sorted_l2_to_l1_msg_hints: get_combined_order_hints_asc(non_revertible.l2_to_l1_msgs, revertible.l2_to_l1_msgs), + sorted_note_encrypted_log_hash_hints: get_combined_order_hints_asc( + non_revertible.note_encrypted_logs_hashes, + revertible.note_encrypted_logs_hashes + ), + sorted_encrypted_log_hash_hints: get_combined_order_hints_asc( + non_revertible.encrypted_logs_hashes, + revertible.encrypted_logs_hashes + ), + sorted_unencrypted_log_hash_hints: get_combined_order_hints_asc( + non_revertible.unencrypted_logs_hashes, + revertible.unencrypted_logs_hashes + ), + public_data_writes, + public_data_leaves, + unique_slot_index_hints, + public_data_linked_index_hints + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_public_data_leaves.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_public_data_leaves.nr index c1bdc0fe7cee..35bf6388954b 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_public_data_leaves.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_public_data_leaves.nr @@ -1,7 +1,7 @@ use dep::types::{ abis::{public_data_read::PublicDataRead, public_data_update_request::PublicDataUpdateRequest}, - data::{OverridablePublicDataTreeLeaf, PublicDataLeafHint, PublicDataTreeLeaf}, - utils::arrays::get_sorted_tuple + data::{OverridablePublicDataTreeLeaf, PublicDataLeafHint, PublicDataTreeLeaf}, traits::Empty, + utils::arrays::{get_sorted_hints, SortedResult} }; struct SlotIndex { @@ -10,6 +10,18 @@ struct SlotIndex { counter: u32, } +impl Eq for SlotIndex { + fn eq(self, other: Self) -> bool { + (self.slot == other.slot) & (self.index == other.index) & (self.counter == other.counter) + } +} + +impl Empty for SlotIndex { + fn empty() -> Self { + SlotIndex { slot: 0, index: 0, counter: 0 } + } +} + fn compare_by_slot_then_index(a: SlotIndex, b: SlotIndex) -> bool { if a.slot == b.slot { (a.index == 0) | (b.index > a.index) @@ -26,49 +38,61 @@ unconstrained pub fn generate_public_data_leaves [OverridablePublicDataTreeLeaf; NUM_HINTS] { - let mut slot_indexes: BoundedVec = BoundedVec::new(); +) -> ([OverridablePublicDataTreeLeaf; NUM_HINTS], SortedResult) { + // Combine reads and writes. The combined data has the slots and original indexes. + // Original indexes for writes are modified to have an offset of NUM_READS, ensuring that writes are placed after reads. + let mut combined_data: BoundedVec = BoundedVec::new(); for i in 0..reads.len() { let read = reads[i]; if read.leaf_slot != 0 { - slot_indexes.push(SlotIndex { slot: read.leaf_slot, index: i, counter: 0 }); + combined_data.push(SlotIndex { slot: read.leaf_slot, index: i, counter: 0 }); } } for i in 0..writes.len() { let write = writes[i]; if write.leaf_slot != 0 { - slot_indexes.push(SlotIndex { slot: write.leaf_slot, index: i + NUM_READS, counter: write.counter }); + combined_data.push(SlotIndex { slot: write.leaf_slot, index: i + NUM_READS, counter: write.counter }); } } - let sorted_slot_indexes = slot_indexes.storage.sort_via(compare_by_slot_then_index); + // Sort the combined data by slot then index. + // Find the data with unique slots from the sorted result. + let sorted_combined_data = combined_data.storage.sort_via(compare_by_slot_then_index); let mut prev_slot = 0; let mut override_counter = 0; - let mut unique_slot_indexes: BoundedVec = BoundedVec::new(); - for sorted in sorted_slot_indexes { - if sorted.slot != 0 { - if sorted.slot != prev_slot { - unique_slot_indexes.push(sorted); - prev_slot = sorted.slot; - override_counter = 0; - } - if (sorted.index >= NUM_READS) - & ((override_counter == 0) | (sorted.counter < override_counter)) { - override_counter = sorted.counter; - unique_slot_indexes.storage[unique_slot_indexes.len() - 1].counter = override_counter; + let mut data_with_unique_slot: BoundedVec = BoundedVec::new(); + for i in 0..combined_data.len() { + let data = sorted_combined_data[i]; + if data.slot != prev_slot { + // Found an unique slot. + data_with_unique_slot.push(data); + prev_slot = data.slot; + override_counter = 0; + } + if data.index >= NUM_READS { + // Found a write. + // If it's the first write, update the override_counter of the data with the same slot. + if (override_counter == 0) | (data.counter < override_counter) { + override_counter = data.counter; + data_with_unique_slot.storage[data_with_unique_slot.len() - 1].counter = override_counter; } } } + let sorted_unique_slots = data_with_unique_slot.storage.map(|d: SlotIndex| d.slot); - let sorted_unique_slot_indexes = unique_slot_indexes.storage.sort_via(compare_by_index); + // Sort the data by original index. let mut leaves = [OverridablePublicDataTreeLeaf::empty(); NUM_HINTS]; - for i in 0..unique_slot_indexes.len() { - let sorted = sorted_unique_slot_indexes[i]; + let mut sorted_index_hints = [0; NUM_HINTS]; + let sorted_result = get_sorted_hints(data_with_unique_slot.storage, compare_by_index); + for i in 0..data_with_unique_slot.len() { + let sorted = sorted_result.sorted_array[i]; + // The provided hints should've been sorted. let hint = hints[i]; let exists = hint.preimage.slot == sorted.slot; let value = if exists { hint.preimage.value } else { 0 }; leaves[i] = OverridablePublicDataTreeLeaf { leaf: PublicDataTreeLeaf { slot: sorted.slot, value }, override_counter: sorted.counter }; + sorted_index_hints[sorted_result.sorted_index_hints[i]] = i; } - leaves + (leaves, SortedResult { sorted_array: sorted_unique_slots, sorted_index_hints }) } diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator.nr index 15e309aad69d..0bba659f052c 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator.nr @@ -1,14 +1,12 @@ -mod output_hints; mod validate_linked_public_data_writes; mod validate_public_data_leaves; use crate::components::{ public_tail_output_validator::{ - output_hints::{generate_output_hints, OutputHints, SiloedNoteHashHint}, validate_linked_public_data_writes::validate_linked_public_data_writes, validate_public_data_leaves::validate_public_data_leaves }, - public_tail_output_composer::LinkedIndexHint + public_tail_output_composer::{OutputHints, SiloedNoteHashHint} }; use dep::types::{ abis::{ @@ -25,38 +23,23 @@ use dep::types::{ } }; -struct PublicTailOutputValidator { +struct PublicTailOutputValidator { output: KernelCircuitPublicInputs, previous_kernel: PublicKernelCircuitPublicInputs, start_state: PartialStateReference, - public_data_writes: [OverridablePublicDataWrite; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - public_data_leaves: [OverridablePublicDataTreeLeaf; N], - public_data_linked_index_hints: [LinkedIndexHint; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - public_data_leaf_hints: [PublicDataLeafHint; N], - hints: OutputHints + hints: OutputHints, + public_data_leaf_hints: [PublicDataLeafHint; NUM_PUBLIC_DATA_LEAVES], } -impl PublicTailOutputValidator { +impl PublicTailOutputValidator { pub fn new( output: KernelCircuitPublicInputs, previous_kernel: PublicKernelCircuitPublicInputs, start_state: PartialStateReference, - public_data_writes: [OverridablePublicDataWrite; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - public_data_leaves: [OverridablePublicDataTreeLeaf; N], - public_data_linked_index_hints: [LinkedIndexHint; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - public_data_leaf_hints: [PublicDataLeafHint; N] + hints: OutputHints, + public_data_leaf_hints: [PublicDataLeafHint; NUM_PUBLIC_DATA_LEAVES] ) -> Self { - let hints = generate_output_hints(previous_kernel, output); - PublicTailOutputValidator { - output, - previous_kernel, - start_state, - public_data_writes, - public_data_leaves, - public_data_linked_index_hints, - public_data_leaf_hints, - hints - } + PublicTailOutputValidator { output, previous_kernel, start_state, hints, public_data_leaf_hints } } pub fn validate(self) { @@ -171,7 +154,8 @@ impl PublicTailOutputValidator { fn validate_deduped_public_data_writes(self) { validate_public_data_leaves( - self.public_data_leaves, + self.hints.public_data_leaves, + self.hints.unique_slot_index_hints, self.public_data_leaf_hints, self.start_state.public_data_tree.root ); @@ -179,18 +163,18 @@ impl PublicTailOutputValidator { assert_combined_transformed_array( self.previous_kernel.end_non_revertible.public_data_update_requests, self.previous_kernel.end.public_data_update_requests, - self.public_data_writes, + self.hints.public_data_writes, |from: PublicDataUpdateRequest, to: OverridablePublicDataWrite| to.write == from // override_counter is checked in validate_linked_public_data_writes. ); validate_linked_public_data_writes( - self.public_data_writes, - self.public_data_leaves, - self.public_data_linked_index_hints + self.hints.public_data_writes, + self.hints.public_data_leaves, + self.hints.public_data_linked_index_hints ); assert_deduped_array( - self.public_data_writes, + self.hints.public_data_writes, self.output.end.public_data_update_requests ); } diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/output_hints.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/output_hints.nr deleted file mode 100644 index 3d7bf824602b..000000000000 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/output_hints.nr +++ /dev/null @@ -1,70 +0,0 @@ -use dep::types::{ - abis::{kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PublicKernelCircuitPublicInputs}}, - constants::{ - MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, MAX_L2_TO_L1_MSGS_PER_TX, - MAX_UNENCRYPTED_LOGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX -}, - hash::silo_note_hash, traits::Empty, - utils::arrays::{array_merge, CombinedOrderHint, get_combined_order_hints_asc} -}; - -struct SiloedNoteHashHint { - siloed_note_hash: Field, - index: u32, -} - -impl Empty for SiloedNoteHashHint { - fn empty() -> Self { - SiloedNoteHashHint { siloed_note_hash: 0, index: 0 } - } -} - -impl Eq for SiloedNoteHashHint { - fn eq(self, other: Self) -> bool { - (self.siloed_note_hash == other.siloed_note_hash) & (self.index == other.index) - } -} - -struct OutputHints { - siloed_note_hash_hints: [SiloedNoteHashHint; MAX_NOTE_HASHES_PER_TX], - sorted_note_hash_hints: [CombinedOrderHint; MAX_NOTE_HASHES_PER_TX], - sorted_nullifier_hints: [CombinedOrderHint; MAX_NULLIFIERS_PER_TX], - sorted_l2_to_l1_msg_hints: [CombinedOrderHint; MAX_L2_TO_L1_MSGS_PER_TX], - sorted_note_encrypted_log_hash_hints: [CombinedOrderHint; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], - sorted_encrypted_log_hash_hints: [CombinedOrderHint; MAX_ENCRYPTED_LOGS_PER_TX], - sorted_unencrypted_log_hash_hints: [CombinedOrderHint; MAX_UNENCRYPTED_LOGS_PER_TX], -} - -unconstrained pub fn generate_output_hints( - previous_kernel: PublicKernelCircuitPublicInputs, - output: KernelCircuitPublicInputs -) -> OutputHints { - let non_revertible = previous_kernel.end_non_revertible; - let revertible = previous_kernel.end; - - let mut siloed_note_hash_hints = output.end.note_hashes.map(|siloed_note_hash| SiloedNoteHashHint { siloed_note_hash, index: 0 }); - for i in 0..siloed_note_hash_hints.len() { - if siloed_note_hash_hints[i].siloed_note_hash != 0 { - siloed_note_hash_hints[i].index = i; - } - } - - OutputHints { - siloed_note_hash_hints, - sorted_note_hash_hints: get_combined_order_hints_asc(non_revertible.note_hashes, revertible.note_hashes), - sorted_nullifier_hints: get_combined_order_hints_asc(non_revertible.nullifiers, revertible.nullifiers), - sorted_l2_to_l1_msg_hints: get_combined_order_hints_asc(non_revertible.l2_to_l1_msgs, revertible.l2_to_l1_msgs), - sorted_note_encrypted_log_hash_hints: get_combined_order_hints_asc( - non_revertible.note_encrypted_logs_hashes, - revertible.note_encrypted_logs_hashes - ), - sorted_encrypted_log_hash_hints: get_combined_order_hints_asc( - non_revertible.encrypted_logs_hashes, - revertible.encrypted_logs_hashes - ), - sorted_unencrypted_log_hash_hints: get_combined_order_hints_asc( - non_revertible.unencrypted_logs_hashes, - revertible.unencrypted_logs_hashes - ) - } -} diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_leaves.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_leaves.nr index 2fb6387bce6a..fb225f606184 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_leaves.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_leaves.nr @@ -1,35 +1,16 @@ use dep::types::{ - abis::public_data_read::PublicDataRead, data::{OverridablePublicDataTreeLeaf, PublicDataLeafHint}, - merkle_tree::conditionally_assert_check_membership, - utils::arrays::get_sorted_tuple::get_sorted_tuple + data::{OverridablePublicDataTreeLeaf, PublicDataLeafHint}, + merkle_tree::conditionally_assert_check_membership, utils::arrays::SortedResult }; -fn sort_by_leaf_slot(a: OverridablePublicDataTreeLeaf, b: OverridablePublicDataTreeLeaf) -> bool { - (b.leaf.slot == 0) | ((a.leaf.slot != 0) & a.leaf.slot.lt(b.leaf.slot)) -} - -unconstrained fn generate_unique_leaf_slot_hints(leaves: [OverridablePublicDataTreeLeaf; N]) -> ([Field; N], [u32; N]) { - let mut sorted_leaf_slots = [0; N]; - let mut sorted_leaf_slot_indexes = [N - 1; N]; - - let sorted_tuples = get_sorted_tuple(leaves, sort_by_leaf_slot); - for i in 0..N { - let elem = sorted_tuples[i].elem; - sorted_leaf_slots[i] = elem.leaf.slot; - - let original_index = sorted_tuples[i].original_index; - sorted_leaf_slot_indexes[original_index] = i; - } - - (sorted_leaf_slots, sorted_leaf_slot_indexes) -} - // All non-zero leaf slots in leaves must be unique, ensuring that writes with the same leaf slot are grouped into a single group. fn validate_unique_leaf_slots( leaves: [OverridablePublicDataTreeLeaf; N], - sorted_leaf_slots: [Field; N], - sorted_leaf_slot_indexes: [u32; N] + unique_slot_index_hints: SortedResult ) { + let sorted_leaf_slots = unique_slot_index_hints.sorted_array; + let sorted_leaf_slot_indexes = unique_slot_index_hints.sorted_index_hints; + let mut prev_slot = 0; let mut num_non_zeros = 0; for i in 0..N { @@ -53,9 +34,9 @@ fn validate_unique_leaf_slots( } } -fn validate_memberships( - leaves: [OverridablePublicDataTreeLeaf; NUM_LEAVES], - leaf_hints: [PublicDataLeafHint; NUM_HINTS], +fn validate_memberships( + leaves: [OverridablePublicDataTreeLeaf; N], + leaf_hints: [PublicDataLeafHint; N], tree_root: Field ) { for i in 0..leaves.len() { @@ -82,12 +63,12 @@ fn validate_memberships( } } -pub fn validate_public_data_leaves( - leaves: [OverridablePublicDataTreeLeaf; NUM_LEAVES], - leaf_hints: [PublicDataLeafHint; NUM_HINTS], +pub fn validate_public_data_leaves( + leaves: [OverridablePublicDataTreeLeaf; N], + unique_slot_index_hints: SortedResult, + leaf_hints: [PublicDataLeafHint; N], tree_root: Field ) { - let (sorted_leaf_slots, sorted_leaf_slot_indexes) = generate_unique_leaf_slot_hints(leaves); - validate_unique_leaf_slots(leaves, sorted_leaf_slots, sorted_leaf_slot_indexes); + validate_unique_leaf_slots(leaves, unique_slot_index_hints); validate_memberships(leaves, leaf_hints, tree_root); } diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr index 2d3db2c79910..896d52746a82 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr @@ -1,23 +1,22 @@ use crate::{ components::{ previous_kernel_validator::PreviousKernelValidator, - public_tail_output_composer::{LinkedIndexHint, PublicTailOutputComposer}, + public_tail_output_composer::{OutputHints, PublicTailOutputComposer}, public_tail_output_validator::PublicTailOutputValidator }, public_kernel_phase::PublicKernelPhase }; -use dep::reset_kernel_lib::{NullifierReadRequestHints, NullifierNonExistentReadRequestHints, PublicValidationRequestProcessor}; +use dep::reset_kernel_lib::{ + NullifierReadRequestHints, NullifierNonExistentReadRequestHints, PublicValidationRequestProcessor, + public_data_read_request_hints::{build_public_data_read_request_hints, PublicDataReadRequestHints} +}; use dep::types::{ - abis::{ - kernel_circuit_public_inputs::KernelCircuitPublicInputs, public_kernel_data::PublicKernelData, - public_data_write::OverridablePublicDataWrite -}, + abis::{kernel_circuit_public_inputs::KernelCircuitPublicInputs, public_kernel_data::PublicKernelData}, constants::{ - MAX_PUBLIC_DATA_HINTS, MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_PUBLIC_DATA_HINTS, MAX_PUBLIC_DATA_READS_PER_TX, PUBLIC_KERNEL_SETUP_INDEX, PUBLIC_KERNEL_APP_LOGIC_INDEX, PUBLIC_KERNEL_TEARDOWN_INDEX }, - data::{OverridablePublicDataTreeLeaf, PublicDataLeafHint}, - partial_state_reference::PartialStateReference + data::PublicDataLeafHint, partial_state_reference::PartialStateReference }; global ALLOWED_PREVIOUS_CIRCUITS = [ @@ -35,15 +34,20 @@ struct PublicKernelTailCircuitPrivateInputs { } impl PublicKernelTailCircuitPrivateInputs { - unconstrained fn generate_output(self) -> (KernelCircuitPublicInputs, [OverridablePublicDataWrite; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], [OverridablePublicDataTreeLeaf; MAX_PUBLIC_DATA_HINTS], [LinkedIndexHint; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]) { - let composer = PublicTailOutputComposer::new( + unconstrained fn generate_output_and_hints(self) -> (KernelCircuitPublicInputs, OutputHints, PublicDataReadRequestHints) { + let (output, output_hints) = PublicTailOutputComposer::new( self.previous_kernel.public_inputs, self.start_state, self.public_data_hints + ).finish(); + + let public_data_read_request_hints = build_public_data_read_request_hints( + self.previous_kernel.public_inputs.validation_requests.public_data_reads, + output_hints.public_data_writes, + output_hints.public_data_leaves ); - ( - composer.finish(), composer.public_data_writes, composer.public_data_leaves, composer.public_data_linked_index_hints - ) + + (output, output_hints, public_data_read_request_hints) } pub fn execute(self) -> KernelCircuitPublicInputs { @@ -51,24 +55,23 @@ impl PublicKernelTailCircuitPrivateInputs { previous_kernel_validator.validate_phase(PublicKernelPhase.TAIL); previous_kernel_validator.validate_proof(ALLOWED_PREVIOUS_CIRCUITS); - let (output, public_data_writes, public_data_leaves, public_data_linked_index_hints) = self.generate_output(); + let (output, output_hints, public_data_read_request_hints) = self.generate_output_and_hints(); PublicValidationRequestProcessor::new( self.previous_kernel.public_inputs, self.nullifier_read_request_hints, self.nullifier_non_existent_read_request_hints, self.start_state.nullifier_tree.root, - public_data_writes, - public_data_leaves + output_hints.public_data_writes, + output_hints.public_data_leaves, + public_data_read_request_hints ).validate(); PublicTailOutputValidator::new( output, self.previous_kernel.public_inputs, self.start_state, - public_data_writes, - public_data_leaves, - public_data_linked_index_hints, + output_hints, self.public_data_hints ).validate(); diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_validation_request_processor.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_validation_request_processor.nr index fb77edac8ae4..00fe7d122a98 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_validation_request_processor.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_validation_request_processor.nr @@ -6,7 +6,7 @@ use crate::{ }, nullifier_read_request_reset::NullifierReadRequestHints, nullifier_non_existent_read_request_reset::NullifierNonExistentReadRequestHints, - public_data_read_request_hints::{build_public_data_read_request_hints, PublicDataReadRequestHints} + public_data_read_request_hints::PublicDataReadRequestHints }; use dep::types::{ abis::{ @@ -26,8 +26,8 @@ struct PublicValidationRequestProcessor, public_data_leaves: [OverridablePublicDataTreeLeaf; NUM_PUBLIC_DATA_LEAVES], + public_data_read_request_hints: PublicDataReadRequestHints, } impl PublicValidationRequestProcessor { @@ -37,7 +37,8 @@ impl PublicVal nullifier_non_existent_read_request_hints: NullifierNonExistentReadRequestHints, nullifier_tree_root: Field, pending_public_data_writes: [OverridablePublicDataWrite; NUN_PUBLIC_DATA_WRITES], - public_data_leaves: [OverridablePublicDataTreeLeaf; NUM_PUBLIC_DATA_LEAVES] + public_data_leaves: [OverridablePublicDataTreeLeaf; NUM_PUBLIC_DATA_LEAVES], + public_data_read_request_hints: PublicDataReadRequestHints ) -> Self { let validation_requests = public_inputs.validation_requests; @@ -46,12 +47,6 @@ impl PublicVal let pending_nullifiers = array_merge(end_non_revertible.nullifiers, end.nullifiers); - let public_data_read_request_hints = build_public_data_read_request_hints( - validation_requests.public_data_reads, - pending_public_data_writes, - public_data_leaves - ); - PublicValidationRequestProcessor { validation_requests, pending_nullifiers, @@ -59,8 +54,8 @@ impl PublicVal nullifier_non_existent_read_request_hints, nullifier_tree_root, pending_public_data_writes, - public_data_read_request_hints, - public_data_leaves + public_data_leaves, + public_data_read_request_hints } } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr index 7f5ddaae3b1c..205a5957e5f3 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr @@ -29,7 +29,7 @@ use assert_split_sorted_transformed_value_arrays::{ }; use assert_sorted_transformed_value_array::{assert_sorted_transformed_value_array, assert_sorted_transformed_value_array_capped_size}; use assert_split_transformed_value_arrays::assert_split_transformed_value_arrays; -use get_sorted_hints::get_sorted_hints; +use get_sorted_hints::{get_sorted_hints, SortedResult}; use sort_by_counter::{sort_by_counter_asc, sort_by_counter_desc}; use crate::traits::{Empty, is_empty}; From aec433de0cfdf56ec68990aad3061d7a26fc4a0a Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Mon, 2 Sep 2024 19:25:56 +0000 Subject: [PATCH 08/14] Breakup files. --- .../public_tail_output_validator.nr | 14 +++- .../validate_public_data_leaf_memberships.nr | 33 +++++++++ .../validate_public_data_leaves.nr | 74 ------------------- .../validate_unique_leaf_slots.nr | 32 ++++++++ 4 files changed, 75 insertions(+), 78 deletions(-) create mode 100644 noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_leaf_memberships.nr delete mode 100644 noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_leaves.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_unique_leaf_slots.nr diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator.nr index 0bba659f052c..6a87e8ba9b16 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator.nr @@ -1,10 +1,12 @@ mod validate_linked_public_data_writes; -mod validate_public_data_leaves; +mod validate_public_data_leaf_memberships; +mod validate_unique_leaf_slots; use crate::components::{ public_tail_output_validator::{ validate_linked_public_data_writes::validate_linked_public_data_writes, - validate_public_data_leaves::validate_public_data_leaves + validate_public_data_leaf_memberships::validate_public_data_leaf_memberships, + validate_unique_leaf_slots::validate_unique_leaf_slots }, public_tail_output_composer::{OutputHints, SiloedNoteHashHint} }; @@ -153,9 +155,13 @@ impl PublicTailOutputValidator( + leaves: [OverridablePublicDataTreeLeaf; N], + leaf_hints: [PublicDataLeafHint; N], + tree_root: Field +) { + for i in 0..leaves.len() { + let leaf = leaves[i].leaf; + let hint = leaf_hints[i]; + if leaf.slot != 0 { + let exists_in_tree = leaf.slot == hint.preimage.slot; + if exists_in_tree { + assert( + leaf.value == hint.preimage.value, "Hinted public data value does not match the value in leaf preimage" + ); + } else { + assert(leaf.value == 0, "Value must be 0 for non-existent public data"); + } + + conditionally_assert_check_membership( + leaf.slot, + exists_in_tree, + hint.preimage, + hint.membership_witness, + tree_root + ); + } + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_leaves.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_leaves.nr deleted file mode 100644 index fb225f606184..000000000000 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_leaves.nr +++ /dev/null @@ -1,74 +0,0 @@ -use dep::types::{ - data::{OverridablePublicDataTreeLeaf, PublicDataLeafHint}, - merkle_tree::conditionally_assert_check_membership, utils::arrays::SortedResult -}; - -// All non-zero leaf slots in leaves must be unique, ensuring that writes with the same leaf slot are grouped into a single group. -fn validate_unique_leaf_slots( - leaves: [OverridablePublicDataTreeLeaf; N], - unique_slot_index_hints: SortedResult -) { - let sorted_leaf_slots = unique_slot_index_hints.sorted_array; - let sorted_leaf_slot_indexes = unique_slot_index_hints.sorted_index_hints; - - let mut prev_slot = 0; - let mut num_non_zeros = 0; - for i in 0..N { - let leaf = leaves[i].leaf; - let sorted_index = if leaf.slot != 0 { - sorted_leaf_slot_indexes[i] - } else { - num_non_zeros += 1; - N - num_non_zeros - }; - let hinted_leaf_slot = sorted_leaf_slots[sorted_index]; - assert_eq(hinted_leaf_slot, leaf.slot); - - let curr_leaf_alot = sorted_leaf_slots[i]; - if leaf.slot != 0 { - assert(prev_slot.lt(curr_leaf_alot)); - } else { - assert_eq(curr_leaf_alot, 0); - } - prev_slot = curr_leaf_alot; - } -} - -fn validate_memberships( - leaves: [OverridablePublicDataTreeLeaf; N], - leaf_hints: [PublicDataLeafHint; N], - tree_root: Field -) { - for i in 0..leaves.len() { - let leaf = leaves[i].leaf; - let hint = leaf_hints[i]; - if leaf.slot != 0 { - let exists_in_tree = leaf.slot == hint.preimage.slot; - if exists_in_tree { - assert( - leaf.value == hint.preimage.value, "Hinted public data value does not match the value in leaf preimage" - ); - } else { - assert(leaf.value == 0, "Value must be 0 for non-existent public data"); - } - - conditionally_assert_check_membership( - leaf.slot, - exists_in_tree, - hint.preimage, - hint.membership_witness, - tree_root - ); - } - } -} - -pub fn validate_public_data_leaves( - leaves: [OverridablePublicDataTreeLeaf; N], - unique_slot_index_hints: SortedResult, - leaf_hints: [PublicDataLeafHint; N], - tree_root: Field -) { - validate_unique_leaf_slots(leaves, unique_slot_index_hints); - validate_memberships(leaves, leaf_hints, tree_root); -} diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_unique_leaf_slots.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_unique_leaf_slots.nr new file mode 100644 index 000000000000..896ffde628a9 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_unique_leaf_slots.nr @@ -0,0 +1,32 @@ +use dep::types::{data::OverridablePublicDataTreeLeaf, utils::arrays::SortedResult}; + +// All non-zero leaf slots in leaves must be unique, ensuring that writes with the same leaf slot are grouped into a single group. +fn validate_unique_leaf_slots( + leaves: [OverridablePublicDataTreeLeaf; N], + unique_slot_index_hints: SortedResult +) { + let sorted_leaf_slots = unique_slot_index_hints.sorted_array; + let sorted_leaf_slot_indexes = unique_slot_index_hints.sorted_index_hints; + + let mut prev_slot = 0; + let mut num_non_zeros = 0; + for i in 0..N { + let leaf = leaves[i].leaf; + let sorted_index = if leaf.slot != 0 { + sorted_leaf_slot_indexes[i] + } else { + num_non_zeros += 1; + N - num_non_zeros + }; + let hinted_leaf_slot = sorted_leaf_slots[sorted_index]; + assert_eq(hinted_leaf_slot, leaf.slot); + + let curr_leaf_alot = sorted_leaf_slots[i]; + if leaf.slot != 0 { + assert(prev_slot.lt(curr_leaf_alot)); + } else { + assert_eq(curr_leaf_alot, 0); + } + prev_slot = curr_leaf_alot; + } +} From db127289b912d701098743ce24b5f771e6106177 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Mon, 2 Sep 2024 20:16:43 +0000 Subject: [PATCH 09/14] Fix types. --- .../src/public_kernel_tail.nr | 8 ++--- .../src/reset/mutable_data_read_request.nr | 30 +++++++------------ 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr index 896d52746a82..c2ae27a2614a 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr @@ -138,18 +138,18 @@ mod tests { ) } - struct PublicKernelTailCircuitPrivateInputsBuilder { + struct PublicKernelTailCircuitPrivateInputsBuilder { previous_kernel: FixtureBuilder, previous_revertible: FixtureBuilder, nullifier_read_request_hints_builder: NullifierReadRequestHintsBuilder, nullifier_non_existent_read_request_hints_builder: NullifierNonExistentReadRequestHintsBuilder, public_data_tree: NonEmptyMerkleTree, - public_data_leaf_preimages: [PublicDataTreeLeafPreimage; N], + public_data_leaf_preimages: [PublicDataTreeLeafPreimage; 6], start_state: PartialStateReference, } - impl PublicKernelTailCircuitPrivateInputsBuilder { - pub fn new() -> PublicKernelTailCircuitPrivateInputsBuilder<6> { + impl PublicKernelTailCircuitPrivateInputsBuilder { + pub fn new() -> PublicKernelTailCircuitPrivateInputsBuilder { let previous_kernel = FixtureBuilder::new().in_vk_tree(PUBLIC_KERNEL_APP_LOGIC_INDEX); let previous_revertible = FixtureBuilder::new(); let nullifier_non_existent_read_request_hints_builder = NullifierNonExistentReadRequestHintsBuilder::new(); diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/mutable_data_read_request.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/mutable_data_read_request.nr index af5b57c1848a..848a88188783 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/mutable_data_read_request.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/mutable_data_read_request.nr @@ -35,7 +35,7 @@ fn validate_pending_read_requests( +fn validate_leaf_data_read_requests( read_requests: [R; READ_REQUEST_LEN], leaf_data: [L; LEAF_DATA_LEN], index_hints: [ReadIndexHint; NUM_HINTS] @@ -149,32 +149,22 @@ mod tests { global READ_REQUEST_LEN = 10; - struct TestBuilder< - let PENDING_VALUE_LEN: u32, - let NUM_LEAF_DATA: u32, - let NUM_PENDING_READS: u32, - let NUM_LEAF_DATA_READS: u32 - >{ + struct TestBuilder { read_requests: [PublicDataRead; READ_REQUEST_LEN], read_request_statuses: [ReadRequestStatus; READ_REQUEST_LEN], - data_writes: [OverridablePublicDataWrite; PENDING_VALUE_LEN], - leaf_data: [TestLeafData; NUM_LEAF_DATA], - pending_read_hints: [ReadIndexHint; NUM_PENDING_READS], - leaf_data_read_hints: [ReadIndexHint; NUM_LEAF_DATA_READS], + data_writes: [OverridablePublicDataWrite; 6], + leaf_data: [TestLeafData; 12], + pending_read_hints: [ReadIndexHint; 5], + leaf_data_read_hints: [ReadIndexHint; 4], num_pending_reads: u32, num_leaf_data_reads: u32, counter: u32, } - impl< - let PENDING_VALUE_LEN: u32, - let NUM_LEAF_DATA_HINTS: u32, - let NUM_PENDING_READS: u32, - let NUM_LEAF_DATA_READS: u32 - > TestBuilder { - pub fn new() -> TestBuilder<6, 12, 5, 4> { - let read_requests = [PublicDataRead::empty(); 10]; - let read_request_statuses = [ReadRequestStatus::empty(); 10]; + impl TestBuilder { + pub fn new() -> TestBuilder { + let read_requests = [PublicDataRead::empty(); READ_REQUEST_LEN]; + let read_request_statuses = [ReadRequestStatus::empty(); READ_REQUEST_LEN]; let leaf_data = pad_end( [ From 9232dce65e3166046c78815d7850581a596ee563 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Mon, 2 Sep 2024 21:25:25 +0000 Subject: [PATCH 10/14] fmt --- .../aztec-nr/aztec/src/oracle/get_public_data_witness.nr | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/oracle/get_public_data_witness.nr b/noir-projects/aztec-nr/aztec/src/oracle/get_public_data_witness.nr index 6ac80d267dc4..956e9586382c 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/get_public_data_witness.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/get_public_data_witness.nr @@ -1,7 +1,4 @@ -use dep::protocol_types::{ - constants::PUBLIC_DATA_TREE_HEIGHT, data::PublicDataTreeLeafPreimage, - utils::arr_copy_slice -}; +use dep::protocol_types::{constants::PUBLIC_DATA_TREE_HEIGHT, data::PublicDataTreeLeafPreimage, utils::arr_copy_slice}; global LEAF_PREIMAGE_LENGTH: u32 = 4; global PUBLIC_DATA_WITNESS: Field = 45; From 958b09e1c623939b1c35fb9052c54826a0a41d52 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Tue, 3 Sep 2024 10:03:57 +0000 Subject: [PATCH 11/14] fmt --- .../aztec-nr/aztec/src/oracle/get_public_data_witness.nr | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/noir-projects/aztec-nr/aztec/src/oracle/get_public_data_witness.nr b/noir-projects/aztec-nr/aztec/src/oracle/get_public_data_witness.nr index 956e9586382c..8c0f76f5eb89 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/get_public_data_witness.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/get_public_data_witness.nr @@ -15,7 +15,10 @@ unconstrained fn get_public_data_witness_oracle( _public_data_tree_index: Field ) -> [Field; PUBLIC_DATA_WITNESS] {} -unconstrained pub fn get_public_data_witness(block_number: u32, public_data_tree_index: Field) -> PublicDataWitness { +unconstrained pub fn get_public_data_witness( + block_number: u32, + public_data_tree_index: Field +) -> PublicDataWitness { let fields = get_public_data_witness_oracle(block_number, public_data_tree_index); PublicDataWitness { index: fields[0], From b838d6c58bab5fcfcae9af3590f19aed2a498fd6 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Thu, 5 Sep 2024 14:29:07 +0000 Subject: [PATCH 12/14] Fix bugs and add tests. --- ...generate_overridable_public_data_writes.nr | 7 +- .../generate_public_data_leaves.nr | 14 +- .../public_tail_output_validator.nr | 27 +-- .../validate_linked_public_data_writes.nr | 159 +++++++++++++++-- .../validate_public_data_leaf_memberships.nr | 2 + .../validate_unique_leaf_slots.nr | 143 ++++++++++++++- ...non_existent_read_request_hints_builder.nr | 4 +- .../crates/types/src/tests/mod.nr | 1 + .../crates/types/src/tests/types.nr | 80 +++++++++ .../crates/types/src/utils/arrays.nr | 4 +- ...combined_sorted_transformed_value_array.nr | 149 ++++++---------- .../get_combined_order_hints.nr | 113 +++++------- .../assert_combined_transformed_array.nr | 108 ++++++++++++ .../src/utils/arrays/assert_deduped_array.nr | 137 ++++++++++++++- ..._exposed_sorted_transformed_value_array.nr | 90 +++------- .../get_order_hints.nr | 69 +++----- .../assert_sorted_transformed_value_array.nr | 91 +++------- ...t_split_sorted_transformed_value_arrays.nr | 164 +++++++----------- .../get_split_order_hints.nr | 49 ++---- .../assert_split_transformed_value_arrays.nr | 41 +---- .../src/utils/arrays/get_sorted_hints.nr | 56 ------ .../src/utils/arrays/get_sorted_result.nr | 59 +++++++ .../types/src/utils/arrays/sort_by_counter.nr | 97 ++++------- 23 files changed, 969 insertions(+), 695 deletions(-) create mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/tests/types.nr delete mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/get_sorted_hints.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/get_sorted_result.nr diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_overridable_public_data_writes.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_overridable_public_data_writes.nr index 4a68320f4a52..9ecb9d699023 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_overridable_public_data_writes.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/generate_overridable_public_data_writes.nr @@ -6,12 +6,11 @@ use dep::types::{ struct LinkedIndexHint { is_first_write: bool, prev_index: u32, - next_index: u32, } impl Empty for LinkedIndexHint { fn empty() -> Self { - LinkedIndexHint { is_first_write: false, prev_index: 0, next_index: 0 } + LinkedIndexHint { is_first_write: false, prev_index: 0 } } } @@ -29,7 +28,6 @@ unconstrained pub fn generate_overridable_public_data_writes write.counter { if (override_counter == 0) | (other.counter < override_counter) { override_counter = other.counter; - next_index = j; } } else if other.counter < write.counter { if other.counter > prev_counter { @@ -54,7 +51,7 @@ unconstrained pub fn generate_overridable_public_data_writes bool { } fn compare_by_index(a: SlotIndex, b: SlotIndex) -> bool { - (b.slot == 0) | ((a.slot != 0) & (a.index < b.index)) + (a.slot != 0) & ((b.slot == 0) | (a.index < b.index)) } unconstrained pub fn generate_public_data_leaves( @@ -82,8 +82,7 @@ unconstrained pub fn generate_public_data_leaves PublicTailOutputValidator( public_data_writes: [OverridablePublicDataWrite; NUM_WRITES], public_data_leaves: [OverridablePublicDataTreeLeaf; NUM_LEAVES], @@ -13,31 +16,163 @@ fn validate_linked_public_data_writes( if hint.is_first_write { // It's linked to a leaf in the tree. let data_hint = public_data_leaves[hint.prev_index]; - assert_eq(data_hint.leaf.slot, write.inner().leaf_slot, "hinted data has different leaf_slot"); + assert_eq(data_hint.leaf.slot, write.inner().leaf_slot, "hinted leaf has different slot"); assert_eq( - data_hint.override_counter, write.counter(), "hinted data does not link to current write" + data_hint.override_counter, write.counter(), "hinted leaf does not link to current write" ); } else { // It's linked to another write for the same leaf slot. let prev = public_data_writes[hint.prev_index]; - assert_eq( - prev.inner().leaf_slot, write.inner().leaf_slot, "hinted write has different leaf_slot" - ); + assert_eq(prev.inner().leaf_slot, write.inner().leaf_slot, "hinted write has different slot"); assert(prev.counter() < write.counter(), "previous write must have a smaller counter"); assert_eq( prev.override_counter, write.counter(), "hinted previous write does not link to current write" ); } } + } +} + +mod tests { + use crate::components::{ + public_tail_output_composer::LinkedIndexHint, + public_tail_output_validator::validate_linked_public_data_writes::validate_linked_public_data_writes + }; + use dep::types::{ + abis::{public_data_write::OverridablePublicDataWrite, public_data_update_request::PublicDataUpdateRequest}, + data::{OverridablePublicDataTreeLeaf, PublicDataTreeLeaf}, tests::utils::pad_end + }; + + struct TestBuilder { + public_data_writes: [OverridablePublicDataWrite; 8], + public_data_leaves: [OverridablePublicDataTreeLeaf; 12], + hints: [LinkedIndexHint; 8] + } + + impl TestBuilder { + pub fn new_empty() -> Self { + let public_data_writes = pad_end([], OverridablePublicDataWrite::empty()); + let public_data_leaves = pad_end([], OverridablePublicDataTreeLeaf::empty()); + let hints = pad_end([], LinkedIndexHint::empty()); + TestBuilder { public_data_writes, public_data_leaves, hints } + } + + pub fn new() -> Self { + let public_data_writes = pad_end( + [ + OverridablePublicDataWrite { write: PublicDataUpdateRequest { leaf_slot: 33, new_value: 300, counter: 30 }, override_counter: 0 }, + OverridablePublicDataWrite { write: PublicDataUpdateRequest { leaf_slot: 22, new_value: 202, counter: 40 }, override_counter: 50 }, + OverridablePublicDataWrite { write: PublicDataUpdateRequest { leaf_slot: 11, new_value: 100, counter: 10 }, override_counter: 0 }, + OverridablePublicDataWrite { write: PublicDataUpdateRequest { leaf_slot: 22, new_value: 201, counter: 20 }, override_counter: 40 }, + OverridablePublicDataWrite { write: PublicDataUpdateRequest { leaf_slot: 22, new_value: 203, counter: 50 }, override_counter: 0 } + ], + OverridablePublicDataWrite::empty() + ); + + let public_data_leaves = pad_end( + [ + OverridablePublicDataTreeLeaf { leaf: PublicDataTreeLeaf { slot: 0, value: 0 }, override_counter: 0 }, + OverridablePublicDataTreeLeaf { leaf: PublicDataTreeLeaf { slot: 22, value: 200 }, override_counter: 20 }, + OverridablePublicDataTreeLeaf { leaf: PublicDataTreeLeaf { slot: 44, value: 0 }, override_counter: 0 }, + OverridablePublicDataTreeLeaf { leaf: PublicDataTreeLeaf { slot: 11, value: 0 }, override_counter: 10 }, + OverridablePublicDataTreeLeaf { leaf: PublicDataTreeLeaf { slot: 33, value: 0 }, override_counter: 30 } + ], + OverridablePublicDataTreeLeaf::empty() + ); - if write.override_counter != 0 { - let next = public_data_writes[hint.next_index]; - let next_hint = hints[hint.next_index]; - assert_eq( - write.override_counter, next.counter(), "hinted next write does not link to current write" + let hints = pad_end( + [ + LinkedIndexHint { is_first_write: true, prev_index: 4 }, + LinkedIndexHint { is_first_write: false, prev_index: 3 }, + LinkedIndexHint { is_first_write: true, prev_index: 3 }, + LinkedIndexHint { is_first_write: true, prev_index: 1 }, + LinkedIndexHint { is_first_write: false, prev_index: 1 } + ], + LinkedIndexHint::empty() ); - assert_eq(next_hint.prev_index, i, "next write has different counter"); - // leaf_slot will be checked when processing the next write. + + TestBuilder { public_data_writes, public_data_leaves, hints } + } + + pub fn execute(self) { + validate_linked_public_data_writes(self.public_data_writes, self.public_data_leaves, self.hints); } } + + #[test] + fn validate_linked_public_data_writes_empty_succeeds() { + let builder = TestBuilder::new_empty(); + builder.execute(); + } + + #[test] + fn validate_linked_public_data_writes_succeeds() { + let builder = TestBuilder::new(); + builder.execute(); + } + + #[test(should_fail_with="hinted leaf has different slot")] + fn validate_linked_public_data_writes_link_prev_to_empty_leaf_fails() { + let mut builder = TestBuilder::new(); + + // Link the first write to an empty leaf. + builder.hints[0].prev_index = 7; + + builder.execute(); + } + + #[test(should_fail_with="hinted leaf does not link to current write")] + fn validate_linked_public_data_writes_link_two_writes_to_same_leaf_fails() { + let mut builder = TestBuilder::new(); + + // Link the write to a leaf with the same slot. + builder.hints[1] = builder.hints[3]; + + builder.execute(); + } + + #[test(should_fail_with="hinted write has different slot")] + fn validate_linked_public_data_writes_link_prev_to_empty_write_fails() { + let mut builder = TestBuilder::new(); + + // Link the write to an empty write. + builder.hints[1].prev_index = 7; + + builder.execute(); + } + + #[test(should_fail_with="previous write must have a smaller counter")] + fn validate_linked_public_data_writes_not_sorted_by_counter_fails() { + let mut builder = TestBuilder::new(); + + // Change from leaf -> write[3] -> write[1] -> write[4] to leaf -> write[1] -> write[3] -> write[4] + let leaf_index = builder.hints[3].prev_index; + // Link the leaf to the 1st write. + builder.public_data_leaves[leaf_index].override_counter = builder.public_data_writes[1].counter(); + // Link the 1st write to the leaf and the 3rd write. + builder.hints[1].is_first_write = true; + builder.hints[1].prev_index = leaf_index; + builder.public_data_writes[1].override_counter = builder.public_data_writes[3].counter(); + // Link the 3rd write to the 1st write and the 4th write. + builder.hints[3].is_first_write = false; + builder.hints[3].prev_index = 1; + builder.public_data_writes[3].override_counter = builder.public_data_writes[4].counter(); + // Link the 4th write to the 3rd write. + builder.hints[4].prev_index = 3; + + builder.execute(); + } + + #[test(should_fail_with="hinted previous write does not link to current write")] + fn validate_linked_public_data_writes_link_two_writes_to_same_prev_write_fails() { + let mut builder = TestBuilder::new(); + + // Link a write to the previous write of its previous write. + let prev_write_index = builder.hints[4].prev_index; + builder.hints[4].prev_index = 3; + // Clear the counter and link of its previous write. + builder.public_data_writes[prev_write_index].override_counter = 0; + + builder.execute(); + } } diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_leaf_memberships.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_leaf_memberships.nr index 3e9993a5da12..6bc8a1e2c1ed 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_leaf_memberships.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_public_data_leaf_memberships.nr @@ -3,6 +3,8 @@ use dep::types::{ merkle_tree::conditionally_assert_check_membership }; +// Perform membership check for all non-zero leaf slots, ensuring that the values being read are correct and the public data tree is updated with the correct low leaves. +// TODO: Update public data tree in the tail circuit. Otherwise, change this to just check the leaves for public data reads. fn validate_public_data_leaf_memberships( leaves: [OverridablePublicDataTreeLeaf; N], leaf_hints: [PublicDataLeafHint; N], diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_unique_leaf_slots.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_unique_leaf_slots.nr index 896ffde628a9..f9216be6590f 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_unique_leaf_slots.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_validator/validate_unique_leaf_slots.nr @@ -1,32 +1,157 @@ use dep::types::{data::OverridablePublicDataTreeLeaf, utils::arrays::SortedResult}; -// All non-zero leaf slots in leaves must be unique, ensuring that writes with the same leaf slot are grouped into a single group. +// Validate that every non-zero slot in leaves is unique. fn validate_unique_leaf_slots( leaves: [OverridablePublicDataTreeLeaf; N], unique_slot_index_hints: SortedResult ) { let sorted_leaf_slots = unique_slot_index_hints.sorted_array; let sorted_leaf_slot_indexes = unique_slot_index_hints.sorted_index_hints; - + let original_leaf_indexes = unique_slot_index_hints.original_index_hints; let mut prev_slot = 0; - let mut num_non_zeros = 0; for i in 0..N { let leaf = leaves[i].leaf; let sorted_index = if leaf.slot != 0 { sorted_leaf_slot_indexes[i] } else { - num_non_zeros += 1; - N - num_non_zeros + i // This ensures that sorted_leaf_slots is padded with the same amount of zero slots as leaves. }; let hinted_leaf_slot = sorted_leaf_slots[sorted_index]; - assert_eq(hinted_leaf_slot, leaf.slot); + assert_eq(hinted_leaf_slot, leaf.slot, "mismatch hinted slot"); + assert_eq(original_leaf_indexes[sorted_index], i, "sorted value does not link to leaf"); // This ensures that each sorted_leaf_slot can only be refered to once. let curr_leaf_alot = sorted_leaf_slots[i]; if leaf.slot != 0 { - assert(prev_slot.lt(curr_leaf_alot)); - } else { - assert_eq(curr_leaf_alot, 0); + assert(prev_slot.lt(curr_leaf_alot), "hinted slots are not sorted"); // This ensures that all non-zero slots are unique. } prev_slot = curr_leaf_alot; } } + +mod tests { + use crate::components::public_tail_output_validator::validate_unique_leaf_slots::validate_unique_leaf_slots; + use dep::types::{ + data::{OverridablePublicDataTreeLeaf, PublicDataTreeLeaf}, tests::utils::pad_end, + utils::arrays::SortedResult + }; + + global NUM_TEST_LEAVES = 10; + + struct TestBuilder { + leaves: [OverridablePublicDataTreeLeaf; NUM_TEST_LEAVES], + sorted_array: [Field; NUM_TEST_LEAVES], + sorted_index_hints: [u32; NUM_TEST_LEAVES], + original_index_hints: [u32; NUM_TEST_LEAVES], + } + + impl TestBuilder { + pub fn new() -> Self { + let leaves = pad_end([], OverridablePublicDataTreeLeaf::empty()); + let sorted_array = pad_end([], 0); + let sorted_index_hints = pad_end([], 0); + let mut original_index_hints = pad_end([], 0); + for i in 0..original_index_hints.len() { + original_index_hints[i] = i; + } + TestBuilder { leaves, sorted_array, sorted_index_hints, original_index_hints } + } + + pub fn update_leaves(&mut self, slots: [Field; N]) { + let leaves = slots.map( + |slot: Field| OverridablePublicDataTreeLeaf { leaf: PublicDataTreeLeaf { slot, value: 1 }, override_counter: 0 } + ); + self.leaves = pad_end(leaves, OverridablePublicDataTreeLeaf::empty()); + } + + pub fn update_sorted_array(&mut self, sorted_values: [Field; N]) { + self.sorted_array = pad_end(sorted_values, 0); + } + + pub fn update_sorted_index_hints(&mut self, sorted_indexes: [u32; N]) { + self.sorted_index_hints = pad_end(sorted_indexes, 0); + } + + pub fn update_original_index_hints(&mut self, original_indexes: [u32; N]) { + let mut original_index_hints = pad_end(original_indexes, 0); + for i in original_indexes.len()..original_index_hints.len() { + original_index_hints[i] = i; + } + self.original_index_hints = original_index_hints; + } + + pub fn execute(self) { + let hints = SortedResult { + sorted_array: self.sorted_array, + sorted_index_hints: self.sorted_index_hints, + original_index_hints: self.original_index_hints + }; + validate_unique_leaf_slots(self.leaves, hints); + } + } + + #[test] + fn validate_unique_leaf_slots_empty_succeeds() { + let builder = TestBuilder::new(); + builder.execute() + } + + #[test] + fn validate_unique_leaf_slots_succeeds() { + let mut builder = TestBuilder::new(); + + builder.update_leaves([22, 44, 33, 11]); + builder.update_sorted_array([11, 22, 33, 44]); + builder.update_sorted_index_hints([1, 3, 2, 0]); + builder.update_original_index_hints([3, 0, 2, 1]); + + builder.execute() + } + + #[test(should_fail_with="sorted value does not link to leaf")] + fn validate_unique_leaf_slots_duplicates_remove_hint_fails() { + let mut builder = TestBuilder::new(); + + builder.update_leaves([22, 11, 11]); + builder.update_sorted_array([11, 22]); + builder.update_sorted_index_hints([1, 0, 0]); + builder.update_original_index_hints([1, 0]); + + builder.execute() + } + + #[test(should_fail_with="hinted slots are not sorted")] + fn validate_unique_leaf_slots_duplicates_extra_hint_fails() { + let mut builder = TestBuilder::new(); + + builder.update_leaves([22, 11, 11]); + builder.update_sorted_array([11, 11, 22]); + builder.update_sorted_index_hints([2, 0, 1]); + builder.update_original_index_hints([1, 2, 0]); + + builder.execute() + } + + #[test(should_fail_with="hinted slots are not sorted")] + fn validate_unique_leaf_slots_duplicates_extra_hint_in_padded_first_zero_fails() { + let mut builder = TestBuilder::new(); + + builder.update_leaves([22, 11, 11]); + builder.update_sorted_array([0, 11, 22, 0, 11]); // The first zero slot triggers the assert. + builder.update_sorted_index_hints([2, 1, 4]); + builder.update_original_index_hints([3, 1, 0, 4, 2]); + + builder.execute() + } + + #[test(should_fail_with="mismatch hinted slot")] + fn validate_unique_leaf_slots_duplicates_extra_hint_in_padded_first_non_zero_fails() { + let mut builder = TestBuilder::new(); + + builder.update_leaves([22, 11, 11]); + builder.update_sorted_array([1, 11, 22, 0, 11]); // Change the first slot to be non-zero. + builder.update_sorted_index_hints([2, 1, 4]); + builder.update_original_index_hints([3, 1, 0, 3, 2]); + + builder.execute() + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/nullifier_non_existent_read_request_hints_builder.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/nullifier_non_existent_read_request_hints_builder.nr index ea0d8affd553..31b7aa2eb9f4 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/nullifier_non_existent_read_request_hints_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/nullifier_non_existent_read_request_hints_builder.nr @@ -6,7 +6,7 @@ use dep::types::{ NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, NULLIFIER_SUBTREE_HEIGHT }, merkle_tree::MembershipWitness, tests::{merkle_tree_utils::NonEmptyMerkleTree}, - utils::{arrays::{find_index_hint, get_sorted_hints}, field::full_field_greater_than} + utils::{arrays::{find_index_hint, get_sorted_result}} }; struct NullifierNonExistentReadRequestHintsBuilder { @@ -50,7 +50,7 @@ impl NullifierNonExistentReadRequestHintsBuilder { } pub fn to_hints(self) -> NullifierNonExistentReadRequestHints { - let sorted_result = get_sorted_hints( + let sorted_result = get_sorted_result( self.pending_nullifiers, |a: Nullifier, b: Nullifier| (b.value == 0) | ((a.value != 0) & a.value.lt(b.value)) ); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/mod.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/mod.nr index 01e031f90fa7..129deea31948 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/mod.nr @@ -1,4 +1,5 @@ mod fixture_builder; mod fixtures; mod merkle_tree_utils; +mod types; mod utils; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/types.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/types.nr new file mode 100644 index 000000000000..710d711d9a9a --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/types.nr @@ -0,0 +1,80 @@ +use crate::{abis::side_effect::Ordered, traits::Empty}; + +struct TestValue { + value: Field, + counter: u32, +} + +impl Empty for TestValue { + fn empty() -> Self { + TestValue { value: 0, counter: 0 } + } +} + +impl Eq for TestValue { + fn eq(self, other: Self) -> bool { + (self.value == other.value) & (self.counter == other.counter) + } +} + +impl Ordered for TestValue { + fn counter(self) -> u32 { + self.counter + } +} + +struct TestTwoValues { + value_1: Field, + value_2: Field, + counter: u32, +} + +impl Empty for TestTwoValues { + fn empty() -> Self { + TestTwoValues { value_1: 0, value_2: 0, counter: 0 } + } +} + +impl Eq for TestTwoValues { + fn eq(self, other: Self) -> bool { + (self.value_1 == other.value_1) & (self.value_2 == other.value_2) & (self.counter == other.counter) + } +} + +impl Ordered for TestTwoValues { + fn counter(self) -> u32 { + self.counter + } +} + +struct TestCombinedValue { + value: Field, +} + +impl Empty for TestCombinedValue { + fn empty() -> Self { + TestCombinedValue { value: 0 } + } +} + +impl Eq for TestCombinedValue { + fn eq(self, other: Self) -> bool { + (self.value == other.value) + } +} + +pub fn sum_two_values(from: TestTwoValues) -> TestValue { + TestValue { value: from.value_1 + from.value_2, counter: from.counter } +} + +pub fn is_summed_from_two_values(from: TestTwoValues, to: TestValue) -> bool { + ((from.value_1 + from.value_2) == to.value) & (from.counter == to.counter) +} + +pub fn combine_two_values(from: TestTwoValues) -> TestCombinedValue { + TestCombinedValue { value: from.value_1 + from.value_2 } +} + +pub fn is_combined_from_two_values(from: TestTwoValues, to: TestCombinedValue) -> bool { + ((from.value_1 + from.value_2) == to.value) +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr index 205a5957e5f3..95ee011bbe0f 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr @@ -6,7 +6,7 @@ mod assert_sorted_array; mod assert_sorted_transformed_value_array; mod assert_split_sorted_transformed_value_arrays; mod assert_split_transformed_value_arrays; -mod get_sorted_hints; +mod get_sorted_result; mod get_sorted_tuple; mod sort_by; mod sort_by_counter; @@ -29,7 +29,7 @@ use assert_split_sorted_transformed_value_arrays::{ }; use assert_sorted_transformed_value_array::{assert_sorted_transformed_value_array, assert_sorted_transformed_value_array_capped_size}; use assert_split_transformed_value_arrays::assert_split_transformed_value_arrays; -use get_sorted_hints::{get_sorted_hints, SortedResult}; +use get_sorted_result::{get_sorted_result, SortedResult}; use sort_by_counter::{sort_by_counter_asc, sort_by_counter_desc}; use crate::traits::{Empty, is_empty}; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_sorted_transformed_value_array.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_sorted_transformed_value_array.nr index 6eb7869fb593..5218f06bd68e 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_sorted_transformed_value_array.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_sorted_transformed_value_array.nr @@ -9,7 +9,9 @@ use crate::{ }; fn get_num_private_items(array: [T; N]) -> u32 where T: Ordered + Empty + Eq { - let length = count_private_items(array); + let length = unsafe { + count_private_items(array) + }; if length != 0 { let last_private_item = array[length - 1]; assert(!is_empty(last_private_item) & (last_private_item.counter() == 0)); @@ -83,7 +85,8 @@ pub fn assert_combined_sorted_transformed_value_array_asc mod tests { use crate::{ - abis::side_effect::Ordered, tests::utils::pad_end, traits::Empty, + tests::utils::pad_end, + tests::types::{combine_two_values, is_combined_from_two_values, TestCombinedValue, TestTwoValues}, utils::arrays::{ array_merge, assert_combined_sorted_transformed_value_array::{ @@ -94,99 +97,49 @@ mod tests { } }; - struct TestItem { - name: Field, - price: Field, - tax: Field, - counter: u32, - } - - impl Ordered for TestItem { - fn counter(self) -> u32 { - self.counter - } - } - - impl Empty for TestItem { - fn empty() -> Self { - TestItem { name: 0, price: 0, tax: 0, counter: 0 } - } - } - - impl Eq for TestItem { - fn eq(self, other: Self) -> bool { - (self.name == other.name) & (self.price == other.price) & (self.tax == other.tax) & (self.counter == other.counter) - } - } - - struct TestValue { - name: Field, - total: Field, - } - - impl Empty for TestValue { - fn empty() -> Self { - TestValue { name: 0, total: 0 } - } - } - - impl Eq for TestValue { - fn eq(self, other: Self) -> bool { - (self.name == other.name) & (self.total == other.total) - } - } - - fn transform(item: TestItem) -> TestValue { - TestValue { name: item.name, total: item.price + item.tax } - } - - fn is_transformed(item: TestItem, value: TestValue) -> bool { - (item.name == value.name) & ((item.price + item.tax) == value.total) - } - - struct TestDataBuilder { - original_array_lt: [T; N], - original_array_gte: [T; N], - sorted_transformed_value_array: [S; N], + struct TestDataBuilder { + original_array_lt: [TestTwoValues; N], + original_array_gte: [TestTwoValues; N], + sorted_transformed_value_array: [TestCombinedValue; N], hints: [CombinedOrderHint; N], } - impl TestDataBuilder { + impl TestDataBuilder<12> { pub fn new() -> Self { let original_array_lt = pad_end( [ - TestItem { name: 7, price: 40, tax: 7, counter: 0 }, - TestItem { name: 4, price: 70, tax: 6, counter: 0 }, - TestItem { name: 6, price: 80, tax: 1, counter: 22 }, - TestItem { name: 3, price: 30, tax: 4, counter: 11 } + TestTwoValues { value_1: 40, value_2: 7, counter: 0 }, + TestTwoValues { value_1: 70, value_2: 6, counter: 0 }, + TestTwoValues { value_1: 80, value_2: 1, counter: 22 }, + TestTwoValues { value_1: 30, value_2: 4, counter: 11 } ], - TestItem::empty() + TestTwoValues::empty() ); let original_array_gte = pad_end( [ - TestItem { name: 9, price: 20, tax: 2, counter: 0 }, - TestItem { name: 8, price: 90, tax: 3, counter: 0 }, - TestItem { name: 5, price: 50, tax: 9, counter: 55 }, - TestItem { name: 1, price: 60, tax: 8, counter: 33 }, - TestItem { name: 2, price: 10, tax: 5, counter: 44 } + TestTwoValues { value_1: 20, value_2: 2, counter: 0 }, + TestTwoValues { value_1: 90, value_2: 3, counter: 0 }, + TestTwoValues { value_1: 50, value_2: 9, counter: 55 }, + TestTwoValues { value_1: 60, value_2: 8, counter: 33 }, + TestTwoValues { value_1: 10, value_2: 5, counter: 44 } ], - TestItem::empty() + TestTwoValues::empty() ); let sorted_transformed_value_array = pad_end( [ - TestValue { name: 7, total: 47 }, - TestValue { name: 4, total: 76 }, - TestValue { name: 9, total: 22 }, - TestValue { name: 8, total: 93 }, - TestValue { name: 3, total: 34 }, - TestValue { name: 6, total: 81 }, - TestValue { name: 1, total: 68 }, - TestValue { name: 2, total: 15 }, - TestValue { name: 5, total: 59 } + TestCombinedValue { value: 47 }, + TestCombinedValue { value: 76 }, + TestCombinedValue { value: 22 }, + TestCombinedValue { value: 93 }, + TestCombinedValue { value: 34 }, + TestCombinedValue { value: 81 }, + TestCombinedValue { value: 68 }, + TestCombinedValue { value: 15 }, + TestCombinedValue { value: 59 } ], - TestValue::empty() + TestCombinedValue::empty() ); let hints = [ @@ -208,34 +161,34 @@ mod tests { } } - impl TestDataBuilder { + impl TestDataBuilder<8> { pub fn new_without_prepended() -> Self { let original_array_lt = pad_end( [ - TestItem { name: 6, price: 80, tax: 1, counter: 22 }, - TestItem { name: 3, price: 30, tax: 4, counter: 11 } + TestTwoValues { value_1: 80, value_2: 1, counter: 22 }, + TestTwoValues { value_1: 30, value_2: 4, counter: 11 } ], - TestItem::empty() + TestTwoValues::empty() ); let original_array_gte = pad_end( [ - TestItem { name: 5, price: 50, tax: 9, counter: 55 }, - TestItem { name: 1, price: 60, tax: 8, counter: 33 }, - TestItem { name: 2, price: 10, tax: 5, counter: 44 } + TestTwoValues { value_1: 50, value_2: 9, counter: 55 }, + TestTwoValues { value_1: 60, value_2: 8, counter: 33 }, + TestTwoValues { value_1: 10, value_2: 5, counter: 44 } ], - TestItem::empty() + TestTwoValues::empty() ); let sorted_transformed_value_array = pad_end( [ - TestValue { name: 3, total: 34 }, - TestValue { name: 6, total: 81 }, - TestValue { name: 1, total: 68 }, - TestValue { name: 2, total: 15 }, - TestValue { name: 5, total: 59 } + TestCombinedValue { value: 34 }, + TestCombinedValue { value: 81 }, + TestCombinedValue { value: 68 }, + TestCombinedValue { value: 15 }, + TestCombinedValue { value: 59 } ], - TestValue::empty() + TestCombinedValue::empty() ); let hints = [ @@ -253,7 +206,7 @@ mod tests { } } - impl TestDataBuilder { + impl TestDataBuilder { pub fn swap_items(&mut self, from: u32, to: u32) { let tmp = self.sorted_transformed_value_array[from]; self.sorted_transformed_value_array[from] = self.sorted_transformed_value_array[to]; @@ -271,7 +224,7 @@ mod tests { self.original_array_lt, self.original_array_gte, self.sorted_transformed_value_array, - is_transformed, + is_combined_from_two_values, self.hints ); } @@ -284,7 +237,7 @@ mod tests { let hints = get_combined_order_hints_asc(builder.original_array_lt, builder.original_array_gte); assert_eq(hints, builder.hints); - let sorted = sort_by_counter_asc(array_merge(builder.original_array_lt, builder.original_array_gte)).map(transform); + let sorted = sort_by_counter_asc(array_merge(builder.original_array_lt, builder.original_array_gte)).map(combine_two_values); assert_eq(sorted, builder.sorted_transformed_value_array); builder.execute(); @@ -297,7 +250,7 @@ mod tests { let hints = get_combined_order_hints_asc(builder.original_array_lt, builder.original_array_gte); assert_eq(hints, builder.hints); - let sorted = sort_by_counter_asc(array_merge(builder.original_array_lt, builder.original_array_gte)).map(transform); + let sorted = sort_by_counter_asc(array_merge(builder.original_array_lt, builder.original_array_gte)).map(combine_two_values); assert_eq(sorted, builder.sorted_transformed_value_array); builder.execute(); @@ -308,7 +261,7 @@ mod tests { let mut builder = TestDataBuilder::new(); // Tweak the value at index 1. - builder.sorted_transformed_value_array[1].total += 1; + builder.sorted_transformed_value_array[1].value += 1; builder.execute(); } @@ -382,7 +335,7 @@ mod tests { let mut builder = TestDataBuilder::new(); // Clear an item. - builder.sorted_transformed_value_array[8] = TestValue::empty(); + builder.sorted_transformed_value_array[8] = TestCombinedValue::empty(); builder.hints[8] = CombinedOrderHint { counter: 0, original_index: 6 }; builder.execute(); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_sorted_transformed_value_array/get_combined_order_hints.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_sorted_transformed_value_array/get_combined_order_hints.nr index 94ec60bbbf6b..c0a7511edbf3 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_sorted_transformed_value_array/get_combined_order_hints.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_sorted_transformed_value_array/get_combined_order_hints.nr @@ -20,7 +20,7 @@ impl Eq for CombinedOrderHint { } } -unconstrained fn count_private_items(array: [T; N]) -> u32 where T: Ordered + Empty + Eq { +unconstrained pub fn count_private_items(array: [T; N]) -> u32 where T: Ordered + Empty + Eq { let mut length = 0; for item in array { if !is_empty(item) & (item.counter() == 0) { @@ -60,50 +60,27 @@ pub fn get_combined_order_hints_asc( mod tests { use crate::{ - abis::side_effect::Ordered, tests::utils::pad_end, traits::Empty, + tests::{types::TestValue, utils::pad_end}, utils::arrays::assert_combined_sorted_transformed_value_array::get_combined_order_hints::{CombinedOrderHint, get_combined_order_hints_asc} }; - struct TestItem { - value: Field, - counter: u32, - } - - impl Ordered for TestItem { - fn counter(self) -> u32 { - self.counter - } - } - - impl Eq for TestItem { - fn eq(self, other: Self) -> bool { - (self.value == other.value) & (self.counter == other.counter) - } - } - - impl Empty for TestItem { - fn empty() -> Self { - TestItem { value: 0, counter: 0 } - } - } - #[test] fn get_combined_order_hints_asc_full_non_empty() { let array_lt = pad_end( [ - TestItem { value: 600, counter: 9 }, - TestItem { value: 400, counter: 3 }, - TestItem { value: 500, counter: 6 } + TestValue { value: 600, counter: 9 }, + TestValue { value: 400, counter: 3 }, + TestValue { value: 500, counter: 6 } ], - TestItem::empty() + TestValue::empty() ); let array_gte = pad_end( [ - TestItem { value: 200, counter: 13 }, - TestItem { value: 100, counter: 19 }, - TestItem { value: 300, counter: 16 } + TestValue { value: 200, counter: 13 }, + TestValue { value: 100, counter: 19 }, + TestValue { value: 300, counter: 16 } ], - TestItem::empty() + TestValue::empty() ); let expected_hints = [ CombinedOrderHint { counter: 3, original_index: 1 }, @@ -120,12 +97,12 @@ mod tests { fn get_combined_order_hints_asc_padded_empty() { let array_lt = pad_end( [ - TestItem { value: 500, counter: 6 }, - TestItem { value: 400, counter: 3 } + TestValue { value: 500, counter: 6 }, + TestValue { value: 400, counter: 3 } ], - TestItem::empty() + TestValue::empty() ); - let array_gte = pad_end([TestItem { value: 100, counter: 19 }], TestItem::empty()); + let array_gte = pad_end([TestValue { value: 100, counter: 19 }], TestValue::empty()); let expected_hints = [ CombinedOrderHint { counter: 3, original_index: 1 }, CombinedOrderHint { counter: 6, original_index: 0 }, @@ -138,13 +115,13 @@ mod tests { #[test] fn get_combined_order_hints_asc_lt_empty() { - let array_lt = [TestItem::empty(); 4]; + let array_lt = [TestValue::empty(); 4]; let array_gte = pad_end( [ - TestItem { value: 200, counter: 13 }, - TestItem { value: 100, counter: 19 } + TestValue { value: 200, counter: 13 }, + TestValue { value: 100, counter: 19 } ], - TestItem::empty() + TestValue::empty() ); let expected_hints = [ CombinedOrderHint { counter: 13, original_index: 0 }, @@ -159,12 +136,12 @@ mod tests { fn get_combined_order_hints_asc_gte_empty() { let array_lt = pad_end( [ - TestItem { value: 400, counter: 3 }, - TestItem { value: 500, counter: 6 } + TestValue { value: 400, counter: 3 }, + TestValue { value: 500, counter: 6 } ], - TestItem::empty() + TestValue::empty() ); - let array_gte = [TestItem::empty(); 4]; + let array_gte = [TestValue::empty(); 4]; let expected_hints = [ CombinedOrderHint { counter: 3, original_index: 0 }, CombinedOrderHint { counter: 6, original_index: 1 }, @@ -176,8 +153,8 @@ mod tests { #[test] fn get_combined_order_hints_asc_all_empty() { - let array_lt = [TestItem::empty(); 3]; - let array_gte = [TestItem::empty(); 3]; + let array_lt = [TestValue::empty(); 3]; + let array_gte = [TestValue::empty(); 3]; let expected_hints = [ CombinedOrderHint { counter: 0, original_index: 0 }, CombinedOrderHint { counter: 0, original_index: 1 }, @@ -190,20 +167,20 @@ mod tests { fn get_combined_order_hints_asc_prepended_zero_counters() { let array_lt = pad_end( [ - TestItem { value: 700, counter: 0 }, - TestItem { value: 500, counter: 0 }, - TestItem { value: 100, counter: 16 }, - TestItem { value: 400, counter: 13 } + TestValue { value: 700, counter: 0 }, + TestValue { value: 500, counter: 0 }, + TestValue { value: 100, counter: 16 }, + TestValue { value: 400, counter: 13 } ], - TestItem::empty() + TestValue::empty() ); let array_gte = pad_end( [ - TestItem { value: 300, counter: 0 }, - TestItem { value: 600, counter: 0 }, - TestItem { value: 200, counter: 19 } + TestValue { value: 300, counter: 0 }, + TestValue { value: 600, counter: 0 }, + TestValue { value: 200, counter: 19 } ], - TestItem::empty() + TestValue::empty() ); let expected_hints = [ CombinedOrderHint { counter: 0, original_index: 0 }, @@ -222,15 +199,15 @@ mod tests { #[test] fn get_combined_order_hints_asc_prepended_zero_counters_lt_empty() { - let array_lt = [TestItem::empty(); 6]; + let array_lt = [TestValue::empty(); 6]; let array_gte = pad_end( [ - TestItem { value: 300, counter: 0 }, - TestItem { value: 600, counter: 0 }, - TestItem { value: 200, counter: 19 }, - TestItem { value: 400, counter: 13 } + TestValue { value: 300, counter: 0 }, + TestValue { value: 600, counter: 0 }, + TestValue { value: 200, counter: 19 }, + TestValue { value: 400, counter: 13 } ], - TestItem::empty() + TestValue::empty() ); let expected_hints = [ CombinedOrderHint { counter: 0, original_index: 0 }, @@ -247,14 +224,14 @@ mod tests { fn get_combined_order_hints_asc_prepended_zero_counters_gte_empty() { let array_lt = pad_end( [ - TestItem { value: 300, counter: 0 }, - TestItem { value: 600, counter: 0 }, - TestItem { value: 200, counter: 19 }, - TestItem { value: 400, counter: 13 } + TestValue { value: 300, counter: 0 }, + TestValue { value: 600, counter: 0 }, + TestValue { value: 200, counter: 19 }, + TestValue { value: 400, counter: 13 } ], - TestItem::empty() + TestValue::empty() ); - let array_gte = [TestItem::empty(); 6]; + let array_gte = [TestValue::empty(); 6]; let expected_hints = [ CombinedOrderHint { counter: 0, original_index: 0 }, CombinedOrderHint { counter: 0, original_index: 1 }, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_transformed_array.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_transformed_array.nr index ab1238524ea9..380bbaddff07 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_transformed_array.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_transformed_array.nr @@ -1,6 +1,7 @@ use crate::{traits::Empty, utils::arrays::array_length}; // original_array(_lt/_gte) must be valid, i.e. validate_array(original_array) == true +// This ensures that combined_array is valid when S can only be empty if and only if T is empty. pub fn assert_combined_transformed_array( original_array_lt: [T; N], original_array_gte: [T; N], @@ -23,3 +24,110 @@ pub fn assert_combined_transformed_array( assert(is_transformed(from, to), "hinted item in the commbined array does not match"); } } + +mod tests { + use crate::{ + tests::{types::{is_summed_from_two_values, TestTwoValues, TestValue}, utils::pad_end}, + utils::arrays::assert_combined_transformed_array::assert_combined_transformed_array + }; + + struct TestBuilder { + original_array_lt: [TestTwoValues; N], + original_array_gte: [TestTwoValues; N], + combined_array: [TestValue; N], + } + + impl TestBuilder<10> { + pub fn new_empty() -> Self { + let original_array_lt = pad_end([], TestTwoValues::empty()); + let original_array_gte = pad_end([], TestTwoValues::empty()); + let combined_array = pad_end([], TestValue::empty()); + TestBuilder { original_array_lt, original_array_gte, combined_array } + } + + pub fn new() -> Self { + let original_array_lt = pad_end( + [ + TestTwoValues { value_1: 10, value_2: 1, counter: 2 }, + TestTwoValues { value_1: 20, value_2: 2, counter: 5 }, + TestTwoValues { value_1: 30, value_2: 3, counter: 3 } + ], + TestTwoValues::empty() + ); + + let original_array_gte = pad_end( + [ + TestTwoValues { value_1: 40, value_2: 4, counter: 1 }, + TestTwoValues { value_1: 50, value_2: 5, counter: 4 } + ], + TestTwoValues::empty() + ); + + let combined_array = pad_end( + [ + TestValue { value: 11, counter: 2 }, + TestValue { value: 22, counter: 5 }, + TestValue { value: 33, counter: 3 }, + TestValue { value: 44, counter: 1 }, + TestValue { value: 55, counter: 4 } + ], + TestValue::empty() + ); + + TestBuilder { original_array_lt, original_array_gte, combined_array } + } + + pub fn execute(self) { + assert_combined_transformed_array( + self.original_array_lt, + self.original_array_gte, + self.combined_array, + is_summed_from_two_values + ); + } + } + + #[test] + fn assert_combined_transformed_array_empty_succeeds() { + let builder = TestBuilder::new_empty(); + builder.execute(); + } + + #[test] + fn assert_combined_transformed_array_succeeds() { + let builder = TestBuilder::new(); + builder.execute(); + } + + #[test(should_fail_with="hinted item in the commbined array does not match")] + fn assert_combined_transformed_array_extra_item_fails() { + let mut builder = TestBuilder::new(); + + // Add random value to an empty item. + builder.combined_array[7].value = 123; + + builder.execute(); + } + + #[test(should_fail_with="hinted item in the commbined array does not match")] + fn assert_combined_transformed_array_missing_item_fails() { + let mut builder = TestBuilder::new(); + + // Clear the last item. + builder.combined_array[4] = TestValue::empty(); + + builder.execute(); + } + + #[test(should_fail_with="hinted item in the commbined array does not match")] + fn assert_combined_transformed_array_unordered_fails() { + let mut builder = TestBuilder::new(); + + // Swap the two items. + let tmp = builder.combined_array[3]; + builder.combined_array[3] = builder.combined_array[1]; + builder.combined_array[1] = tmp; + + builder.execute(); + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_deduped_array.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_deduped_array.nr index 1debebe520d5..438804e8d9dc 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_deduped_array.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_deduped_array.nr @@ -1,19 +1,23 @@ use crate::{abis::side_effect::{Inner, Overridable}, traits::{Empty, is_empty}}; +// Check that deduped_array contains values that are not being overriden, i.e. override_counter == 0. pub fn assert_deduped_array( original_array: [S; N], deduped_array: [T; N] ) where S: Overridable + Inner, T: Eq + Empty { - let mut num_deduped = 0; + let mut num_added = 0; for i in 0..original_array.len() { let original = original_array[i]; if original.override_counter() == 0 { - assert_eq(original.inner(), deduped_array[num_deduped], "mismatch deduped item"); - num_deduped += 1; - } else { - assert( - is_empty(deduped_array[N - num_deduped - 1]), "empty items must be padded to the deduped array" - ); + assert_eq(original.inner(), deduped_array[num_added], "mismatch deduped item"); + num_added += 1; + } + } + let mut should_be_empty = false; + for i in 0..original_array.len() { + should_be_empty |= i == num_added; + if should_be_empty { + assert(is_empty(deduped_array[i]), "empty items must be padded to the deduped array"); } } } @@ -28,3 +32,122 @@ pub fn dedupe_array(original_array: [S; N]) -> [T; N] where S: } deduped.storage } + +mod tests { + use crate::{ + abis::side_effect::{Inner, Overridable}, tests::{types::TestValue, utils::pad_end}, + traits::Empty, utils::arrays::assert_deduped_array::{assert_deduped_array, dedupe_array} + }; + + struct TestItem { + value: TestValue, + override_by: TestValue, + } + + impl Overridable for TestItem { + fn override_counter(self) -> u32 { + self.override_by.counter + } + } + + impl Inner for TestItem { + fn inner(self) -> TestValue { + self.value + } + } + + impl Empty for TestItem { + fn empty() -> Self { + TestItem { value: TestValue::empty(), override_by: TestValue::empty() } + } + } + + impl Eq for TestItem { + fn eq(self, other: Self) -> bool { + (self.value == other.value) & (self.override_by == other.override_by) + } + } + + global NUM_TEST_ITEMS = 10; + + struct TestBuilder { + original_array: [TestItem; NUM_TEST_ITEMS], + deduped_array: [TestValue; NUM_TEST_ITEMS], + } + + impl TestBuilder { + pub fn new_empty() -> Self { + let original_array = pad_end([], TestItem::empty()); + let deduped_array = pad_end([], TestValue::empty()); + TestBuilder { original_array, deduped_array } + } + + pub fn new() -> Self { + let values = [ + TestValue { value: 22, counter: 2 }, + TestValue { value: 11, counter: 1 }, + TestValue { value: 44, counter: 4 }, + TestValue { value: 11, counter: 6 }, + TestValue { value: 11, counter: 3 }, + TestValue { value: 33, counter: 5 } + ]; + let mut original_array = pad_end( + values.map(|value: TestValue| TestItem { value, override_by: TestValue::empty() }), + TestItem::empty() + ); + original_array[1].override_by = values[4]; + original_array[4].override_by = values[3]; + + let deduped_array = pad_end( + [ + values[0], + values[2], + values[3], + values[5] + ], + TestValue::empty() + ); + + TestBuilder { original_array, deduped_array } + } + + pub fn execute(self) { + assert_deduped_array(self.original_array, self.deduped_array); + } + + pub fn succeeds(self) { + assert_eq(self.deduped_array, dedupe_array(self.original_array)); + self.execute(); + } + } + + #[test] + fn assert_deduped_array_empty_succeeds() { + let builder = TestBuilder::new_empty(); + builder.succeeds(); + } + + #[test] + fn assert_deduped_array_succeeds() { + let builder = TestBuilder::new(); + builder.succeeds(); + } + + #[test(should_fail_with="mismatch deduped item")] + fn assert_deduped_array_extra_item_fails() { + let mut builder = TestBuilder::new(); + + builder.deduped_array[7] = builder.original_array[1].inner(); + + builder.execute(); + } + + #[test(should_fail_with="empty items must be padded to the deduped array")] + fn assert_deduped_array_extra_item_at_end_fails() { + let mut builder = TestBuilder::new(); + + builder.deduped_array[NUM_TEST_ITEMS - 1] = builder.original_array[1].inner(); + + builder.execute(); + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_exposed_sorted_transformed_value_array.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_exposed_sorted_transformed_value_array.nr index dd0260be56a2..4a4929c6bf4a 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_exposed_sorted_transformed_value_array.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_exposed_sorted_transformed_value_array.nr @@ -38,80 +38,34 @@ pub fn assert_exposed_sorted_transformed_value_array( mod tests { use crate::{ - abis::side_effect::Ordered, traits::Empty, + tests::types::{is_combined_from_two_values, TestCombinedValue, TestTwoValues}, utils::arrays::{assert_exposed_sorted_transformed_value_array::{assert_exposed_sorted_transformed_value_array, get_order_hints::OrderHint}} }; - struct TestItem { - name: Field, - price: Field, - tax: Field, - counter: u32, - } - - impl Ordered for TestItem { - fn counter(self) -> u32 { - self.counter - } - } - - impl Empty for TestItem { - fn empty() -> Self { - TestItem { name: 0, price: 0, tax: 0, counter: 0 } - } - } - - impl Eq for TestItem { - fn eq(self, other: Self) -> bool { - (self.name == other.name) & (self.price == other.price) & (self.tax == other.tax) & (self.counter == other.counter) - } - } - - struct TestValue { - name: Field, - total: Field, - } - - impl Empty for TestValue { - fn empty() -> Self { - TestValue { name: 0, total: 0 } - } - } - - impl Eq for TestValue { - fn eq(self, other: Self) -> bool { - (self.name == other.name) & (self.total == other.total) - } - } - - fn is_transformed(item: TestItem, value: TestValue) -> bool { - (item.name == value.name) & ((item.price + item.tax) == value.total) - } - - struct TestDataBuilder { - original_array: [T; N], - exposed_sorted_transformed_value_array: [S; N], + struct TestDataBuilder { + original_array: [TestTwoValues; N], + exposed_sorted_transformed_value_array: [TestCombinedValue; N], hints: [OrderHint; N], } - impl TestDataBuilder { + impl TestDataBuilder<6> { pub fn new() -> Self { let original_array = [ - TestItem { name: 100, price: 10, tax: 5, counter: 44 }, - TestItem { name: 200, price: 20, tax: 6, counter: 22 }, - TestItem { name: 300, price: 30, tax: 7, counter: 11 }, - TestItem { name: 400, price: 40, tax: 8, counter: 33 }, - TestItem::empty(), - TestItem::empty() + TestTwoValues { value_1: 10, value_2: 5, counter: 44 }, + TestTwoValues { value_1: 20, value_2: 6, counter: 22 }, + TestTwoValues { value_1: 30, value_2: 7, counter: 11 }, + TestTwoValues { value_1: 40, value_2: 8, counter: 33 }, + TestTwoValues::empty(), + TestTwoValues::empty() ]; let exposed_sorted_transformed_value_array = [ - TestValue { name: 300, total: 37 }, - TestValue { name: 200, total: 26 }, - TestValue { name: 400, total: 48 }, - TestValue { name: 100, total: 15 }, - TestValue::empty(), - TestValue::empty() + TestCombinedValue { value: 37 }, + TestCombinedValue { value: 26 }, + TestCombinedValue { value: 48 }, + TestCombinedValue { value: 15 }, + TestCombinedValue::empty(), + TestCombinedValue::empty() ]; let hints = [ @@ -130,7 +84,7 @@ mod tests { assert_exposed_sorted_transformed_value_array( self.original_array, self.exposed_sorted_transformed_value_array, - is_transformed, + is_combined_from_two_values, self.hints ); } @@ -147,7 +101,7 @@ mod tests { let mut builder = TestDataBuilder::new(); // Tweak the value at index 1. - builder.exposed_sorted_transformed_value_array[1].total += 1; + builder.exposed_sorted_transformed_value_array[1].value += 1; builder.execute(); } @@ -202,7 +156,7 @@ mod tests { let mut builder = TestDataBuilder::new(); // Add a random item. - builder.exposed_sorted_transformed_value_array[4] = TestValue { name: 500, total: 10 }; + builder.exposed_sorted_transformed_value_array[4] = TestCombinedValue { value: 10 }; builder.execute(); } @@ -212,7 +166,7 @@ mod tests { let mut builder = TestDataBuilder::new(); // Add a random item. - builder.exposed_sorted_transformed_value_array[4] = TestValue { name: 500, total: 10 }; + builder.exposed_sorted_transformed_value_array[4] = TestCombinedValue { value: 10 }; // Change the hint to point to an empty item. builder.hints[4].sorted_index = 5; @@ -224,7 +178,7 @@ mod tests { let mut builder = TestDataBuilder::new(); // Remove an item. - builder.exposed_sorted_transformed_value_array[3] = TestValue::empty(); + builder.exposed_sorted_transformed_value_array[3] = TestCombinedValue::empty(); builder.execute(); } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_exposed_sorted_transformed_value_array/get_order_hints.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_exposed_sorted_transformed_value_array/get_order_hints.nr index 6bfdca7a162b..87cc4f1a2fc4 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_exposed_sorted_transformed_value_array/get_order_hints.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_exposed_sorted_transformed_value_array/get_order_hints.nr @@ -1,5 +1,5 @@ use crate::{ - abis::side_effect::Ordered, traits::{Empty, is_empty}, + abis::side_effect::Ordered, traits::Empty, utils::arrays::{ sort_by_counter::{compare_by_counter_empty_padded_asc, compare_by_counter_empty_padded_desc}, get_sorted_tuple::get_sorted_tuple @@ -26,19 +26,15 @@ impl Eq for OrderHint { pub fn get_order_hints( array: [T; N], ordering: fn(T, T) -> bool -) -> [OrderHint; N] where T: Ordered + Eq + Empty { +) -> [OrderHint; N] where T: Ordered { let sorted_tuples = get_sorted_tuple(array, ordering); let mut hints = [OrderHint::empty(); N]; for i in 0..N { let elem = sorted_tuples[i].elem; hints[i].counter = elem.counter(); - if !is_empty(elem) { - let original_index = sorted_tuples[i].original_index; - hints[original_index].sorted_index = i; - } else { - hints[i].sorted_index = i; - } + let original_index = sorted_tuples[i].original_index; + hints[original_index].sorted_index = i; } hints @@ -54,39 +50,16 @@ pub fn get_order_hints_desc(array: [T; N]) -> [OrderHint; N] wher mod tests { use crate::{ - abis::side_effect::Ordered, traits::Empty, + tests::types::TestValue, utils::arrays::assert_exposed_sorted_transformed_value_array::get_order_hints::{get_order_hints_asc, get_order_hints_desc, OrderHint} }; - struct TestItem { - value: Field, - counter: u32, - } - - impl Ordered for TestItem { - fn counter(self) -> u32 { - self.counter - } - } - - impl Eq for TestItem { - fn eq(self, other: Self) -> bool { - (self.value == other.value) & (self.counter == other.counter) - } - } - - impl Empty for TestItem { - fn empty() -> Self { - TestItem { value: 0, counter: 0 } - } - } - #[test] fn get_order_hints_asc_full_non_empty() { let array = [ - TestItem { value: 100, counter: 9 }, - TestItem { value: 200, counter: 3 }, - TestItem { value: 300, counter: 6 } + TestValue { value: 100, counter: 9 }, + TestValue { value: 200, counter: 3 }, + TestValue { value: 300, counter: 6 } ]; let hints = get_order_hints_asc(array); let expected_hints = [ @@ -100,11 +73,11 @@ mod tests { #[test] fn get_order_hints_asc_padded_empty() { let array = [ - TestItem { value: 100, counter: 9 }, - TestItem { value: 200, counter: 3 }, - TestItem { value: 300, counter: 6 }, - TestItem::empty(), - TestItem::empty() + TestValue { value: 100, counter: 9 }, + TestValue { value: 200, counter: 3 }, + TestValue { value: 300, counter: 6 }, + TestValue::empty(), + TestValue::empty() ]; let hints = get_order_hints_asc(array); let expected_hints = [ @@ -120,9 +93,9 @@ mod tests { #[test] fn get_order_hints_desc_full_non_empty() { let array = [ - TestItem { value: 100, counter: 9 }, - TestItem { value: 200, counter: 3 }, - TestItem { value: 300, counter: 6 } + TestValue { value: 100, counter: 9 }, + TestValue { value: 200, counter: 3 }, + TestValue { value: 300, counter: 6 } ]; let hints = get_order_hints_desc(array); let expected_hints = [ @@ -136,11 +109,11 @@ mod tests { #[test] fn get_order_hints_desc_padded_empty() { let array = [ - TestItem { value: 100, counter: 9 }, - TestItem { value: 200, counter: 3 }, - TestItem { value: 300, counter: 6 }, - TestItem::empty(), - TestItem::empty() + TestValue { value: 100, counter: 9 }, + TestValue { value: 200, counter: 3 }, + TestValue { value: 300, counter: 6 }, + TestValue::empty(), + TestValue::empty() ]; let hints = get_order_hints_desc(array); let expected_hints = [ diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_sorted_transformed_value_array.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_sorted_transformed_value_array.nr index c85f82b3ed6e..a8be9895087b 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_sorted_transformed_value_array.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_sorted_transformed_value_array.nr @@ -53,81 +53,32 @@ pub fn assert_sorted_transformed_value_array_capped_size( mod tests { use crate::{ - abis::side_effect::Ordered, traits::Empty, + tests::types::{is_summed_from_two_values, TestTwoValues, TestValue}, utils::arrays::{assert_sorted_transformed_value_array::{assert_sorted_transformed_value_array, assert_sorted_transformed_value_array_capped_size}} }; - struct TestItem { - name: Field, - price: Field, - tax: Field, - counter: u32, - } - - impl Empty for TestItem { - fn empty() -> Self { - TestItem { name: 0, price: 0, tax: 0, counter: 0 } - } - } - - impl Eq for TestItem { - fn eq(self, other: Self) -> bool { - (self.name == other.name) & (self.price == other.price) & (self.tax == other.tax) & (self.counter == other.counter) - } - } - - struct TestValue { - name: Field, - total: Field, - counter: u32, - } - - impl Ordered for TestValue { - fn counter(self) -> u32 { - self.counter - } - } - - impl Empty for TestValue { - fn empty() -> Self { - TestValue { name: 0, total: 0, counter: 0 } - } - } - - impl Eq for TestValue { - fn eq(self, other: Self) -> bool { - (self.name == other.name) & (self.total == other.total) & (self.counter == other.counter) - } - } - - fn is_transformed(item: TestItem, value: TestValue) -> bool { - (item.name == value.name) - & ((item.price + item.tax) == value.total) - & (item.counter == value.counter) - } - - struct TestDataBuilder { - original_array: [T; N], - sorted_transformed_value_array: [S; N], + struct TestDataBuilder { + original_array: [TestTwoValues; N], + sorted_transformed_value_array: [TestValue; N], sorted_indexes: [u32; N], } - impl TestDataBuilder { + impl TestDataBuilder<6> { pub fn new() -> Self { let original_array = [ - TestItem { name: 100, price: 10, tax: 5, counter: 44 }, - TestItem { name: 200, price: 20, tax: 6, counter: 22 }, - TestItem { name: 300, price: 30, tax: 7, counter: 11 }, - TestItem { name: 400, price: 40, tax: 8, counter: 33 }, - TestItem::empty(), - TestItem::empty() + TestTwoValues { value_1: 10, value_2: 5, counter: 44 }, + TestTwoValues { value_1: 20, value_2: 6, counter: 22 }, + TestTwoValues { value_1: 30, value_2: 7, counter: 11 }, + TestTwoValues { value_1: 40, value_2: 8, counter: 33 }, + TestTwoValues::empty(), + TestTwoValues::empty() ]; let sorted_transformed_value_array = [ - TestValue { name: 300, total: 37, counter: 11 }, - TestValue { name: 200, total: 26, counter: 22 }, - TestValue { name: 400, total: 48, counter: 33 }, - TestValue { name: 100, total: 15, counter: 44 }, + TestValue { value: 37, counter: 11 }, + TestValue { value: 26, counter: 22 }, + TestValue { value: 48, counter: 33 }, + TestValue { value: 15, counter: 44 }, TestValue::empty(), TestValue::empty() ]; @@ -141,7 +92,7 @@ mod tests { assert_sorted_transformed_value_array( self.original_array, self.sorted_transformed_value_array, - is_transformed, + is_summed_from_two_values, self.sorted_indexes ); } @@ -150,7 +101,7 @@ mod tests { assert_sorted_transformed_value_array_capped_size( self.original_array, self.sorted_transformed_value_array, - is_transformed, + is_summed_from_two_values, self.sorted_indexes, capped_size ); @@ -168,7 +119,7 @@ mod tests { let mut builder = TestDataBuilder::new(); // Tweak the value at index 1. - builder.sorted_transformed_value_array[1].total += 1; + builder.sorted_transformed_value_array[1].value += 1; builder.execute(); } @@ -198,7 +149,7 @@ mod tests { let mut builder = TestDataBuilder::new(); // Add a random item. - builder.sorted_transformed_value_array[4] = TestValue { name: 500, total: 10, counter: 55 }; + builder.sorted_transformed_value_array[4] = TestValue { value: 10, counter: 55 }; builder.execute(); } @@ -208,7 +159,7 @@ mod tests { let mut builder = TestDataBuilder::new(); // Add a random item. - builder.sorted_transformed_value_array[4] = TestValue { name: 500, total: 10, counter: 55 }; + builder.sorted_transformed_value_array[4] = TestValue { value: 10, counter: 55 }; // Change the hint to point to an empty item. builder.sorted_indexes[4] = 5; @@ -242,7 +193,7 @@ mod tests { let mut builder = TestDataBuilder::new(); // Add a random item outside of capped_size. - builder.sorted_transformed_value_array[4] = TestValue { name: 500, total: 10, counter: 55 }; + builder.sorted_transformed_value_array[4] = TestValue { value: 10, counter: 55 }; builder.execute_capped(4); } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_sorted_transformed_value_arrays.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_sorted_transformed_value_arrays.nr index 5efe0c698d5c..1e87d8f539e7 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_sorted_transformed_value_arrays.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_sorted_transformed_value_arrays.nr @@ -1,7 +1,7 @@ mod get_split_order_hints; use crate::{ - abis::side_effect::Ordered, traits::{Empty, is_empty}, + abis::side_effect::Ordered, traits::Empty, utils::arrays::{ array_length, assert_split_sorted_transformed_value_arrays::get_split_order_hints::SplitOrderHints, validate_array @@ -115,7 +115,7 @@ pub fn assert_split_sorted_transformed_value_arrays_desc( mod tests { use crate::{ - abis::side_effect::Ordered, traits::Empty, + tests::types::{combine_two_values, TestCombinedValue, TestTwoValues}, utils::arrays::{ assert_split_sorted_transformed_value_arrays::{ assert_split_sorted_transformed_value_arrays_asc, @@ -124,80 +124,34 @@ mod tests { } }; - struct TestItem { - name: Field, - price: Field, - tax: Field, - counter: u32, - } - - impl Ordered for TestItem { - fn counter(self) -> u32 { - self.counter - } - } - - impl Empty for TestItem { - fn empty() -> Self { - TestItem { name: 0, price: 0, tax: 0, counter: 0 } - } - } - - impl Eq for TestItem { - fn eq(self, other: Self) -> bool { - (self.name == other.name) & (self.price == other.price) & (self.tax == other.tax) & (self.counter == other.counter) - } - } - - struct TestValue { - name: Field, - total: Field, - } - - impl Empty for TestValue { - fn empty() -> Self { - TestValue { name: 0, total: 0 } - } - } - - impl Eq for TestValue { - fn eq(self, other: Self) -> bool { - (self.name == other.name) & (self.total == other.total) - } - } - - fn transform_value(item: TestItem) -> TestValue { - TestValue { name: item.name, total: item.price + item.tax } - } - global original_array = [ - TestItem { name: 0, price: 1, tax: 0, counter: 33 }, - TestItem { name: 1, price: 10, tax: 6, counter: 44 }, - TestItem { name: 2, price: 20, tax: 7, counter: 11 }, - TestItem { name: 3, price: 30, tax: 8, counter: 0 }, - TestItem { name: 4, price: 40, tax: 9, counter: 22 }, - TestItem::empty(), - TestItem::empty(), - TestItem::empty() + TestTwoValues { value_1: 1, value_2: 0, counter: 33 }, + TestTwoValues { value_1: 10, value_2: 6, counter: 44 }, + TestTwoValues { value_1: 20, value_2: 7, counter: 11 }, + TestTwoValues { value_1: 30, value_2: 8, counter: 0 }, + TestTwoValues { value_1: 40, value_2: 9, counter: 22 }, + TestTwoValues::empty(), + TestTwoValues::empty(), + TestTwoValues::empty() ]; - struct TestDataBuilder { - original_array: [T; N], - transformed_value_array: [S; N], - sorted_transformed_value_array_lt: [S; N], - sorted_transformed_value_array_gte: [S; N], + struct TestDataBuilder { + original_array: [TestTwoValues; N], + transformed_value_array: [TestCombinedValue; N], + sorted_transformed_value_array_lt: [TestCombinedValue; N], + sorted_transformed_value_array_gte: [TestCombinedValue; N], split_counter: u32, hints: SplitOrderHints, ascending: bool, } - impl TestDataBuilder { + impl TestDataBuilder<8> { pub fn empty() -> Self { TestDataBuilder { - original_array: [TestItem::empty(); 8], - transformed_value_array: [TestValue::empty(); 8], - sorted_transformed_value_array_lt: [TestValue::empty(); 8], - sorted_transformed_value_array_gte: [TestValue::empty(); 8], + original_array: [TestTwoValues::empty(); 8], + transformed_value_array: [TestCombinedValue::empty(); 8], + sorted_transformed_value_array_lt: [TestCombinedValue::empty(); 8], + sorted_transformed_value_array_gte: [TestCombinedValue::empty(); 8], split_counter: 0, hints: SplitOrderHints::empty(), ascending: false @@ -205,28 +159,28 @@ mod tests { } pub fn new() -> Self { - let transformed_value_array = original_array.map(|item: TestItem| transform_value(item)); + let transformed_value_array = original_array.map(|item: TestTwoValues| combine_two_values(item)); let split_counter = 15; let sorted_transformed_value_array_lt = [ - TestValue { name: 3, total: 38 }, - TestValue { name: 2, total: 27 }, - TestValue::empty(), - TestValue::empty(), - TestValue::empty(), - TestValue::empty(), - TestValue::empty(), - TestValue::empty() + TestCombinedValue { value: 38 }, + TestCombinedValue { value: 27 }, + TestCombinedValue::empty(), + TestCombinedValue::empty(), + TestCombinedValue::empty(), + TestCombinedValue::empty(), + TestCombinedValue::empty(), + TestCombinedValue::empty() ]; let sorted_transformed_value_array_gte = [ - TestValue { name: 4, total: 49 }, - TestValue { name: 0, total: 1 }, - TestValue { name: 1, total: 16 }, - TestValue::empty(), - TestValue::empty(), - TestValue::empty(), - TestValue::empty(), - TestValue::empty() + TestCombinedValue { value: 49 }, + TestCombinedValue { value: 1 }, + TestCombinedValue { value: 16 }, + TestCombinedValue::empty(), + TestCombinedValue::empty(), + TestCombinedValue::empty(), + TestCombinedValue::empty(), + TestCombinedValue::empty() ]; let hints = SplitOrderHints { sorted_counters_lt: [0, 11, 0, 0, 0, 0, 0, 0], @@ -246,28 +200,28 @@ mod tests { } pub fn new_desc() -> Self { - let transformed_value_array = original_array.map(|item: TestItem| transform_value(item)); + let transformed_value_array = original_array.map(|item: TestTwoValues| combine_two_values(item)); let split_counter = 15; let sorted_transformed_value_array_lt = [ - TestValue { name: 2, total: 27 }, - TestValue { name: 3, total: 38 }, - TestValue::empty(), - TestValue::empty(), - TestValue::empty(), - TestValue::empty(), - TestValue::empty(), - TestValue::empty() + TestCombinedValue { value: 27 }, + TestCombinedValue { value: 38 }, + TestCombinedValue::empty(), + TestCombinedValue::empty(), + TestCombinedValue::empty(), + TestCombinedValue::empty(), + TestCombinedValue::empty(), + TestCombinedValue::empty() ]; let sorted_transformed_value_array_gte = [ - TestValue { name: 1, total: 16 }, - TestValue { name: 0, total: 1 }, - TestValue { name: 4, total: 49 }, - TestValue::empty(), - TestValue::empty(), - TestValue::empty(), - TestValue::empty(), - TestValue::empty() + TestCombinedValue { value: 16 }, + TestCombinedValue { value: 1 }, + TestCombinedValue { value: 49 }, + TestCombinedValue::empty(), + TestCombinedValue::empty(), + TestCombinedValue::empty(), + TestCombinedValue::empty(), + TestCombinedValue::empty() ]; let hints = SplitOrderHints { sorted_counters_lt: [11, 0, 0, 0, 0, 0, 0, 0], @@ -353,7 +307,7 @@ mod tests { fn assert_split_sorted_transformed_value_array_asc_empty_extra_item_fails() { let mut builder = TestDataBuilder::empty(); - builder.sorted_transformed_value_array_lt[0].total = 1; + builder.sorted_transformed_value_array_lt[0].value = 1; builder.execute(); } @@ -362,7 +316,7 @@ mod tests { fn assert_split_sorted_transformed_value_array_asc_lt_wrong_sorted_value_fails() { let mut builder = TestDataBuilder::new(); - builder.sorted_transformed_value_array_lt[0].total += 1; + builder.sorted_transformed_value_array_lt[0].value += 1; builder.execute(); } @@ -416,7 +370,7 @@ mod tests { // Move the item with counter 44 from _gte to _lt. builder.sorted_transformed_value_array_lt[2] = builder.sorted_transformed_value_array_gte[2]; builder.hints.sorted_counters_lt[2] = 44; - builder.sorted_transformed_value_array_gte[2] = TestValue::empty(); + builder.sorted_transformed_value_array_gte[2] = TestCombinedValue::empty(); builder.hints.sorted_counters_lt[2] = 0; builder.update_sorted_index(44, 2); // Item of counter 44 is now at index 2 of the sorted array. @@ -482,7 +436,7 @@ mod tests { builder.update_sorted_index(counter, to_index); } // Empty the values at index 0. - builder.sorted_transformed_value_array_lt[0] = TestValue::empty(); + builder.sorted_transformed_value_array_lt[0] = TestCombinedValue::empty(); builder.hints.sorted_counters_lt[0] = 0; builder.execute(); @@ -502,7 +456,7 @@ mod tests { builder.update_sorted_index(counter, to_index); } // Empty the values at index 0. - builder.sorted_transformed_value_array_gte[0] = TestValue::empty(); + builder.sorted_transformed_value_array_gte[0] = TestCombinedValue::empty(); builder.hints.sorted_counters_gte[0] = 0; builder.execute(); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_sorted_transformed_value_arrays/get_split_order_hints.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_sorted_transformed_value_arrays/get_split_order_hints.nr index f00a7650b30d..4cd78bf529e2 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_sorted_transformed_value_arrays/get_split_order_hints.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_sorted_transformed_value_arrays/get_split_order_hints.nr @@ -83,49 +83,26 @@ pub fn get_split_order_hints_desc( mod tests { use crate::{ - abis::side_effect::Ordered, traits::Empty, + tests::types::TestValue, utils::arrays::assert_split_sorted_transformed_value_arrays::get_split_order_hints::{get_split_order_hints_asc, get_split_order_hints_desc, SplitOrderHints} }; - struct TestItem { - value: Field, - counter: u32, - } - - impl Ordered for TestItem { - fn counter(self) -> u32 { - self.counter - } - } - - impl Eq for TestItem { - fn eq(self, other: Self) -> bool { - (self.value == other.value) & (self.counter == other.counter) - } - } - - impl Empty for TestItem { - fn empty() -> Self { - TestItem { value: 0, counter: 0 } - } - } - global full_array = [ - TestItem { value: 100, counter: 11 }, - TestItem { value: 200, counter: 17 }, - TestItem { value: 300, counter: 7 }, - TestItem { value: 400, counter: 5 }, - TestItem { value: 500, counter: 13 } + TestValue { value: 100, counter: 11 }, + TestValue { value: 200, counter: 17 }, + TestValue { value: 300, counter: 7 }, + TestValue { value: 400, counter: 5 }, + TestValue { value: 500, counter: 13 } ]; global padded_array = [ - TestItem { value: 100, counter: 11 }, - TestItem { value: 200, counter: 17 }, - TestItem { value: 300, counter: 7 }, - TestItem { value: 400, counter: 5 }, - TestItem { value: 500, counter: 13 }, - TestItem::empty(), - TestItem::empty() + TestValue { value: 100, counter: 11 }, + TestValue { value: 200, counter: 17 }, + TestValue { value: 300, counter: 7 }, + TestValue { value: 400, counter: 5 }, + TestValue { value: 500, counter: 13 }, + TestValue::empty(), + TestValue::empty() ]; // asc diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_transformed_value_arrays.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_transformed_value_arrays.nr index c1927035aae7..472d70034cdf 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_transformed_value_arrays.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_transformed_value_arrays.nr @@ -70,41 +70,18 @@ pub fn assert_split_transformed_value_arrays( mod tests { use crate::{ - abis::side_effect::Ordered, tests::utils::pad_end, traits::Empty, + tests::{types::TestValue, utils::pad_end}, utils::arrays::assert_split_transformed_value_arrays::{assert_split_transformed_value_arrays, assert_split_transformed_value_arrays_with_hint} }; - struct TestItem { - value: Field, - counter: u32, - } - - impl Ordered for TestItem { - fn counter(self) -> u32 { - self.counter - } - } - - impl Eq for TestItem { - fn eq(self, other: Self) -> bool { - (self.value == other.value) & (self.counter == other.counter) - } - } - - impl Empty for TestItem { - fn empty() -> Self { - TestItem { value: 0, counter: 0 } - } - } - global NUM_TEST_ITEMS = 5; - fn is_transformed(from: TestItem, to: Field) -> bool { + fn is_transformed(from: TestValue, to: Field) -> bool { from.value == to } struct TestBuilder { - sorted_array: [TestItem; NUM_TEST_ITEMS], + sorted_array: [TestValue; NUM_TEST_ITEMS], transformed_value_array_lt: [Field; NUM_TEST_ITEMS], transformed_value_array_gte: [Field; NUM_TEST_ITEMS], split_counter: u32 @@ -113,11 +90,11 @@ mod tests { impl TestBuilder { pub fn new() -> Self { let sorted_array = [ - TestItem { value: 40, counter: 2 }, - TestItem { value: 30, counter: 7 }, - TestItem { value: 80, counter: 11 }, - TestItem { value: 10, counter: 13 }, - TestItem { value: 50, counter: 29 } + TestValue { value: 40, counter: 2 }, + TestValue { value: 30, counter: 7 }, + TestValue { value: 80, counter: 11 }, + TestValue { value: 10, counter: 13 }, + TestValue { value: 50, counter: 29 } ]; let transformed_value_array_lt = pad_end([40, 30, 80], 0); @@ -159,7 +136,7 @@ mod tests { fn assert_split_transformed_value_arrays_empty_succeeds() { let mut builder = TestBuilder::new(); - builder.sorted_array = [TestItem::empty(); NUM_TEST_ITEMS]; + builder.sorted_array = [TestValue::empty(); NUM_TEST_ITEMS]; builder.transformed_value_array_lt = [0; NUM_TEST_ITEMS]; builder.transformed_value_array_gte = [0; NUM_TEST_ITEMS]; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/get_sorted_hints.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/get_sorted_hints.nr deleted file mode 100644 index 9e6a490a0089..000000000000 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/get_sorted_hints.nr +++ /dev/null @@ -1,56 +0,0 @@ -use crate::{traits::{Empty, is_empty}, utils::arrays::{get_sorted_tuple::{get_sorted_tuple, SortedTuple}}}; - -struct SortedResult { - sorted_array: [T; N], - sorted_index_hints: [u32; N], -} - -pub fn get_sorted_hints( - values: [T; N], - ordering: fn(T, T) -> bool -) -> SortedResult where T: Eq + Empty { - let sorted = get_sorted_tuple(values, ordering); - - let sorted_array = sorted.map(|t: SortedTuple| t.elem); - let mut sorted_index_hints = [0; N]; - for i in 0..N { - if !is_empty(sorted[i].elem) { - let original_index = sorted[i].original_index; - sorted_index_hints[original_index] = i; - } - } - - SortedResult { sorted_array, sorted_index_hints } -} - -#[test] -fn get_sorted_hints_asc_non_padded() { - let values = [40, 60, 20, 50]; - let res = get_sorted_hints(values, |a: Field, b: Field| a.lt(b)); - assert_eq(res.sorted_array, [20, 40, 50, 60]); - assert_eq(res.sorted_index_hints, [1, 3, 0, 2]); -} - -#[test] -fn get_sorted_hints_desc_non_padded() { - let values = [40, 20, 60, 50]; - let res = get_sorted_hints(values, |a: Field, b: Field| b.lt(a)); - assert_eq(res.sorted_array, [60, 50, 40, 20]); - assert_eq(res.sorted_index_hints, [2, 3, 0, 1]); -} - -#[test] -fn get_sorted_hints_asc_padded() { - let values = [40, 60, 20, 50, 0, 0]; - let res = get_sorted_hints(values, |a: Field, b: Field| (b == 0) | ((a != 0) & a.lt(b))); - assert_eq(res.sorted_array, [20, 40, 50, 60, 0, 0]); - assert_eq(res.sorted_index_hints, [1, 3, 0, 2, 0, 0]); -} - -#[test] -fn get_sorted_hints_desc_padded() { - let values = [40, 20, 60, 50, 0, 0]; - let res = get_sorted_hints(values, |a: Field, b: Field| (b == 0) | b.lt(a)); - assert_eq(res.sorted_array, [60, 50, 40, 20, 0, 0]); - assert_eq(res.sorted_index_hints, [2, 3, 0, 1, 0, 0]); -} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/get_sorted_result.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/get_sorted_result.nr new file mode 100644 index 000000000000..06f78eea3254 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/get_sorted_result.nr @@ -0,0 +1,59 @@ +use crate::{utils::arrays::{get_sorted_tuple::{get_sorted_tuple, SortedTuple}}}; + +struct SortedResult { + sorted_array: [T; N], + sorted_index_hints: [u32; N], + original_index_hints: [u32; N], +} + +pub fn get_sorted_result( + values: [T; N], + ordering: fn(T, T) -> bool +) -> SortedResult { + let sorted = get_sorted_tuple(values, ordering); + + let sorted_array = sorted.map(|t: SortedTuple| t.elem); + let original_index_hints = sorted.map(|t: SortedTuple| t.original_index); + let mut sorted_index_hints = [0; N]; + for i in 0..N { + let original_index = sorted[i].original_index; + sorted_index_hints[original_index] = i; + } + + SortedResult { sorted_array, sorted_index_hints, original_index_hints } +} + +#[test] +fn get_sorted_hints_asc_non_padded() { + let values = [40, 60, 20, 50]; + let res = get_sorted_result(values, |a: u32, b: u32| a < b); + assert_eq(res.sorted_array, [20, 40, 50, 60]); + assert_eq(res.sorted_index_hints, [1, 3, 0, 2]); + assert_eq(res.original_index_hints, [2, 0, 3, 1]); +} + +#[test] +fn get_sorted_hints_desc_non_padded() { + let values = [40, 20, 60, 50]; + let res = get_sorted_result(values, |a: u32, b: u32| b < a); + assert_eq(res.sorted_array, [60, 50, 40, 20]); + assert_eq(res.sorted_index_hints, [2, 3, 0, 1]); +} + +#[test] +fn get_sorted_hints_asc_padded() { + let values = [40, 60, 20, 50, 0, 0]; + let res = get_sorted_result(values, |a: u32, b: u32| (a != 0) & ((b == 0) | (a < b))); + assert_eq(res.sorted_array, [20, 40, 50, 60, 0, 0]); + assert_eq(res.sorted_index_hints, [1, 3, 0, 2, 4, 5]); + assert_eq(res.original_index_hints, [2, 0, 3, 1, 4, 5]); +} + +#[test] +fn get_sorted_hints_desc_padded() { + let values = [40, 60, 20, 50, 0, 0]; + let res = get_sorted_result(values, |a: u32, b: u32| (a != 0) & ((b == 0) | (b < a))); + assert_eq(res.sorted_array, [60, 50, 40, 20, 0, 0]); + assert_eq(res.sorted_index_hints, [2, 0, 3, 1, 4, 5]); + assert_eq(res.original_index_hints, [1, 3, 0, 2, 4, 5]); +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_by_counter.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_by_counter.nr index b23a317e59a6..2f8da437b5fc 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_by_counter.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_by_counter.nr @@ -18,57 +18,34 @@ pub fn sort_by_counter_desc(array: [T; N]) -> [T; N] where T: Ord mod tests { use crate::{ - abis::side_effect::Ordered, traits::Empty, + tests::types::TestValue, utils::arrays::sort_by_counter::{ compare_by_counter_empty_padded_asc, compare_by_counter_empty_padded_desc, sort_by_counter_asc, sort_by_counter_desc } }; - struct TestItem { - value: u32, - counter: u32, - } - - impl Ordered for TestItem { - fn counter(self) -> u32 { - self.counter - } - } - - impl Eq for TestItem { - fn eq(self, other: Self) -> bool { - (self.value == other.value) & (self.counter == other.counter) - } - } - - impl Empty for TestItem { - fn empty() -> Self { - TestItem { value: 0, counter: 0 } - } - } - fn sort_by_values_asc(values: [u32; N]) -> [u32; N] { - let items = values.map(|value| TestItem { value, counter: value }); - sort_by_counter_asc(items).map(|item: TestItem| item.value) + let items = values.map(|value| TestValue { value: value as Field, counter: value }); + sort_by_counter_asc(items).map(|item: TestValue| item.counter) } fn sort_by_values_desc(values: [u32; N]) -> [u32; N] { - let items = values.map(|value| TestItem { value, counter: value }); - sort_by_counter_desc(items).map(|item: TestItem| item.value) + let items = values.map(|value| TestValue { value: value as Field, counter: value }); + sort_by_counter_desc(items).map(|item: TestValue| item.counter) } fn compare_test_items_asc(value_1: u32, value_2: u32) -> bool { compare_by_counter_empty_padded_asc( - TestItem { value: value_1, counter: value_1 }, - TestItem { value: value_2, counter: value_2 } + TestValue { value: value_1 as Field, counter: value_1 }, + TestValue { value: value_2 as Field, counter: value_2 } ) } fn compare_test_items_desc(value_1: u32, value_2: u32) -> bool { compare_by_counter_empty_padded_desc( - TestItem { value: value_1, counter: value_1 }, - TestItem { value: value_2, counter: value_2 } + TestValue { value: value_1 as Field, counter: value_1 }, + TestValue { value: value_2 as Field, counter: value_2 } ) } @@ -115,22 +92,22 @@ mod tests { #[test] fn sort_by_counter_asc_with_zero_counters() { let original = [ - TestItem { value: 55, counter: 1 }, - TestItem { value: 11, counter: 0 }, - TestItem { value: 33, counter: 2 }, - TestItem { value: 44, counter: 0 }, - TestItem { value: 22, counter: 0 }, - TestItem::empty(), - TestItem::empty() + TestValue { value: 55, counter: 1 }, + TestValue { value: 11, counter: 0 }, + TestValue { value: 33, counter: 2 }, + TestValue { value: 44, counter: 0 }, + TestValue { value: 22, counter: 0 }, + TestValue::empty(), + TestValue::empty() ]; let expected = [ - TestItem { value: 11, counter: 0 }, - TestItem { value: 44, counter: 0 }, - TestItem { value: 22, counter: 0 }, - TestItem { value: 55, counter: 1 }, - TestItem { value: 33, counter: 2 }, - TestItem::empty(), - TestItem::empty() + TestValue { value: 11, counter: 0 }, + TestValue { value: 44, counter: 0 }, + TestValue { value: 22, counter: 0 }, + TestValue { value: 55, counter: 1 }, + TestValue { value: 33, counter: 2 }, + TestValue::empty(), + TestValue::empty() ]; assert_eq(sort_by_counter_asc(original), expected); } @@ -138,22 +115,22 @@ mod tests { #[test] fn sort_by_counter_desc_with_zero_counters() { let original = [ - TestItem { value: 55, counter: 1 }, - TestItem { value: 11, counter: 0 }, - TestItem { value: 33, counter: 2 }, - TestItem { value: 44, counter: 0 }, - TestItem { value: 22, counter: 0 }, - TestItem::empty(), - TestItem::empty() + TestValue { value: 55, counter: 1 }, + TestValue { value: 11, counter: 0 }, + TestValue { value: 33, counter: 2 }, + TestValue { value: 44, counter: 0 }, + TestValue { value: 22, counter: 0 }, + TestValue::empty(), + TestValue::empty() ]; let expected = [ - TestItem { value: 33, counter: 2 }, - TestItem { value: 55, counter: 1 }, - TestItem { value: 22, counter: 0 }, - TestItem { value: 44, counter: 0 }, - TestItem { value: 11, counter: 0 }, - TestItem::empty(), - TestItem::empty() + TestValue { value: 33, counter: 2 }, + TestValue { value: 55, counter: 1 }, + TestValue { value: 22, counter: 0 }, + TestValue { value: 44, counter: 0 }, + TestValue { value: 11, counter: 0 }, + TestValue::empty(), + TestValue::empty() ]; assert_eq(sort_by_counter_desc(original), expected); } From a022143cc81040a666b7597dc0923a6346ddac71 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Thu, 5 Sep 2024 16:05:17 +0000 Subject: [PATCH 13/14] fmt --- .../src/public_validation_request_processor.nr | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_validation_request_processor.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_validation_request_processor.nr index 67cd2ddc5bd7..7e27be861939 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_validation_request_processor.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_validation_request_processor.nr @@ -20,7 +20,8 @@ use dep::types::{ MAX_NULLIFIERS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_PUBLIC_DATA_READS_PER_TX, NOTE_HASH_TREE_HEIGHT }, - data::OverridablePublicDataTreeLeaf, hash::compute_siloed_nullifier, partial_state_reference::PartialStateReference, traits::is_empty, + data::OverridablePublicDataTreeLeaf, hash::compute_siloed_nullifier, + partial_state_reference::PartialStateReference, traits::is_empty, utils::arrays::{array_merge, array_to_bounded_vec, assert_sorted_array} }; From c55440aca767054de54492efde8361aabb66036a Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Thu, 5 Sep 2024 17:36:13 +0000 Subject: [PATCH 14/14] Refactoring. --- .../src/public_kernel_tail.nr | 4 +- .../public_validation_request_processor.nr | 20 ++- .../crates/types/src/utils/arrays.nr | 4 +- .../src/utils/arrays/assert_combined_array.nr | 141 ++++++++++++++++++ .../assert_combined_transformed_array.nr | 39 ++++- .../src/utils/arrays/assert_deduped_array.nr | 6 +- 6 files changed, 197 insertions(+), 17 deletions(-) create mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_array.nr diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr index 6be4cbd06091..c39c9252792d 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr @@ -60,7 +60,9 @@ impl PublicKernelTailCircuitPrivateInputs { previous_kernel_validator.validate_phase(PublicKernelPhase.TAIL); previous_kernel_validator.validate_proof(ALLOWED_PREVIOUS_CIRCUITS); - let (output, output_hints, public_data_read_request_hints) = self.generate_output_and_hints(); + let (output, output_hints, public_data_read_request_hints) = unsafe { + self.generate_output_and_hints() + }; PublicValidationRequestProcessor::new( self.previous_kernel.public_inputs, diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_validation_request_processor.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_validation_request_processor.nr index 7e27be861939..a07fbef61144 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_validation_request_processor.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_validation_request_processor.nr @@ -22,7 +22,7 @@ use dep::types::{ }, data::OverridablePublicDataTreeLeaf, hash::compute_siloed_nullifier, partial_state_reference::PartialStateReference, traits::is_empty, - utils::arrays::{array_merge, array_to_bounded_vec, assert_sorted_array} + utils::arrays::{array_to_bounded_vec, assert_combined_array, assert_sorted_array, combine_arrays} }; struct PublicValidationRequestProcessor { @@ -52,15 +52,19 @@ impl PublicVal public_data_leaves: [OverridablePublicDataTreeLeaf; NUM_PUBLIC_DATA_LEAVES], public_data_read_request_hints: PublicDataReadRequestHints ) -> Self { - let validation_requests = public_inputs.validation_requests; - - let end_non_revertible = public_inputs.end_non_revertible; - let end = public_inputs.end; - - let pending_nullifiers = array_merge(end_non_revertible.nullifiers, end.nullifiers); + let non_revertible_nullifiers = public_inputs.end_non_revertible.nullifiers; + let revertible_nullifiers = public_inputs.end.nullifiers; + let pending_nullifiers = unsafe { + combine_arrays(non_revertible_nullifiers, revertible_nullifiers) + }; + assert_combined_array( + non_revertible_nullifiers, + revertible_nullifiers, + pending_nullifiers + ); PublicValidationRequestProcessor { - validation_requests, + validation_requests: public_inputs.validation_requests, note_hash_read_request_hints, note_hash_tree_root: start_state.note_hash_tree.root, pending_nullifiers, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr index 95ee011bbe0f..8a6d19051315 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr @@ -1,3 +1,4 @@ +mod assert_combined_array; mod assert_combined_sorted_transformed_value_array; mod assert_combined_transformed_array; mod assert_deduped_array; @@ -12,11 +13,12 @@ mod sort_by; mod sort_by_counter; // Re-exports. +use assert_combined_array::{assert_combined_array, combine_arrays}; use assert_combined_sorted_transformed_value_array::{ assert_combined_sorted_transformed_value_array_asc, get_combined_order_hints::{CombinedOrderHint, get_combined_order_hints_asc} }; -use assert_combined_transformed_array::assert_combined_transformed_array; +use assert_combined_transformed_array::{assert_combined_transformed_array, combine_and_transform_arrays}; use assert_exposed_sorted_transformed_value_array::{ assert_exposed_sorted_transformed_value_array, get_order_hints::{get_order_hints_asc, get_order_hints_desc, OrderHint} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_array.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_array.nr new file mode 100644 index 000000000000..90079131ab6a --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_array.nr @@ -0,0 +1,141 @@ +use crate::{ + traits::Empty, + utils::arrays::assert_combined_transformed_array::{assert_combined_transformed_array, combine_and_transform_arrays} +}; + +// original_array(_lt/_gte) must be valid, i.e. validate_array(original_array) == true +// This ensures that combined_array is valid when S can only be empty if and only if T is empty. +pub fn assert_combined_array( + original_array_lt: [T; N], + original_array_gte: [T; N], + combined_array: [T; N] +) where T: Empty + Eq { + assert_combined_transformed_array( + original_array_lt, + original_array_gte, + combined_array, + |from: T, to: T| from == to + ) +} + +unconstrained pub fn combine_arrays( + original_array_lt: [T; N], + original_array_gte: [T; N] +) -> [T; N] where T: Empty + Eq { + combine_and_transform_arrays(original_array_lt, original_array_gte, |from: T| from) +} + +mod tests { + use crate::{ + tests::{types::TestValue, utils::pad_end}, + utils::arrays::assert_combined_array::{assert_combined_array, combine_arrays} + }; + + struct TestBuilder { + original_array_lt: [TestValue; N], + original_array_gte: [TestValue; N], + combined_array: [TestValue; N], + } + + impl TestBuilder<10> { + pub fn new_empty() -> Self { + let original_array_lt = pad_end([], TestValue::empty()); + let original_array_gte = pad_end([], TestValue::empty()); + let combined_array = pad_end([], TestValue::empty()); + TestBuilder { original_array_lt, original_array_gte, combined_array } + } + + pub fn new() -> Self { + let original_array_lt = pad_end( + [ + TestValue { value: 11, counter: 2 }, + TestValue { value: 22, counter: 5 }, + TestValue { value: 33, counter: 3 } + ], + TestValue::empty() + ); + + let original_array_gte = pad_end( + [ + TestValue { value: 44, counter: 1 }, + TestValue { value: 55, counter: 4 } + ], + TestValue::empty() + ); + + let combined_array = pad_end( + [ + TestValue { value: 11, counter: 2 }, + TestValue { value: 22, counter: 5 }, + TestValue { value: 33, counter: 3 }, + TestValue { value: 44, counter: 1 }, + TestValue { value: 55, counter: 4 } + ], + TestValue::empty() + ); + + TestBuilder { original_array_lt, original_array_gte, combined_array } + } + + pub fn execute(self) { + assert_combined_array( + self.original_array_lt, + self.original_array_gte, + self.combined_array + ); + } + + pub fn check_and_execute(self) { + let combined = unsafe { + combine_arrays(self.original_array_lt, self.original_array_gte) + }; + assert_eq(combined, self.combined_array); + + self.execute(); + } + } + + #[test] + fn assert_combined_array_empty_succeeds() { + let builder = TestBuilder::new_empty(); + builder.check_and_execute(); + } + + #[test] + fn assert_combined_array_succeeds() { + let builder = TestBuilder::new(); + builder.check_and_execute(); + } + + #[test(should_fail_with="hinted item in the commbined array does not match")] + fn assert_combined_array_extra_item_fails() { + let mut builder = TestBuilder::new(); + + // Add random value to an empty item. + builder.combined_array[7].value = 123; + + builder.execute(); + } + + #[test(should_fail_with="hinted item in the commbined array does not match")] + fn assert_combined_array_missing_item_fails() { + let mut builder = TestBuilder::new(); + + // Clear the last item. + builder.combined_array[4] = TestValue::empty(); + + builder.execute(); + } + + #[test(should_fail_with="hinted item in the commbined array does not match")] + fn assert_combined_array_unordered_fails() { + let mut builder = TestBuilder::new(); + + // Swap the two items. + let tmp = builder.combined_array[3]; + builder.combined_array[3] = builder.combined_array[1]; + builder.combined_array[1] = tmp; + + builder.execute(); + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_transformed_array.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_transformed_array.nr index 380bbaddff07..bd81809c9b9d 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_transformed_array.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_transformed_array.nr @@ -25,10 +25,28 @@ pub fn assert_combined_transformed_array( } } +unconstrained pub fn combine_and_transform_arrays( + original_array_lt: [T; N], + original_array_gte: [T; N], + transform: fn[Env](T) -> S +) -> [S; N] where T: Empty + Eq { + let mut combined = original_array_lt.map(transform); + + let num_lt = array_length(original_array_lt); + for i in 0..N { + if i >= num_lt { + let from = original_array_gte[i - num_lt]; + combined[i] = transform(from); + } + } + + combined +} + mod tests { use crate::{ - tests::{types::{is_summed_from_two_values, TestTwoValues, TestValue}, utils::pad_end}, - utils::arrays::assert_combined_transformed_array::assert_combined_transformed_array + tests::{types::{is_summed_from_two_values, sum_two_values, TestTwoValues, TestValue}, utils::pad_end}, + utils::arrays::assert_combined_transformed_array::{assert_combined_transformed_array, combine_and_transform_arrays} }; struct TestBuilder { @@ -85,18 +103,31 @@ mod tests { is_summed_from_two_values ); } + + pub fn check_and_execute(self) { + let combined = unsafe { + combine_and_transform_arrays( + self.original_array_lt, + self.original_array_gte, + sum_two_values + ) + }; + assert_eq(combined, self.combined_array); + + self.execute(); + } } #[test] fn assert_combined_transformed_array_empty_succeeds() { let builder = TestBuilder::new_empty(); - builder.execute(); + builder.check_and_execute(); } #[test] fn assert_combined_transformed_array_succeeds() { let builder = TestBuilder::new(); - builder.execute(); + builder.check_and_execute(); } #[test(should_fail_with="hinted item in the commbined array does not match")] diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_deduped_array.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_deduped_array.nr index 438804e8d9dc..3bafbc0de97d 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_deduped_array.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_deduped_array.nr @@ -115,7 +115,7 @@ mod tests { assert_deduped_array(self.original_array, self.deduped_array); } - pub fn succeeds(self) { + pub fn check_and_execute(self) { assert_eq(self.deduped_array, dedupe_array(self.original_array)); self.execute(); } @@ -124,13 +124,13 @@ mod tests { #[test] fn assert_deduped_array_empty_succeeds() { let builder = TestBuilder::new_empty(); - builder.succeeds(); + builder.check_and_execute(); } #[test] fn assert_deduped_array_succeeds() { let builder = TestBuilder::new(); - builder.succeeds(); + builder.check_and_execute(); } #[test(should_fail_with="mismatch deduped item")]