Skip to content

Commit

Permalink
feat: public inputs refactor (#5500)
Browse files Browse the repository at this point in the history
## Changes to `PublicInputs`

### ✅  New Flow

```
PrivateInit
     |  PrivateKernelCircuitPublicInputs
PrivateInner
     |  PrivateKernelCircuitPublicInputs
PrivateTail
     |  KernelCircuitPublicInputs
BaseRollup
```

If the tx contains public calls:

```
PrivateInit
     |  PrivateKernelCircuitPublicInputs
PrivateInner
     |  PrivateKernelCircuitPublicInputs
PrivateTailToPublic
     |  PublicKernelCircuitPublicInputs
PublicSetup
     |  PublicKernelCircuitPublicInputs
PublicAppLogic
     |  PublicKernelCircuitPublicInputs
PublicTeardown
     |  PublicKernelCircuitPublicInputs
PublicTail
     |  KernelCircuitPublicInputs
BaseRollup
```

A new circuit `PrivateTailToPublic` is introduced, which is similar to
`PrivateTail`, but it also processes data relevant to the public kernel,
such as public call requests, and outputs `PublicCircuitPublicInputs`.

### ⛔  Previous Flow

Previously, public inputs had to be converted in typescript before
feeding to the `PublicSetup` and `BaseRollup` circuits. And the circuits
not only had to convert the public inputs back to the correct format
according to the type of the previous proof, they also had to
conditionally check the data. For example, if the previous proof is
`PrivateTail`, `BaseRollup` had to assert that all public update
requests in the given `RollupKernelCircuitPublicInputs` are empty.

```
PrivateInit
     |  PrivateKernelInnerCircuitPublicInputs
PrivateInner
     |  PrivateKernelInnerCircuitPublicInputs
PrivateTail
     |  PrivateKernelTailCircuitPublicInputs (converted to RollupKernelCircuitPublicInputs in typescript)
BaseRollup (converted back to PrivateKernelTailCircuitPublicInputs in noir)
```

If the tx contains public calls:

```
PrivateInit
     |  PrivateKernelInnerCircuitPublicInputs
PrivateInner
     |  PrivateKernelInnerCircuitPublicInputs
PrivateTail
     |  PrivateKernelTailCircuitPublicInputs (converted to PublicKernelCircuitPublicInputs in typescript)
PublicSetup (converted back to PrivateKernelTailCircuitPublicInputs in noir)
     |  PublicKernelCircuitPublicInputs
PublicAppLogic
     |  PublicKernelCircuitPublicInputs
PublicTeardown
     |  PublicKernelCircuitPublicInputs
PublicTail
     |  PublicKernelCircuitPublicInputs (converted to RollupKernelCircuitPublicInputs in typescript)
BaseRollup (converted back to PublicKernelCircuitPublicInputs in noir)
```

## Changes to `AccumulatedData`

### ✅  New Format

- PrivateKernelCircuitPublicInputs.end: `PrivateAccumulatedData`
- PublicKernelCircuitPublicInputs.end: `PublicAccumulatedData`
- PublicKernelCircuitPublicInputs.end_non_revertible:
`PublicAccumulatedData`
- KernelCircuitPublicInputs.end: `AccumulatedData`

### ⛔  Previous Format

- PrivateKernelInnerCircuitPublicInputs.end: `CombinedAccumulatedData`
- PrivateKernelTailCircuitPublicInputs.end:
`PrivateAccumulatedRevertibleData`
- PrivateKernelTailCircuitPublicInputs.end_non_revertible:
`PrivateAccumulatedNonRevertibleData`
- PublicKernelCircuitPublicInputs.end: `PublicAccumulatedRevertibleData`
- PublicKernelCircuitPublicInputs.end_non_revertible:
`PublicAccumulatedNonRevertibleData`
- RollupKernelCircuitPublicInputs.end: `CombinedAccumulatedData`
  • Loading branch information
LeilaWang authored Apr 3, 2024
1 parent 0249737 commit 6b9a538
Show file tree
Hide file tree
Showing 123 changed files with 4,201 additions and 4,887 deletions.
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

0 comments on commit 6b9a538

Please sign in to comment.