Skip to content

Commit ae86347

Browse files
authored
fix: public data reads and writes verification (#8296)
- Add counter to `PublicDataUpdateRequest` so that we can check the ordering of the reads vs writes and verify that the reads are reading the correct value, either in the tree or a newly updated value. - Improve the algorithm for squashing writes. - Build hints in noir.
1 parent 9fe2c07 commit ae86347

File tree

84 files changed

+2534
-2784
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+2534
-2784
lines changed

noir-projects/aztec-nr/aztec/src/oracle/get_public_data_witness.nr

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
use dep::protocol_types::{
2-
constants::PUBLIC_DATA_TREE_HEIGHT, public_data_tree_leaf_preimage::PublicDataTreeLeafPreimage,
3-
utils::arr_copy_slice
4-
};
1+
use dep::protocol_types::{constants::PUBLIC_DATA_TREE_HEIGHT, data::PublicDataTreeLeafPreimage, utils::arr_copy_slice};
52

63
global LEAF_PREIMAGE_LENGTH: u32 = 4;
74
global PUBLIC_DATA_WITNESS: Field = 45;
@@ -18,7 +15,10 @@ unconstrained fn get_public_data_witness_oracle(
1815
_public_data_tree_index: Field
1916
) -> [Field; PUBLIC_DATA_WITNESS] {}
2017

21-
unconstrained pub fn get_public_data_witness(block_number: u32, public_data_tree_index: Field) -> PublicDataWitness {
18+
unconstrained pub fn get_public_data_witness(
19+
block_number: u32,
20+
public_data_tree_index: Field
21+
) -> PublicDataWitness {
2222
let fields = get_public_data_witness_oracle(block_number, public_data_tree_index);
2323
PublicDataWitness {
2424
index: fields[0],
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,47 @@
11
mod combine_data;
2+
mod generate_output_hints;
3+
mod generate_overridable_public_data_writes;
4+
mod generate_public_data_leaves;
25

3-
use crate::components::public_tail_output_composer::combine_data::combine_data;
6+
use generate_output_hints::{LinkedIndexHint, OutputHints, SiloedNoteHashHint};
7+
8+
use crate::components::public_tail_output_composer::{combine_data::combine_data, generate_output_hints::generate_output_hints};
49
use dep::types::{
510
abis::{kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PublicKernelCircuitPublicInputs}},
6-
partial_state_reference::PartialStateReference
11+
data::PublicDataLeafHint, partial_state_reference::PartialStateReference
712
};
813

9-
struct PublicTailOutputComposer {
14+
struct PublicTailOutputComposer<let NUM_PUBLIC_DATA_LEAVES: u32> {
1015
previous_kernel: PublicKernelCircuitPublicInputs,
1116
start_state: PartialStateReference,
17+
public_data_leaf_hints: [PublicDataLeafHint; NUM_PUBLIC_DATA_LEAVES],
1218
}
1319

14-
impl PublicTailOutputComposer {
20+
impl<let NUM_PUBLIC_DATA_LEAVES: u32> PublicTailOutputComposer<NUM_PUBLIC_DATA_LEAVES> {
1521
pub fn new(
1622
previous_kernel: PublicKernelCircuitPublicInputs,
17-
start_state: PartialStateReference
23+
start_state: PartialStateReference,
24+
public_data_leaf_hints: [PublicDataLeafHint; NUM_PUBLIC_DATA_LEAVES]
1825
) -> Self {
19-
PublicTailOutputComposer { previous_kernel, start_state }
26+
PublicTailOutputComposer { previous_kernel, start_state, public_data_leaf_hints }
2027
}
2128

22-
pub fn finish(self) -> KernelCircuitPublicInputs {
29+
pub fn finish(self) -> (KernelCircuitPublicInputs, OutputHints<NUM_PUBLIC_DATA_LEAVES>) {
30+
let output_hints = generate_output_hints(self.previous_kernel, self.public_data_leaf_hints);
31+
2332
let end = combine_data(
2433
self.previous_kernel.end_non_revertible,
25-
self.previous_kernel.end
34+
self.previous_kernel.end,
35+
output_hints
2636
);
2737

28-
KernelCircuitPublicInputs {
38+
(KernelCircuitPublicInputs {
2939
rollup_validation_requests: self.previous_kernel.validation_requests.for_rollup,
3040
end,
3141
constants: self.previous_kernel.constants,
3242
start_state: self.start_state,
3343
revert_code: self.previous_kernel.revert_code,
3444
fee_payer: self.previous_kernel.fee_payer
35-
}
45+
}, output_hints)
3646
}
3747
}

noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/components/public_tail_output_composer/combine_data.nr

+6-23
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,22 @@
1+
use crate::components::public_tail_output_composer::generate_output_hints::OutputHints;
12
use dep::types::{
23
abis::{
34
accumulated_data::{CombinedAccumulatedData, PublicAccumulatedData},
45
log_hash::{LogHash, ScopedLogHash}, nullifier::Nullifier
56
},
6-
constants::MAX_NOTE_HASHES_PER_TX, hash::silo_note_hash,
77
utils::arrays::{array_merge, dedupe_array, sort_by_counter_asc}
88
};
99

10-
unconstrained pub fn combine_data(
10+
unconstrained pub fn combine_data<let NUM_PUBLIC_DATA_LEAVES: u32>(
1111
non_revertible: PublicAccumulatedData,
12-
revertible: PublicAccumulatedData
12+
revertible: PublicAccumulatedData,
13+
output_hints: OutputHints<NUM_PUBLIC_DATA_LEAVES>
1314
) -> CombinedAccumulatedData {
14-
let mut note_hashes = [0; MAX_NOTE_HASHES_PER_TX];
15-
let sorted_unsiloed_note_hashes = sort_by_counter_asc(array_merge(non_revertible.note_hashes, revertible.note_hashes));
16-
let tx_hash = non_revertible.nullifiers[0].value;
17-
for i in 0..sorted_unsiloed_note_hashes.len() {
18-
let note_hash = sorted_unsiloed_note_hashes[i];
19-
note_hashes[i] = if note_hash.counter() == 0 {
20-
// 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.
21-
note_hash.value()
22-
} else {
23-
silo_note_hash(note_hash, tx_hash, i)
24-
};
25-
}
26-
2715
let nullifiers = sort_by_counter_asc(array_merge(non_revertible.nullifiers, revertible.nullifiers)).map(|n: Nullifier| n.value);
2816

2917
let l2_to_l1_msgs = sort_by_counter_asc(array_merge(non_revertible.l2_to_l1_msgs, revertible.l2_to_l1_msgs));
3018

31-
let public_data_update_requests = dedupe_array(
32-
array_merge(
33-
non_revertible.public_data_update_requests,
34-
revertible.public_data_update_requests
35-
)
36-
);
19+
let public_data_update_requests = dedupe_array(output_hints.public_data_writes);
3720

3821
let note_encrypted_logs_hashes = sort_by_counter_asc(
3922
array_merge(
@@ -60,7 +43,7 @@ unconstrained pub fn combine_data(
6043
let unencrypted_log_preimages_length = unencrypted_logs_hashes.fold(0, |a, b: ScopedLogHash| a + b.log_hash.length);
6144

6245
CombinedAccumulatedData {
63-
note_hashes,
46+
note_hashes: output_hints.siloed_note_hashes,
6447
nullifiers,
6548
l2_to_l1_msgs,
6649
note_encrypted_logs_hashes,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
use crate::components::public_tail_output_composer::{
2+
generate_overridable_public_data_writes::{generate_overridable_public_data_writes, LinkedIndexHint},
3+
generate_public_data_leaves::generate_public_data_leaves
4+
};
5+
use dep::types::{
6+
abis::{
7+
kernel_circuit_public_inputs::PublicKernelCircuitPublicInputs,
8+
public_data_write::OverridablePublicDataWrite
9+
},
10+
constants::{
11+
MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, MAX_L2_TO_L1_MSGS_PER_TX,
12+
MAX_UNENCRYPTED_LOGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX,
13+
MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX
14+
},
15+
data::{OverridablePublicDataTreeLeaf, PublicDataLeafHint}, hash::silo_note_hash, traits::Empty,
16+
utils::arrays::{array_merge, CombinedOrderHint, get_combined_order_hints_asc, sort_by_counter_asc, SortedResult}
17+
};
18+
19+
struct SiloedNoteHashHint {
20+
siloed_note_hash: Field,
21+
index: u32,
22+
}
23+
24+
impl Empty for SiloedNoteHashHint {
25+
fn empty() -> Self {
26+
SiloedNoteHashHint { siloed_note_hash: 0, index: 0 }
27+
}
28+
}
29+
30+
impl Eq for SiloedNoteHashHint {
31+
fn eq(self, other: Self) -> bool {
32+
(self.siloed_note_hash == other.siloed_note_hash) & (self.index == other.index)
33+
}
34+
}
35+
36+
struct OutputHints<let NUM_PUBLIC_DATA_LEAVES: u32> {
37+
siloed_note_hashes: [Field; MAX_NOTE_HASHES_PER_TX],
38+
siloed_note_hash_hints: [SiloedNoteHashHint; MAX_NOTE_HASHES_PER_TX],
39+
sorted_note_hash_hints: [CombinedOrderHint; MAX_NOTE_HASHES_PER_TX],
40+
sorted_nullifier_hints: [CombinedOrderHint; MAX_NULLIFIERS_PER_TX],
41+
sorted_l2_to_l1_msg_hints: [CombinedOrderHint; MAX_L2_TO_L1_MSGS_PER_TX],
42+
sorted_note_encrypted_log_hash_hints: [CombinedOrderHint; MAX_NOTE_ENCRYPTED_LOGS_PER_TX],
43+
sorted_encrypted_log_hash_hints: [CombinedOrderHint; MAX_ENCRYPTED_LOGS_PER_TX],
44+
sorted_unencrypted_log_hash_hints: [CombinedOrderHint; MAX_UNENCRYPTED_LOGS_PER_TX],
45+
public_data_writes: [OverridablePublicDataWrite; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX],
46+
public_data_leaves: [OverridablePublicDataTreeLeaf; NUM_PUBLIC_DATA_LEAVES],
47+
unique_slot_index_hints: SortedResult<Field, NUM_PUBLIC_DATA_LEAVES>,
48+
public_data_linked_index_hints: [LinkedIndexHint; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX],
49+
}
50+
51+
unconstrained pub fn generate_output_hints<let NUM_PUBLIC_DATA_LEAVES: u32>(
52+
previous_kernel: PublicKernelCircuitPublicInputs,
53+
public_data_leaf_hints: [PublicDataLeafHint; NUM_PUBLIC_DATA_LEAVES]
54+
) -> OutputHints<NUM_PUBLIC_DATA_LEAVES> {
55+
let non_revertible = previous_kernel.end_non_revertible;
56+
let revertible = previous_kernel.end;
57+
58+
// Note hashes.
59+
let mut siloed_note_hashes = [0; MAX_NOTE_HASHES_PER_TX];
60+
let mut siloed_note_hash_hints = [SiloedNoteHashHint::empty(); MAX_NOTE_HASHES_PER_TX];
61+
let sorted_unsiloed_note_hashes = sort_by_counter_asc(array_merge(non_revertible.note_hashes, revertible.note_hashes));
62+
let tx_hash = non_revertible.nullifiers[0].value;
63+
for i in 0..sorted_unsiloed_note_hashes.len() {
64+
let note_hash = sorted_unsiloed_note_hashes[i];
65+
let siloed_note_hash = if note_hash.counter() == 0 {
66+
// 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.
67+
note_hash.value()
68+
} else {
69+
silo_note_hash(note_hash, tx_hash, i)
70+
};
71+
siloed_note_hashes[i] = siloed_note_hash;
72+
if siloed_note_hash != 0 {
73+
siloed_note_hash_hints[i] = SiloedNoteHashHint { siloed_note_hash, index: i };
74+
}
75+
}
76+
77+
// Public data.
78+
let combined_writes = array_merge(
79+
previous_kernel.end_non_revertible.public_data_update_requests,
80+
previous_kernel.end.public_data_update_requests
81+
);
82+
let (public_data_leaves, unique_slot_index_hints) = generate_public_data_leaves(
83+
previous_kernel.validation_requests.public_data_reads,
84+
combined_writes,
85+
public_data_leaf_hints
86+
);
87+
let (public_data_writes, public_data_linked_index_hints) = generate_overridable_public_data_writes(combined_writes, public_data_leaves);
88+
89+
OutputHints {
90+
siloed_note_hashes,
91+
siloed_note_hash_hints,
92+
sorted_note_hash_hints: get_combined_order_hints_asc(non_revertible.note_hashes, revertible.note_hashes),
93+
sorted_nullifier_hints: get_combined_order_hints_asc(non_revertible.nullifiers, revertible.nullifiers),
94+
sorted_l2_to_l1_msg_hints: get_combined_order_hints_asc(non_revertible.l2_to_l1_msgs, revertible.l2_to_l1_msgs),
95+
sorted_note_encrypted_log_hash_hints: get_combined_order_hints_asc(
96+
non_revertible.note_encrypted_logs_hashes,
97+
revertible.note_encrypted_logs_hashes
98+
),
99+
sorted_encrypted_log_hash_hints: get_combined_order_hints_asc(
100+
non_revertible.encrypted_logs_hashes,
101+
revertible.encrypted_logs_hashes
102+
),
103+
sorted_unencrypted_log_hash_hints: get_combined_order_hints_asc(
104+
non_revertible.unencrypted_logs_hashes,
105+
revertible.unencrypted_logs_hashes
106+
),
107+
public_data_writes,
108+
public_data_leaves,
109+
unique_slot_index_hints,
110+
public_data_linked_index_hints
111+
}
112+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
use dep::types::{
2+
abis::{public_data_update_request::PublicDataUpdateRequest, public_data_write::OverridablePublicDataWrite},
3+
data::OverridablePublicDataTreeLeaf, traits::Empty, utils::arrays::{array_length, find_index_hint}
4+
};
5+
6+
struct LinkedIndexHint {
7+
is_first_write: bool,
8+
prev_index: u32,
9+
}
10+
11+
impl Empty for LinkedIndexHint {
12+
fn empty() -> Self {
13+
LinkedIndexHint { is_first_write: false, prev_index: 0 }
14+
}
15+
}
16+
17+
unconstrained pub fn generate_overridable_public_data_writes<let NUM_WRITES: u32, let NUM_LEAVES: u32>(
18+
public_data_writes: [PublicDataUpdateRequest; NUM_WRITES],
19+
public_data_leaves: [OverridablePublicDataTreeLeaf; NUM_LEAVES]
20+
) -> ([OverridablePublicDataWrite; NUM_WRITES], [LinkedIndexHint; NUM_WRITES]) {
21+
let mut overridable_public_data_writes = [OverridablePublicDataWrite::empty(); NUM_WRITES];
22+
let mut hints = [LinkedIndexHint::empty(); NUM_WRITES];
23+
24+
let writes_len = array_length(public_data_writes);
25+
for i in 0..writes_len {
26+
let write = public_data_writes[i];
27+
let mut override_counter = 0;
28+
let mut is_first_write = false;
29+
let mut prev_index = 0;
30+
let mut prev_counter = 0;
31+
32+
for j in 0..writes_len {
33+
let other = public_data_writes[j];
34+
if (j != i) & (other.leaf_slot == write.leaf_slot) {
35+
if other.counter > write.counter {
36+
if (override_counter == 0) | (other.counter < override_counter) {
37+
override_counter = other.counter;
38+
}
39+
} else if other.counter < write.counter {
40+
if other.counter > prev_counter {
41+
prev_counter = other.counter;
42+
prev_index = j;
43+
}
44+
}
45+
}
46+
}
47+
48+
if prev_counter == 0 {
49+
is_first_write = true;
50+
prev_index = find_index_hint(public_data_leaves, |leaf: OverridablePublicDataTreeLeaf| leaf.leaf.slot == write.leaf_slot);
51+
}
52+
53+
overridable_public_data_writes[i] = OverridablePublicDataWrite { write, override_counter };
54+
hints[i] = LinkedIndexHint { is_first_write, prev_index };
55+
}
56+
57+
(overridable_public_data_writes, hints)
58+
}

0 commit comments

Comments
 (0)