-
Notifications
You must be signed in to change notification settings - Fork 592
draft: aztec-nr review #21213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
draft: aztec-nr review #21213
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,3 @@ | ||
| // TODO: Can be deleted. Not used anywhere. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will also remove, thanks.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in #22244. |
||
| // protocl global vars are equal to the private ones so we just re-export them here under a different name | ||
| pub use crate::protocol::abis::global_variables::GlobalVariables as PublicGlobalVariables; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,7 +40,7 @@ use crate::protocol::{ | |
| MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PRIVATE_LOGS_PER_CALL, MAX_TX_LIFETIME, | ||
| NULL_MSG_SENDER_CONTRACT_ADDRESS, PRIVATE_LOG_SIZE_IN_FIELDS, | ||
| }, | ||
| hash::poseidon2_hash, | ||
| hash::compute_contract_class_log_hash, | ||
| messaging::l2_to_l1_message::L2ToL1Message, | ||
| side_effect::{Counted, scoped::Scoped}, | ||
| traits::{Empty, Hash, ToField}, | ||
|
|
@@ -575,6 +575,12 @@ impl PrivateContext { | |
| pub fn end_setup(&mut self) { | ||
| // Incrementing the side effect counter when ending setup ensures non ambiguity for the counter where we change | ||
| // phases. | ||
| // TODO: Can remove this manually incrementing the side effect counter. | ||
| // It's not necessary to increment the side effect counter here, because the side effect counter is always | ||
| // incremented by the next_counter function every time a side effect is emitted. | ||
| // This was added because the code below used to be: | ||
| // `self.min_revertible_side_effect_counter = self.side_effect_counter;` | ||
| // which made it necessary to increment the side effect counter manually. | ||
nventuro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| self.side_effect_counter += 1; | ||
| // crate::protocol::logging::debug_log_format( | ||
| // "Ending setup at counter {0}", | ||
|
|
@@ -949,10 +955,10 @@ impl PrivateContext { | |
| // Safety: The below length is constrained in the base rollup, which will make sure that all the fields beyond | ||
| // length are zero. However, it won't be able to check that we didn't add extra padding (trailing zeroes) or | ||
| // that we cut trailing zeroes from the end. | ||
| let length = unsafe { trimmed_array_length_hint(log_to_emit) }; | ||
| let length = unsafe { trimmed_array_length_hint(log) }; | ||
| // We hash the entire padded log to ensure a user cannot pass a shorter length and so emit incorrect shorter | ||
| // bytecode. | ||
| let log_hash = poseidon2_hash(log_to_emit); | ||
| let log_hash = compute_contract_class_log_hash(log_to_emit); | ||
nventuro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // Safety: the below only exists to broadcast the raw log, so we can provide it to the base rollup later to be | ||
| // constrained. | ||
| unsafe { | ||
|
|
@@ -1110,6 +1116,9 @@ impl PrivateContext { | |
| args_hash: Field, | ||
| is_static_call: bool, | ||
| ) -> ReturnsHash { | ||
| // Q: It can be misleading to make a static call when the caller specifically requests a non-static call | ||
| // (`is_static_call == false`). Although this is not allowed and will cause the kernel to fail. | ||
| // But rather than altering the user's intent, throw an error from the simulator might be better? | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed, there's a planned refactor to call interfaces. In practice this ends up not happening as users don't construct these calls manually, but we should still fix the plumbing here. |
||
| let is_static_call = is_static_call | self.inputs.call_context.is_static_call; | ||
| let start_side_effect_counter = self.side_effect_counter; | ||
|
|
||
|
|
@@ -1143,14 +1152,11 @@ impl PrivateContext { | |
| }, | ||
| ); | ||
|
|
||
| // TODO (fees) figure out why this crashes the prover and enable it we need this in order to pay fees inside | ||
| // child call contexts assert( | ||
| // (item.public_inputs.min_revertible_side_effect_counter == 0 as u32) | ||
| // | (item.public_inputs.min_revertible_side_effect_counter | ||
| // > self.min_revertible_side_effect_counter) | ||
| // ); if item.public_inputs.min_revertible_side_effect_counter | ||
| // > self.min_revertible_side_effect_counter { self.min_revertible_side_effect_counter = | ||
| // item.public_inputs.min_revertible_side_effect_counter; } | ||
| // The kernel circuits ensure that end_side_effect_counter is greater than start_side_effect_counter, | ||
| // and that all side effects emitted in the child call have counters within the range | ||
| // [start_side_effect_counter, end_side_effect_counter]. | ||
| // Therefore, we only need to ensure that the next side effect from the current call starts after the | ||
| // end side effect from the child call. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in #22245. |
||
| self.side_effect_counter = end_side_effect_counter + 1; // TODO: call `next_counter` | ||
| // instead, for consistency | ||
| ReturnsHash::new(returns_hash) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -104,6 +104,8 @@ pub fn assert_nullifier_did_not_exist_by(block_header: BlockHeader, siloed_nulli | |
| ); | ||
|
|
||
| // 4) Prove that the low leaf is indeed smaller than the nullifier | ||
| // Q: Maybe use low_nullifier_leaf.nullifier.lt(siloed_nullifier) directly instead? | ||
| // We should probably remove full_field_less_than and full_field_greater_than. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think these are from before those existed |
||
| assert( | ||
| full_field_less_than(low_nullifier_leaf.nullifier, siloed_nullifier), | ||
| "Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed", | ||
|
|
@@ -112,6 +114,7 @@ pub fn assert_nullifier_did_not_exist_by(block_header: BlockHeader, siloed_nulli | |
| // 5) Prove that the low leaf is pointing "over" the nullifier, which means that the nullifier is not included in | ||
| // the nullifier tree, since if it were it'd need to be between the low leaf and the next leaf. Note the special | ||
| // case in which the low leaf is the largest of all entries, in which case there's no 'next' entry. | ||
| // Q: Same here, siloed_nullifier.lt(low_nullifier_leaf.next_nullifier) should be easier to read? | ||
| assert( | ||
| full_field_greater_than(low_nullifier_leaf.next_nullifier, siloed_nullifier) | ||
| | (low_nullifier_leaf.next_index == 0), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,6 +33,8 @@ fn extract_property_value_from_selector<let N: u32>(packed_note: [Field; N], sel | |
| let mut acc: Field = 1; | ||
| for i in 0..32 { | ||
| if i < length { | ||
| // TODO: Any non-zero offset will cause the index to be out of bounds. | ||
| // Is it supposed to be `31 - offset - 1`? | ||
| value_field += value[(31 + offset - i) as u32] as Field * acc; | ||
| acc = acc * 256; | ||
| } | ||
|
|
@@ -41,6 +43,12 @@ fn extract_property_value_from_selector<let N: u32>(packed_note: [Field; N], sel | |
| } | ||
|
|
||
| fn check_packed_note<let N: u32>(packed_note: [Field; N], selects: BoundedVec<Option<Select>, N>) { | ||
| // TODO: selects.len() as a loop bound requires compile-time known length, which is true for the existing use | ||
| // cases. | ||
| // But if a caller conditionally pushes selects based on a runtime variable, this will fail to compile. | ||
| // Consider using `for i in 0..N { if i < selects.len() ... }` instead. When .len() is compile-time known, the | ||
| // compiler optimizes both forms to the same gate count. | ||
| // Same for `check_notes_order`. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good callout, yes. We should iterate over |
||
| for i in 0..selects.len() { | ||
| let select = selects.get_unchecked(i).unwrap_unchecked(); | ||
| let value_field = extract_property_value_from_selector(packed_note, select.property_selector); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -46,6 +46,8 @@ pub fn fields_from_bytes<let N: u32>(bytes: BoundedVec<u8, N>) -> BoundedVec<Fie | |
| let p = std::field::modulus_be_bytes(); | ||
|
|
||
| // Since input length is a multiple of 32, we can simply process all chunks fully | ||
| // TODO: Should be `for i in 0..N / 32 { if i < bytes.len() / 32 { ... } }` instead. | ||
| // This hasn't caused any problem because it's only used in an unconstrained fn at the moment. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch. This function is due a small rework, as the name doesn't clearly explain what it is it does (it's not 'conversion'), we'll make it unconstrained as part of that. |
||
| for i in 0..bytes.len() / 32 { | ||
| let mut field = 0; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,7 +8,7 @@ pub unconstrained fn get_random_bytes<let N: u32>() -> [u8; N] { | |
| for i in 0..N { | ||
| if idx == 32 { | ||
| randomness = random().to_be_bytes(); | ||
| idx = 1; // Skip the first byte as it's always 0. | ||
| idx = 1; // Skip the first byte as it's biased (not uniformly distributed over 0-255). | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We got rid of this entire file as this function was bad, replaced for the new poseidon masking mechanism |
||
| } | ||
| bytes[i] = randomness[idx]; | ||
| idx += 1; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -50,6 +50,8 @@ impl NoteHash for UintNote { | |
|
|
||
| // Then compute the completion note hash. In a real partial note this step would be performed in public. | ||
| partial_note.compute_complete_note_hash(self.value) | ||
|
|
||
| // Q: The partial note and the complete note probably shouldn't use the same domain separator? | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this is the followup work to be done in #21189 - we need new domseps, and we need to change how the final note hash is computed (so that it guarantees separation by storage slot). Partial note hash construction is about to get reworked, which is why this looks wrong now. |
||
| } | ||
| // docs:end:compute_note_hash | ||
|
|
||
|
|
@@ -225,6 +227,7 @@ impl PartialUintNote { | |
| // contract that emitted the nullifier) as it checks the value directly against the nullifier tree and all the | ||
| // nullifiers in the tree are siloed by the protocol. | ||
| let siloed_validity_commitment = compute_siloed_nullifier(context.this_address(), validity_commitment); | ||
| // Q: Why not use context.assert_nullifier_exists()? | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unclear, that function may not have been nicely exposed at the time. Agreed that this is better, thanks. Possibly done this way because the author was porting the public case, and had settled nullifiers in mind. |
||
| assert_nullifier_existed_by( | ||
| context.get_anchor_block_header(), | ||
| siloed_validity_commitment, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will remove in the next pass, thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in #22243