-
Notifications
You must be signed in to change notification settings - Fork 311
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: private kernel output validator (#6892)
Please read [contributing guidelines](CONTRIBUTING.md) and remove this line.
- Loading branch information
Showing
38 changed files
with
1,517 additions
and
299 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 3 additions & 0 deletions
3
noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components.nr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,7 @@ | ||
mod kernel_circuit_output_hints; | ||
mod kernel_circuit_output_validator; | ||
mod kernel_circuit_public_inputs_composer; | ||
mod previous_kernel_validator; | ||
mod private_call_data_validator; | ||
mod private_kernel_circuit_output_validator; | ||
mod private_kernel_circuit_public_inputs_composer; |
109 changes: 109 additions & 0 deletions
109
...protocol-circuits/crates/private-kernel-lib/src/components/kernel_circuit_output_hints.nr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
use dep::types::{ | ||
abis::{ | ||
kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, | ||
log_hash::{LogHash, NoteLogHash, ScopedLogHash, ScopedEncryptedLogHash} | ||
}, | ||
constants::{ | ||
MAX_ENCRYPTED_LOGS_PER_TX, MAX_NEW_L2_TO_L1_MSGS_PER_TX, MAX_NEW_NOTE_HASHES_PER_TX, | ||
MAX_NEW_NULLIFIERS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX | ||
}, | ||
hash::{ | ||
silo_encrypted_log_hash, silo_l2_to_l1_message, silo_note_hash, silo_nullifier, | ||
silo_unencrypted_log_hash | ||
}, | ||
messaging::l2_to_l1_message::ScopedL2ToL1Message, traits::{Empty, is_empty}, | ||
utils::arrays::{OrderHint, sort_by_counters_asc, sort_get_order_hints_asc} | ||
}; | ||
|
||
struct Hints { | ||
// Note hashes. | ||
sorted_note_hash_hints: [OrderHint; MAX_NEW_NOTE_HASHES_PER_TX], | ||
siloed_note_hashes: [Field; MAX_NEW_NOTE_HASHES_PER_TX], | ||
// Nullifiers. | ||
sorted_nullifier_hints: [OrderHint; MAX_NEW_NULLIFIERS_PER_TX], | ||
siloed_nullifiers: [Field; MAX_NEW_NULLIFIERS_PER_TX], | ||
// L2 to l1 msgs. | ||
sorted_l2_to_l1_msg_hints: [OrderHint; MAX_NEW_L2_TO_L1_MSGS_PER_TX], | ||
siloed_l2_to_l1_msgs: [Field; MAX_NEW_L2_TO_L1_MSGS_PER_TX], | ||
// Note encrypted log hashes. | ||
note_encrypted_log_hashes: [LogHash; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], | ||
sorted_note_encrypted_log_hashes: [LogHash; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], | ||
sorted_note_encrypted_log_hash_hints: [OrderHint; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], | ||
// Encrypted log hashes. | ||
siloed_encrypted_log_hashes: [LogHash; MAX_ENCRYPTED_LOGS_PER_TX], | ||
sorted_siloed_encrypted_log_hashes: [LogHash; MAX_ENCRYPTED_LOGS_PER_TX], | ||
sorted_encrypted_log_hash_hints: [OrderHint; MAX_ENCRYPTED_LOGS_PER_TX], | ||
// Unencrypted log hashes. | ||
siloed_unencrypted_log_hashes: [LogHash; MAX_UNENCRYPTED_LOGS_PER_TX], | ||
sorted_siloed_unencrypted_log_hashes: [LogHash; MAX_UNENCRYPTED_LOGS_PER_TX], | ||
sorted_unencrypted_log_hash_hints: [OrderHint; MAX_UNENCRYPTED_LOGS_PER_TX], | ||
} | ||
|
||
unconstrained pub fn generate_hints(previous_kernel: PrivateKernelCircuitPublicInputs) -> Hints { | ||
// note_hashes | ||
let sorted_note_hash_hints = sort_get_order_hints_asc(previous_kernel.end.new_note_hashes); | ||
|
||
let mut siloed_note_hashes = [0; MAX_NEW_NOTE_HASHES_PER_TX]; | ||
let first_nullifier = previous_kernel.end.new_nullifiers[0].value(); | ||
let unsiloed_note_hashes = previous_kernel.end.new_note_hashes; | ||
for i in 0..unsiloed_note_hashes.len() { | ||
siloed_note_hashes[i] = silo_note_hash(unsiloed_note_hashes[i], first_nullifier, i); | ||
} | ||
|
||
// nullifiers | ||
let sorted_nullifier_hints = sort_get_order_hints_asc(previous_kernel.end.new_nullifiers); | ||
let siloed_nullifiers = previous_kernel.end.new_nullifiers.map(silo_nullifier); | ||
|
||
// l2_to_l1_msgs | ||
let sorted_l2_to_l1_msg_hints = sort_get_order_hints_asc(previous_kernel.end.new_l2_to_l1_msgs); | ||
|
||
let tx_context = previous_kernel.constants.tx_context; | ||
let siloed_l2_to_l1_msgs = previous_kernel.end.new_l2_to_l1_msgs.map( | ||
|m: ScopedL2ToL1Message| silo_l2_to_l1_message( | ||
m, | ||
tx_context.version, | ||
tx_context.chain_id, | ||
) | ||
); | ||
|
||
// note_encrypted_logs | ||
let note_encrypted_log_hashes = previous_kernel.end.note_encrypted_logs_hashes.map(|h: NoteLogHash| h.expose_to_public()); | ||
let sorted_note_encrypted_log_hashes = sort_by_counters_asc(previous_kernel.end.note_encrypted_logs_hashes).map(|h: NoteLogHash| h.expose_to_public()); | ||
let sorted_note_encrypted_log_hash_hints = sort_get_order_hints_asc(previous_kernel.end.note_encrypted_logs_hashes); | ||
|
||
// encrypted_logs | ||
let mut siloed_log_hashes = previous_kernel.end.encrypted_logs_hashes; | ||
for i in 0..siloed_log_hashes.len() { | ||
siloed_log_hashes[i].log_hash.value = silo_encrypted_log_hash(previous_kernel.end.encrypted_logs_hashes[i]); | ||
} | ||
let sorted_siloed_encrypted_log_hashes = sort_by_counters_asc(siloed_log_hashes).map(|h: ScopedEncryptedLogHash| h.expose_to_public()); | ||
let siloed_encrypted_log_hashes = siloed_log_hashes.map(|h: ScopedEncryptedLogHash| h.expose_to_public()); | ||
let sorted_encrypted_log_hash_hints = sort_get_order_hints_asc(previous_kernel.end.encrypted_logs_hashes); | ||
|
||
// unencrypted_logs | ||
let mut siloed_log_hashes = previous_kernel.end.unencrypted_logs_hashes; | ||
for i in 0..siloed_log_hashes.len() { | ||
siloed_log_hashes[i].log_hash.value = silo_unencrypted_log_hash(previous_kernel.end.unencrypted_logs_hashes[i]); | ||
} | ||
let sorted_siloed_unencrypted_log_hashes = sort_by_counters_asc(siloed_log_hashes).map(|h: ScopedLogHash| h.inner()); | ||
let siloed_unencrypted_log_hashes = siloed_log_hashes.map(|h: ScopedLogHash| h.inner()); | ||
let sorted_unencrypted_log_hash_hints = sort_get_order_hints_asc(previous_kernel.end.unencrypted_logs_hashes); | ||
|
||
Hints { | ||
sorted_note_hash_hints, | ||
siloed_note_hashes, | ||
sorted_nullifier_hints, | ||
siloed_nullifiers, | ||
sorted_l2_to_l1_msg_hints, | ||
siloed_l2_to_l1_msgs, | ||
note_encrypted_log_hashes, | ||
sorted_note_encrypted_log_hashes, | ||
sorted_siloed_encrypted_log_hashes, | ||
sorted_note_encrypted_log_hash_hints, | ||
siloed_encrypted_log_hashes, | ||
sorted_encrypted_log_hash_hints, | ||
siloed_unencrypted_log_hashes, | ||
sorted_siloed_unencrypted_log_hashes, | ||
sorted_unencrypted_log_hash_hints | ||
} | ||
} |
170 changes: 170 additions & 0 deletions
170
...ocol-circuits/crates/private-kernel-lib/src/components/kernel_circuit_output_validator.nr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
use crate::components::{kernel_circuit_output_hints::{Hints, OrderHint}}; | ||
use dep::types::{ | ||
abis::{ | ||
kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PrivateKernelCircuitPublicInputs}, | ||
log_hash::{LogHash, NoteLogHash, ScopedEncryptedLogHash, ScopedLogHash} | ||
}, | ||
constants::MAX_NEW_NOTE_HASHES_PER_TX, | ||
hash::{ | ||
compute_tx_logs_hash, compute_tx_note_logs_hash, silo_encrypted_log_hash, silo_l2_to_l1_message, | ||
silo_note_hash, silo_nullifier, silo_unencrypted_log_hash | ||
}, | ||
traits::{Empty, is_empty}, utils::arrays::assert_sorted_transformed_value_array | ||
}; | ||
|
||
fn validate_transformed_value_array<T, S, N, Env>( | ||
original_array: [T; N], | ||
transformed_value_array: [S; N], | ||
is_transformed: fn[Env](T, S) -> bool | ||
) { | ||
for i in 0..N { | ||
assert(is_transformed(original_array[i], transformed_value_array[i]), "invalid transformed value"); | ||
} | ||
} | ||
|
||
fn validate_siloed_value_array<T, S, N, Env>( | ||
original_array: [T; N], | ||
transformed_value_array: [S; N], | ||
silo_value: fn[Env](T) -> S | ||
) where S: Empty + Eq { | ||
validate_transformed_value_array( | ||
original_array, | ||
transformed_value_array, | ||
|original: T, transformed: S| transformed == silo_value(original) | ||
); | ||
} | ||
|
||
struct KernelCircuitOutputValidator { | ||
output: KernelCircuitPublicInputs, | ||
previous_kernel: PrivateKernelCircuitPublicInputs | ||
} | ||
|
||
impl KernelCircuitOutputValidator { | ||
pub fn new( | ||
output: KernelCircuitPublicInputs, | ||
previous_kernel: PrivateKernelCircuitPublicInputs | ||
) -> Self { | ||
KernelCircuitOutputValidator { output, previous_kernel } | ||
} | ||
|
||
pub fn validate<N>(self, hints: Hints) { | ||
self.validate_empty_values(); | ||
self.validate_propagated_values(); | ||
self.validate_propagated_sorted_siloed_values(hints); | ||
self.validate_accumulated_values(hints); | ||
} | ||
|
||
fn validate_empty_values(self) { | ||
assert(is_empty(self.output.start_state), "start_state must be empty"); | ||
assert_eq(self.output.revert_code, 0, "revert_code must be empty"); | ||
} | ||
|
||
fn validate_propagated_values(self) { | ||
assert_eq(self.output.constants, self.previous_kernel.constants, "mismatch constants"); | ||
|
||
assert_eq( | ||
self.output.rollup_validation_requests, self.previous_kernel.validation_requests.for_rollup, "mismatch rollup_validation_requests" | ||
); | ||
|
||
assert_eq(self.output.fee_payer, self.previous_kernel.fee_payer, "mismatch fee_payer"); | ||
} | ||
|
||
fn validate_propagated_sorted_siloed_values(self, hints: Hints) { | ||
// new_note_hashes | ||
let first_nullifier = self.output.end.new_nullifiers[0]; | ||
let unsiloed_note_hashes = self.previous_kernel.end.new_note_hashes; | ||
for i in 0..unsiloed_note_hashes.len() { | ||
let siloed_note_hash = silo_note_hash(unsiloed_note_hashes[i], first_nullifier, i); | ||
assert_eq(hints.siloed_note_hashes[i], siloed_note_hash, "mismatch siloed note hashes"); | ||
} | ||
|
||
assert_sorted_transformed_value_array( | ||
self.previous_kernel.end.new_note_hashes, | ||
hints.siloed_note_hashes, | ||
self.output.end.new_note_hashes, | ||
hints.sorted_note_hash_hints | ||
); | ||
|
||
// new_nullifiers | ||
validate_siloed_value_array( | ||
self.previous_kernel.end.new_nullifiers, | ||
hints.siloed_nullifiers, | ||
silo_nullifier | ||
); | ||
|
||
assert_sorted_transformed_value_array( | ||
self.previous_kernel.end.new_nullifiers, | ||
hints.siloed_nullifiers, | ||
self.output.end.new_nullifiers, | ||
hints.sorted_nullifier_hints | ||
); | ||
|
||
// new_l2_to_l1_msgs | ||
let tx_context = self.previous_kernel.constants.tx_context; | ||
validate_siloed_value_array( | ||
self.previous_kernel.end.new_l2_to_l1_msgs, | ||
hints.siloed_l2_to_l1_msgs, | ||
|msg| silo_l2_to_l1_message(msg, tx_context.version, tx_context.chain_id) | ||
); | ||
|
||
assert_sorted_transformed_value_array( | ||
self.previous_kernel.end.new_l2_to_l1_msgs, | ||
hints.siloed_l2_to_l1_msgs, | ||
self.output.end.new_l2_to_l1_msgs, | ||
hints.sorted_l2_to_l1_msg_hints | ||
); | ||
} | ||
|
||
fn validate_accumulated_values(self, hints: Hints) { | ||
// note_encrypted_log_hashes | ||
validate_transformed_value_array( | ||
self.previous_kernel.end.note_encrypted_logs_hashes, | ||
hints.note_encrypted_log_hashes, | ||
|nlh: NoteLogHash, lh: LogHash| (nlh.value == lh.value) & (nlh.length == lh.length) | ||
); | ||
|
||
assert_sorted_transformed_value_array( | ||
self.previous_kernel.end.note_encrypted_logs_hashes, | ||
hints.note_encrypted_log_hashes, | ||
hints.sorted_note_encrypted_log_hashes, | ||
hints.sorted_note_encrypted_log_hash_hints | ||
); | ||
|
||
let hash = compute_tx_note_logs_hash(hints.sorted_note_encrypted_log_hashes); | ||
assert_eq(hash, self.output.end.note_encrypted_logs_hash, "mismatch note_encrypted_logs_hash"); | ||
|
||
// encrypted_log_hashes | ||
validate_transformed_value_array( | ||
self.previous_kernel.end.encrypted_logs_hashes, | ||
hints.siloed_encrypted_log_hashes, | ||
|slh: ScopedEncryptedLogHash, lh: LogHash| (lh.value == silo_encrypted_log_hash(slh)) & (lh.length == slh.log_hash.length) | ||
); | ||
|
||
assert_sorted_transformed_value_array( | ||
self.previous_kernel.end.encrypted_logs_hashes, | ||
hints.siloed_encrypted_log_hashes, | ||
hints.sorted_siloed_encrypted_log_hashes, | ||
hints.sorted_encrypted_log_hash_hints | ||
); | ||
|
||
let hash = compute_tx_logs_hash(hints.sorted_siloed_encrypted_log_hashes); | ||
assert_eq(hash, self.output.end.encrypted_logs_hash, "mismatch encrypted_logs_hash"); | ||
|
||
// unencrypted_log_hashes | ||
validate_transformed_value_array( | ||
self.previous_kernel.end.unencrypted_logs_hashes, | ||
hints.siloed_unencrypted_log_hashes, | ||
|slh: ScopedLogHash, lh: LogHash| (lh.value == silo_unencrypted_log_hash(slh)) & (lh.length == slh.log_hash.length) | ||
); | ||
|
||
assert_sorted_transformed_value_array( | ||
self.previous_kernel.end.unencrypted_logs_hashes, | ||
hints.siloed_unencrypted_log_hashes, | ||
hints.sorted_siloed_unencrypted_log_hashes, | ||
hints.sorted_unencrypted_log_hash_hints | ||
); | ||
|
||
let hash = compute_tx_logs_hash(hints.sorted_siloed_unencrypted_log_hashes); | ||
assert_eq(hash, self.output.end.unencrypted_logs_hash, "mismatch unencrypted_logs_hash"); | ||
} | ||
} |
Oops, something went wrong.