Skip to content
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

feat: public inputs refactor #5500

Merged
merged 17 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,10 @@ library Constants {
uint256 internal constant MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL = 2;
uint256 internal constant MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL = 1;
uint256 internal constant MAX_NEW_NOTE_HASHES_PER_TX = 64;
uint256 internal constant MAX_NON_REVERTIBLE_NOTE_HASHES_PER_TX = 8;
uint256 internal constant MAX_REVERTIBLE_NOTE_HASHES_PER_TX = 56;
uint256 internal constant MAX_NEW_NULLIFIERS_PER_TX = 64;
uint256 internal constant MAX_NON_REVERTIBLE_NULLIFIERS_PER_TX = 8;
uint256 internal constant MAX_REVERTIBLE_NULLIFIERS_PER_TX = 56;
uint256 internal constant MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX = 8;
uint256 internal constant MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX = 8;
uint256 internal constant MAX_NON_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX = 3;
uint256 internal constant MAX_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX = 5;
uint256 internal constant MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX = 32;
uint256 internal constant MAX_NON_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX = 16;
uint256 internal constant MAX_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX = 16;
uint256 internal constant MAX_PUBLIC_DATA_READS_PER_TX = 32;
uint256 internal constant MAX_NEW_L2_TO_L1_MSGS_PER_TX = 2;
uint256 internal constant MAX_NOTE_HASH_READ_REQUESTS_PER_TX = 128;
Expand Down
2 changes: 2 additions & 0 deletions noir-projects/noir-protocol-circuits/Nargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ members = [
"crates/private-kernel-inner-simulated",
"crates/private-kernel-tail",
"crates/private-kernel-tail-simulated",
"crates/private-kernel-tail-to-public",
"crates/private-kernel-tail-to-public-simulated",
"crates/public-kernel-lib",
"crates/public-kernel-setup",
"crates/public-kernel-setup-simulated",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use dep::private_kernel_lib::PrivateKernelInitCircuitPrivateInputs;
use dep::types::PrivateKernelInnerCircuitPublicInputs;
use dep::types::PrivateKernelCircuitPublicInputs;

unconstrained fn main(input: PrivateKernelInitCircuitPrivateInputs) -> distinct pub PrivateKernelInnerCircuitPublicInputs {
unconstrained fn main(input: PrivateKernelInitCircuitPrivateInputs) -> distinct pub PrivateKernelCircuitPublicInputs {
input.native_private_kernel_circuit_initial()
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use dep::private_kernel_lib::PrivateKernelInitCircuitPrivateInputs;
use dep::types::PrivateKernelInnerCircuitPublicInputs;
use dep::types::PrivateKernelCircuitPublicInputs;

fn main(input: PrivateKernelInitCircuitPrivateInputs) -> distinct pub PrivateKernelInnerCircuitPublicInputs {
fn main(input: PrivateKernelInitCircuitPrivateInputs) -> distinct pub PrivateKernelCircuitPublicInputs {
input.native_private_kernel_circuit_initial()
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use dep::private_kernel_lib::PrivateKernelInnerCircuitPrivateInputs;
use dep::types::PrivateKernelInnerCircuitPublicInputs;
use dep::types::PrivateKernelCircuitPublicInputs;

unconstrained fn main(input: PrivateKernelInnerCircuitPrivateInputs) -> distinct pub PrivateKernelInnerCircuitPublicInputs {
unconstrained fn main(input: PrivateKernelInnerCircuitPrivateInputs) -> distinct pub PrivateKernelCircuitPublicInputs {
input.native_private_kernel_circuit_inner()
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use dep::private_kernel_lib::PrivateKernelInnerCircuitPrivateInputs;
use dep::types::PrivateKernelInnerCircuitPublicInputs;
use dep::types::PrivateKernelCircuitPublicInputs;

fn main(input: PrivateKernelInnerCircuitPrivateInputs) -> distinct pub PrivateKernelInnerCircuitPublicInputs {
fn main(input: PrivateKernelInnerCircuitPrivateInputs) -> distinct pub PrivateKernelCircuitPublicInputs {
input.native_private_kernel_circuit_inner()
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use dep::std;
use dep::types::{
abis::{
call_request::CallRequest, accumulated_data::CombinedAccumulatedData,
call_request::CallRequest, accumulated_data::PrivateAccumulatedData,
kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputsBuilder,
max_block_number::MaxBlockNumber, membership_witness::NoteHashReadRequestMembershipWitness,
private_circuit_public_inputs::PrivateCircuitPublicInputs,
private_kernel::private_call_data::PrivateCallData, kernel_data::PrivateKernelInnerData,
private_kernel::private_call_data::PrivateCallData, kernel_data::PrivateKernelData,
side_effect::{SideEffect, SideEffectLinkedToNoteHash}
},
address::{AztecAddress, EthAddress, PartialAddress, compute_initialization_hash},
Expand All @@ -15,10 +15,9 @@ use dep::types::{
MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL,
MAX_NULLIFIER_READ_REQUESTS_PER_CALL, MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL
},
grumpkin_private_key::GrumpkinPrivateKey,
hash::{
compute_l2_to_l1_hash, compute_logs_hash, pedersen_hash, private_functions_root_from_siblings,
silo_note_hash, silo_nullifier, stdlib_recursion_verification_key_compress_native_vk
compute_l2_to_l1_hash, compute_logs_hash, private_functions_root_from_siblings, silo_note_hash,
silo_nullifier, stdlib_recursion_verification_key_compress_native_vk
},
merkle_tree::check_membership,
utils::{arrays::{array_length, array_to_bounded_vec, validate_array}},
Expand Down Expand Up @@ -85,7 +84,7 @@ pub fn validate_note_hash_read_requests(
}

pub fn initialize_end_values(
previous_kernel: PrivateKernelInnerData,
previous_kernel: PrivateKernelData,
public_inputs: &mut PrivateKernelCircuitPublicInputsBuilder
) {
public_inputs.constants = previous_kernel.public_inputs.constants;
Expand Down Expand Up @@ -345,7 +344,7 @@ pub fn contract_logic(private_call: PrivateCallData) {
assert(computed_address.eq(contract_address), "computed contract address does not match expected one");
}

pub fn validate_previous_kernel_values(end: CombinedAccumulatedData) {
pub fn validate_previous_kernel_values(end: PrivateAccumulatedData) {
assert(end.new_nullifiers[0].value != 0, "The 0th nullifier in the accumulated nullifier array is zero");
}

Expand Down Expand Up @@ -379,28 +378,3 @@ pub fn validate_call_against_request(private_call: PrivateCallData, request: Cal
);
}
}

fn field_to_grumpkin_private_key(val: Field) -> GrumpkinPrivateKey {
let bytes = val.to_be_bytes(32);
let mut v = 1;
let mut high = 0;
let mut low = 0;

for i in 0..16 {
high = high + (bytes[15 - i] as Field) * v;
low = low + (bytes[16 + 15 - i] as Field) * v;
v = v * 256;
}

GrumpkinPrivateKey { high, low }
}

pub fn compute_siloed_nullifier_secret_key(secret_key: GrumpkinPrivateKey, contract_address: AztecAddress) -> GrumpkinPrivateKey {
// TODO: Temporary hack. Should replace it with a secure way to derive the secret key.
// Match the way keys are derived in circuits.js/src/keys/index.ts
let hash = pedersen_hash(
[secret_key.high, secret_key.low, contract_address.to_field()],
0
);
field_to_grumpkin_private_key(hash)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
use crate::common;
use dep::std::{cmp::Eq, option::Option, unsafe};
use dep::reset_kernel_lib::{NullifierReadRequestHints, PrivateValidationRequestProcessor};
use dep::types::{
abis::{
kernel_data::PrivateKernelData,
kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PrivateKernelCircuitPublicInputsBuilder, PublicKernelCircuitPublicInputs},
side_effect::{SideEffect, SideEffectLinkedToNoteHash, Ordered}
},
constants::{
MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX,
MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX,
MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX
},
grumpkin_private_key::GrumpkinPrivateKey,
hash::{compute_note_hash_nonce, compute_unique_siloed_note_hash},
utils::arrays::{array_length, array_to_bounded_vec, assert_sorted_array}, traits::{Empty, is_empty}
};

fn asc_sort_by_counters<T>(a: T, b: T) -> bool where T: Ordered {
a.counter() < b.counter()
}

struct KernelCircuitPublicInputsComposer {
public_inputs: PrivateKernelCircuitPublicInputsBuilder,
previous_kernel: PrivateKernelData,
sorted_note_hashes: [SideEffect; MAX_NEW_NOTE_HASHES_PER_TX],
sorted_note_hashes_indexes: [u64; MAX_NEW_NOTE_HASHES_PER_TX],
sorted_nullifiers: [SideEffectLinkedToNoteHash; MAX_NEW_NULLIFIERS_PER_TX],
sorted_nullifiers_indexes: [u64; MAX_NEW_NULLIFIERS_PER_TX],
transient_note_hash_index_hints: [u64; MAX_NEW_NULLIFIERS_PER_TX],
}

impl KernelCircuitPublicInputsComposer {
pub fn new(
previous_kernel: PrivateKernelData,
sorted_note_hashes: [SideEffect; MAX_NEW_NOTE_HASHES_PER_TX],
sorted_note_hashes_indexes: [u64; MAX_NEW_NOTE_HASHES_PER_TX],
sorted_nullifiers: [SideEffectLinkedToNoteHash; MAX_NEW_NULLIFIERS_PER_TX],
sorted_nullifiers_indexes: [u64; MAX_NEW_NULLIFIERS_PER_TX],
transient_note_hash_index_hints: [u64; MAX_NEW_NULLIFIERS_PER_TX]
) -> Self {
let public_inputs: PrivateKernelCircuitPublicInputsBuilder = unsafe::zeroed();

KernelCircuitPublicInputsComposer {
public_inputs,
previous_kernel,
sorted_note_hashes,
sorted_note_hashes_indexes,
sorted_nullifiers,
sorted_nullifiers_indexes,
transient_note_hash_index_hints
}
}

pub fn compose(&mut self) -> Self {
assert_eq(
array_length(self.previous_kernel.public_inputs.end.private_call_stack), 0, "Private call stack must be empty when executing the tail circuit"
);

self.propagate_rollup_validation_requests();

self.propagate_constant_data();

self.propagate_sorted_arrays();

// TODO: Should be done in a reset circuit.
self.squash_transient_note_hashes_and_nullifiers();

self.silo_values();

*self
}

pub fn compose_public(&mut self) -> Self {
let _ = self.compose();

self.propagate_sorted_public_call_requests();

*self
}

pub fn finish(self) -> KernelCircuitPublicInputs {
self.public_inputs.finish_tail()
}

pub fn finish_to_public(self) -> PublicKernelCircuitPublicInputs {
let min_revertible_side_effect_counter = self.previous_kernel.public_inputs.min_revertible_side_effect_counter;
self.public_inputs.finish_to_public(min_revertible_side_effect_counter)
}

fn silo_values(&mut self) {
self.silo_note_hashes();
// TODO: Move siloing from init/inner circuits to here.
}

fn silo_note_hashes(&mut self) {
let first_nullifier = self.public_inputs.end.new_nullifiers.get_unchecked(0);
assert(first_nullifier.value != 0, "The 0th nullifier in the accumulated nullifier array is zero");

let note_hashes = self.public_inputs.end.new_note_hashes.storage;
for i in 0..MAX_NEW_NOTE_HASHES_PER_TX {
let note_hash = note_hashes[i];
if note_hash.value != 0 {
let nonce = compute_note_hash_nonce(first_nullifier.value, i);
let unique_note_hash = compute_unique_siloed_note_hash(nonce, note_hash.value);
self.public_inputs.end.new_note_hashes.storage[i].value = unique_note_hash;
}
}
}

fn propagate_rollup_validation_requests(&mut self) {
self.public_inputs.validation_requests.max_block_number = self.previous_kernel.public_inputs.validation_requests.for_rollup.max_block_number;
}

fn propagate_constant_data(&mut self) {
self.public_inputs.constants = self.previous_kernel.public_inputs.constants;
}

fn propagate_sorted_arrays(&mut self) {
let accumulated_data = self.previous_kernel.public_inputs.end;

assert_sorted_array(
accumulated_data.new_note_hashes,
self.sorted_note_hashes,
self.sorted_note_hashes_indexes,
asc_sort_by_counters
);
self.public_inputs.end.new_note_hashes = array_to_bounded_vec(self.sorted_note_hashes);

assert_sorted_array(
accumulated_data.new_nullifiers,
self.sorted_nullifiers,
self.sorted_nullifiers_indexes,
asc_sort_by_counters
);
self.public_inputs.end.new_nullifiers = array_to_bounded_vec(self.sorted_nullifiers);

// TODO: Sort all the side effects below.
self.public_inputs.end.new_l2_to_l1_msgs = array_to_bounded_vec(accumulated_data.new_l2_to_l1_msgs);
self.public_inputs.end.encrypted_logs_hash = accumulated_data.encrypted_logs_hash;
self.public_inputs.end.unencrypted_logs_hash = accumulated_data.unencrypted_logs_hash;
self.public_inputs.end.encrypted_log_preimages_length = accumulated_data.encrypted_log_preimages_length;
self.public_inputs.end.unencrypted_log_preimages_length = accumulated_data.unencrypted_log_preimages_length;
}

fn propagate_sorted_public_call_requests(&mut self) {
let accumulated_data = self.previous_kernel.public_inputs.end;
self.public_inputs.end.public_call_stack = array_to_bounded_vec(accumulated_data.public_call_stack);
}

fn squash_transient_note_hashes_and_nullifiers(&mut self) {
// Remark: The commitments in public_inputs.end have already been siloed by contract address!
// Match nullifiers/nullified_commitments to commitments from the previous call(s)
let mut new_note_hashes = self.public_inputs.end.new_note_hashes.storage;
let mut new_nullifiers = self.public_inputs.end.new_nullifiers.storage;

// Should start from 1 to skip the first nullifier?
for n_idx in 0..MAX_NEW_NULLIFIERS_PER_TX {
let nullifier = new_nullifiers[n_idx];
// TODO - should not be able to squash the first nullifier.
let nullified_note_hash = nullifier.note_hash;
let hint_pos = self.transient_note_hash_index_hints[n_idx];

// Nullified_commitment of value `0` implies non-transient (persistable)
// nullifier in which case no attempt will be made to match it to a hash.
// Non-empty nullified_note_hash implies transient nullifier which MUST be matched to a hash below!
// 0-valued nullified_note_hash is empty and will be ignored
if nullified_note_hash != 0 {
assert(
hint_pos < MAX_NEW_NOTE_HASHES_PER_TX, "New nullifier is transient but hint is invalid"
);
let hash = new_note_hashes[hint_pos];
assert_eq(nullified_note_hash, hash.value, "Hinted hash does not match");
assert(
nullifier.counter > hash.counter, "Nullifier counter must be greater than hash counter"
);
// match found!
// squash both the nullifier and the hash
// (set to 0 here and then rearrange array after loop)
new_note_hashes[hint_pos] = SideEffect::empty();
new_nullifiers[n_idx] = SideEffectLinkedToNoteHash::empty();
}
// non-transient (persistable) nullifiers are just kept in new_nullifiers array and forwarded
// to public inputs (used later by base rollup circuit)
}

// Move all zero-ed (removed) entries of these arrays to the end and preserve ordering of other entries

let mut new_note_hashes_vec = BoundedVec::new();

for c_idx in 0..MAX_NEW_NOTE_HASHES_PER_TX {
if new_note_hashes[c_idx].value != 0 {
new_note_hashes_vec.push(new_note_hashes[c_idx]);
}
}

self.public_inputs.end.new_note_hashes = new_note_hashes_vec;

let mut new_nullifiers_vec = BoundedVec::new();

for n_idx in 0..MAX_NEW_NULLIFIERS_PER_TX {
if new_nullifiers[n_idx].value != 0 {
new_nullifiers_vec.push(new_nullifiers[n_idx]);
}
}

self.public_inputs.end.new_nullifiers = new_nullifiers_vec;
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
mod kernel_circuit_public_inputs_composer;
mod private_kernel_init;
mod private_kernel_inner;
mod private_kernel_tail;
mod private_kernel_tail_to_public;

use private_kernel_init::PrivateKernelInitCircuitPrivateInputs;
use private_kernel_inner::PrivateKernelInnerCircuitPrivateInputs;
use private_kernel_tail::PrivateKernelTailCircuitPrivateInputs;
use private_kernel_tail_to_public::PrivateKernelTailToPublicCircuitPrivateInputs;

// TODO: rename to be precise as to what its common to.
mod common;
Loading
Loading