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(circuits): hints nullifier transient commitments #2056

Merged
merged 5 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from 4 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
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "private_call_data.hpp"
#include "../previous_kernel_data.hpp"

#include "aztec3/constants.hpp"
#include "aztec3/utils/types/circuit_types.hpp"
#include "aztec3/utils/types/native_types.hpp"

Expand All @@ -20,10 +21,11 @@ template <typename NCT> struct PrivateKernelInputsOrdering {

PreviousKernelData<NCT> previous_kernel{};

std::array<fr, MAX_READ_REQUESTS_PER_TX> hint_to_commitments{};
std::array<fr, MAX_READ_REQUESTS_PER_TX> read_commitment_hints{};
std::array<fr, MAX_NEW_NULLIFIERS_PER_TX> nullifier_commitment_hints{};

// For serialization, update with new fields
MSGPACK_FIELDS(previous_kernel, hint_to_commitments);
MSGPACK_FIELDS(previous_kernel, read_commitment_hints, nullifier_commitment_hints);
boolean operator==(PrivateKernelInputsOrdering<NCT> const& other) const
{
return msgpack_derived_equals<boolean>(*this, other);
Expand All @@ -36,7 +38,8 @@ template <typename NCT> struct PrivateKernelInputsOrdering {

PrivateKernelInputsOrdering<CircuitTypes<Builder>> private_inputs = {
previous_kernel.to_circuit_type(builder),
hint_to_commitments.to_circuit_type(builder),
read_commitment_hints.to_circuit_type(builder),
nullifier_commitment_hints.to_circuit_type(builder),
};

return private_inputs;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include "aztec3/circuits/abis/private_kernel/private_kernel_inputs_ordering.hpp"
#include "aztec3/circuits/abis/read_request_membership_witness.hpp"
#include "aztec3/circuits/apps/test_apps/escrow/deposit.hpp"
#include "aztec3/circuits/hash.hpp"
#include "aztec3/circuits/kernel/private/common.hpp"
#include "aztec3/circuits/kernel/private/init.hpp"
#include "aztec3/constants.hpp"
Expand Down Expand Up @@ -59,8 +58,8 @@ TEST_F(native_private_kernel_tests, native_accumulate_transient_read_requests)
private_inputs_init.private_call.call_stack_item.public_inputs.read_requests[0] = fr(23);
private_inputs_init.private_call.read_request_membership_witnesses[0].is_transient = true;

std::array<fr, MAX_READ_REQUESTS_PER_TX> hint_to_commitments{};
hint_to_commitments[0] = fr(1);
std::array<fr, MAX_READ_REQUESTS_PER_TX> read_commitment_hints{};
read_commitment_hints[0] = fr(1);

DummyBuilder builder = DummyBuilder("native_private_kernel_tests__native_accumulate_transient_read_requests");
auto public_inputs = native_private_kernel_circuit_initial(builder, private_inputs_init);
Expand All @@ -76,7 +75,7 @@ TEST_F(native_private_kernel_tests, native_accumulate_transient_read_requests)
private_inputs_inner.private_call.call_stack_item.public_inputs.read_requests[0] = fr(12);
private_inputs_inner.private_call.read_request_membership_witnesses[0].is_transient = true;

hint_to_commitments[1] = fr(0);
read_commitment_hints[1] = fr(0);

// We need to update the previous_kernel's private_call_stack because the current_call_stack_item has changed
// i.e. we changed the new_commitments and read_requests of the current_call_stack_item's public_inputs
Expand All @@ -97,7 +96,7 @@ TEST_F(native_private_kernel_tests, native_accumulate_transient_read_requests)
auto& previous_kernel = private_inputs_inner.previous_kernel;
previous_kernel.public_inputs = public_inputs;

PrivateKernelInputsOrdering<NT> private_inputs{ previous_kernel, hint_to_commitments };
PrivateKernelInputsOrdering<NT> private_inputs{ previous_kernel, read_commitment_hints };
auto final_public_inputs = native_private_kernel_circuit_ordering(builder, private_inputs);

ASSERT_FALSE(builder.failed()) << "failure: " << builder.get_first_failure()
Expand All @@ -116,8 +115,8 @@ TEST_F(native_private_kernel_tests, native_transient_read_requests_no_match)
private_inputs_init.private_call.call_stack_item.public_inputs.read_requests[0] = fr(23);
private_inputs_init.private_call.read_request_membership_witnesses[0].is_transient = true;

std::array<fr, MAX_READ_REQUESTS_PER_TX> hint_to_commitments{};
hint_to_commitments[0] = fr(1);
std::array<fr, MAX_READ_REQUESTS_PER_TX> read_commitment_hints{};
read_commitment_hints[0] = fr(1);

DummyBuilder builder = DummyBuilder("native_private_kernel_tests__native_transient_read_requests_no_match");
auto public_inputs = native_private_kernel_circuit_initial(builder, private_inputs_init);
Expand All @@ -133,7 +132,7 @@ TEST_F(native_private_kernel_tests, native_transient_read_requests_no_match)
private_inputs_inner.private_call.call_stack_item.public_inputs.read_requests[0] = fr(12);
private_inputs_inner.private_call.read_request_membership_witnesses[0].is_transient = true;

hint_to_commitments[1] = fr(0); // There is not correct possible value.
read_commitment_hints[1] = fr(0); // There is not correct possible value.

// We need to update the previous_kernel's private_call_stack because the current_call_stack_item has changed
// i.e. we changed the new_commitments and read_requests of the current_call_stack_item's public_inputs
Expand All @@ -154,7 +153,7 @@ TEST_F(native_private_kernel_tests, native_transient_read_requests_no_match)
auto& previous_kernel = private_inputs_inner.previous_kernel;
previous_kernel.public_inputs = public_inputs;

PrivateKernelInputsOrdering<NT> private_inputs{ previous_kernel, hint_to_commitments };
PrivateKernelInputsOrdering<NT> private_inputs{ previous_kernel, read_commitment_hints };
auto final_public_inputs = native_private_kernel_circuit_ordering(builder, private_inputs);

ASSERT_TRUE(builder.failed());
Expand All @@ -176,7 +175,7 @@ TEST_F(native_private_kernel_tests, native_empty_nullified_commitment_respected)

private_inputs_inner.private_call.call_stack_item.public_inputs.nullified_commitments[0] =
fr(EMPTY_NULLIFIED_COMMITMENT);
private_inputs_inner.private_call.call_stack_item.public_inputs.nullified_commitments[1] = fr(23);
private_inputs_inner.private_call.call_stack_item.public_inputs.nullified_commitments[1] = fr(33);

// update the private call stack contents to reflect the above changes which affect the item hash
private_inputs_inner.previous_kernel.public_inputs.end.private_call_stack[0] =
Expand All @@ -203,7 +202,9 @@ TEST_F(native_private_kernel_tests, native_empty_nullified_commitment_respected)
auto& previous_kernel = private_inputs_inner.previous_kernel;
previous_kernel.public_inputs = public_inputs;

PrivateKernelInputsOrdering<NT> private_inputs{ .previous_kernel = previous_kernel };
PrivateKernelInputsOrdering<NT> private_inputs{ .previous_kernel = previous_kernel,
.nullifier_commitment_hints =
std::array<fr, MAX_NEW_NULLIFIERS_PER_TX>{ 0, 1 } };

auto final_public_inputs = native_private_kernel_circuit_ordering(builder, private_inputs);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ namespace aztec3::circuits::kernel::private_kernel {

void match_reads_to_commitments(DummyCircuitBuilder& builder,
std::array<NT::fr, MAX_READ_REQUESTS_PER_TX> const& read_requests,
std::array<NT::fr, MAX_READ_REQUESTS_PER_TX> const& hint_to_commitments,
std::array<NT::fr, MAX_READ_REQUESTS_PER_TX> const& read_commitment_hints,
std::array<NT::fr, MAX_NEW_COMMITMENTS_PER_TX> const& new_commitments)
{
// match reads to commitments from the previous call(s)
for (size_t rr_idx = 0; rr_idx < MAX_READ_REQUESTS_PER_TX; rr_idx++) {
const auto& read_request = read_requests[rr_idx];
const auto& hint_to_commitment = hint_to_commitments[rr_idx];
const auto hint_pos = static_cast<size_t>(uint64_t(hint_to_commitment));
const auto& read_commitment_hint = read_commitment_hints[rr_idx];
const auto hint_pos = static_cast<size_t>(uint64_t(read_commitment_hint));

if (read_request != 0) {
size_t match_pos = MAX_NEW_COMMITMENTS_PER_TX;
Expand All @@ -59,7 +59,7 @@ void match_reads_to_commitments(DummyCircuitBuilder& builder,
"\n\tread_request: ",
read_request,
"\n\thint_to_commitment: ",
hint_to_commitment,
read_commitment_hint,
"\n\t* the read_request position/index is not expected to match position in app-circuit "
"outputs because kernel iterations gradually remove non-transient read_requests as "
"membership checks are resolved."),
Expand Down Expand Up @@ -92,22 +92,24 @@ void match_nullifiers_to_commitments_and_squash(
DummyCircuitBuilder& builder,
std::array<NT::fr, MAX_NEW_NULLIFIERS_PER_TX>& new_nullifiers,
std::array<NT::fr, MAX_NEW_NULLIFIERS_PER_TX> const& nullified_commitments,
std::array<NT::fr, MAX_NEW_NULLIFIERS_PER_TX> const& nullifier_commitment_hints,
std::array<NT::fr, MAX_NEW_COMMITMENTS_PER_TX>& new_commitments)
{
// match reads to commitments from the previous call(s)
// match nullifiers/nullified_commitments to commitments from the previous call(s)
for (size_t n_idx = 0; n_idx < MAX_NEW_NULLIFIERS_PER_TX; n_idx++) {
const auto& nullified_commitment = nullified_commitments[n_idx];
const auto& nullifier_commitment_hint = nullifier_commitment_hints[n_idx];
const auto hint_pos = static_cast<size_t>(uint64_t(nullifier_commitment_hint));
// Nullified_commitment of value `EMPTY_NULLIFIED_COMMITMENT` implies non-transient (persistable)
// nullifier in which case no attempt will be made to match it to a commitment.
// Non-empty nullified_commitment implies transient nullifier which MUST be matched to a commitment below!
// 0-valued nullified_commitment is empty and will be ignored
if (nullified_commitments[n_idx] != NT::fr(0) &&
nullified_commitments[n_idx] != NT::fr(EMPTY_NULLIFIED_COMMITMENT)) {
size_t match_pos = MAX_NEW_COMMITMENTS_PER_TX;
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/837): inefficient
// O(n^2) inner loop will be optimized via matching hints
for (size_t c_idx = 0; c_idx < MAX_NEW_COMMITMENTS_PER_TX; c_idx++) {
// If there are multiple matches, this picks the last one
match_pos = (nullified_commitments[n_idx] == new_commitments[c_idx]) ? c_idx : match_pos;

if (hint_pos < MAX_NEW_COMMITMENTS_PER_TX) {
match_pos = nullified_commitment == new_commitments[hint_pos] ? hint_pos : match_pos;
}

if (match_pos != MAX_NEW_COMMITMENTS_PER_TX) {
Expand Down Expand Up @@ -169,7 +171,7 @@ KernelCircuitPublicInputsFinal<NT> native_private_kernel_circuit_ordering(
// Remark: The commitments in public_inputs.end have already been siloed by contract address!
match_reads_to_commitments(builder,
private_inputs.previous_kernel.public_inputs.end.read_requests,
private_inputs.hint_to_commitments,
private_inputs.read_commitment_hints,
private_inputs.previous_kernel.public_inputs.end.new_commitments);

// Matching nullifiers to pending commitments requires the full list of new commitments accumulated over
Expand All @@ -180,6 +182,7 @@ KernelCircuitPublicInputsFinal<NT> native_private_kernel_circuit_ordering(
match_nullifiers_to_commitments_and_squash(builder,
public_inputs.end.new_nullifiers,
public_inputs.end.nullified_commitments,
private_inputs.nullifier_commitment_hints,
public_inputs.end.new_commitments);

// tx hash
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ TEST_F(native_private_kernel_ordering_tests, native_squash_one_of_one_transient_
previous_kernel.public_inputs.end.new_nullifiers = new_nullifiers;
previous_kernel.public_inputs.end.nullified_commitments = nullifier_commitments;

// Correct nullifier_commitment hint for new_nullifiers[0] == 0 is correct due to the default
// initialization of the array.
PrivateKernelInputsOrdering<NT> private_inputs{ .previous_kernel = previous_kernel };

DummyBuilder builder =
Expand Down Expand Up @@ -254,7 +256,9 @@ TEST_F(native_private_kernel_ordering_tests, native_squash_one_of_two_transient_
previous_kernel.public_inputs.end.new_nullifiers = new_nullifiers;
previous_kernel.public_inputs.end.nullified_commitments = nullifier_commitments;

PrivateKernelInputsOrdering<NT> private_inputs{ .previous_kernel = previous_kernel };
PrivateKernelInputsOrdering<NT> private_inputs{ .previous_kernel = previous_kernel,
.nullifier_commitment_hints =
std::array<fr, MAX_NEW_NULLIFIERS_PER_TX>{ 1 } };

DummyBuilder builder =
DummyBuilder("native_private_kernel_ordering_tests__native_squash_one_of_two_transient_matches_works");
Expand Down Expand Up @@ -289,7 +293,9 @@ TEST_F(native_private_kernel_ordering_tests, native_squash_two_of_two_transient_
previous_kernel.public_inputs.end.new_nullifiers = new_nullifiers;
previous_kernel.public_inputs.end.nullified_commitments = nullifier_commitments;

PrivateKernelInputsOrdering<NT> private_inputs{ .previous_kernel = previous_kernel };
PrivateKernelInputsOrdering<NT> private_inputs{ .previous_kernel = previous_kernel,
.nullifier_commitment_hints =
std::array<fr, MAX_NEW_NULLIFIERS_PER_TX>{ 1 } };

DummyBuilder builder =
DummyBuilder("native_private_kernel_ordering_tests__native_squash_two_of_two_transient_matches_works");
Expand Down
39 changes: 37 additions & 2 deletions yarn-project/aztec-rpc/src/kernel_prover/kernel_prover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
CONTRACT_TREE_HEIGHT,
Fr,
MAX_NEW_COMMITMENTS_PER_TX,
MAX_NEW_NULLIFIERS_PER_TX,
MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL,
MAX_READ_REQUESTS_PER_CALL,
MAX_READ_REQUESTS_PER_TX,
Expand All @@ -22,6 +23,7 @@ import {
makeEmptyProof,
makeTuple,
} from '@aztec/circuits.js';
import { EMPTY_NULLIFIED_COMMITMENT } from '@aztec/circuits.js';
import { Tuple, assertLength } from '@aztec/foundation/serialize';

import { KernelProofCreator, ProofCreator, ProofOutput, ProofOutputFinal } from './proof_creator.js';
Expand Down Expand Up @@ -171,11 +173,21 @@ export class KernelProver {
assertLength<Fr, typeof VK_TREE_HEIGHT>(previousVkMembershipWitness.siblingPath, VK_TREE_HEIGHT),
);

const hintToCommitments = this.getReadRequestHints(
const readCommitmentHints = this.getReadRequestHints(
output.publicInputs.end.readRequests,
output.publicInputs.end.newCommitments,
);
const privateInputs = new PrivateKernelInputsOrdering(previousKernelData, hintToCommitments);

const nullifierCommitmentHints = this.getNullifierHints(
output.publicInputs.end.nullifiedCommitments,
output.publicInputs.end.newCommitments,
);

const privateInputs = new PrivateKernelInputsOrdering(
previousKernelData,
readCommitmentHints,
nullifierCommitmentHints,
);
const outputFinal = await this.proofCreator.createProofOrdering(privateInputs);

// Only return the notes whose commitment is in the commitments of the final proof.
Expand Down Expand Up @@ -264,4 +276,27 @@ export class KernelProver {
}
return hints;
}

private getNullifierHints(
nullifiedCommitments: Tuple<Fr, typeof MAX_NEW_NULLIFIERS_PER_TX>,
commitments: Tuple<Fr, typeof MAX_NEW_COMMITMENTS_PER_TX>,
): Tuple<Fr, typeof MAX_NEW_NULLIFIERS_PER_TX> {
jeanmon marked this conversation as resolved.
Show resolved Hide resolved
const hints = makeTuple(MAX_NEW_NULLIFIERS_PER_TX, Fr.zero);
for (let i = 0; i < MAX_NEW_NULLIFIERS_PER_TX; i++) {
if (!nullifiedCommitments[i].isZero() && !nullifiedCommitments[i].equals(new Fr(EMPTY_NULLIFIED_COMMITMENT))) {
const equalToCommitment = (cmt: Fr) => cmt.equals(nullifiedCommitments[i]);
const result = commitments.findIndex(equalToCommitment);
if (result == -1) {
throw new Error(
`The nullified commitment at index ${i} with value ${nullifiedCommitments[
i
].toString()} does not match to any commitment.`,
);
} else {
hints[i] = new Fr(result);
}
}
}
return hints;
}
}
23 changes: 16 additions & 7 deletions yarn-project/circuits.js/src/cbind/circuits.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1675,32 +1675,41 @@ export function fromPrivateKernelInputsInner(o: PrivateKernelInputsInner): Msgpa

interface MsgpackPrivateKernelInputsOrdering {
previous_kernel: MsgpackPreviousKernelData;
hint_to_commitments: Tuple<Buffer, 16>;
read_commitment_hints: Tuple<Buffer, 16>;
nullifier_commitment_hints: Tuple<Buffer, 16>;
}

export function toPrivateKernelInputsOrdering(o: MsgpackPrivateKernelInputsOrdering): PrivateKernelInputsOrdering {
if (o.previous_kernel === undefined) {
throw new Error('Expected previous_kernel in PrivateKernelInputsOrdering deserialization');
}
if (o.hint_to_commitments === undefined) {
throw new Error('Expected hint_to_commitments in PrivateKernelInputsOrdering deserialization');
if (o.read_commitment_hints === undefined) {
throw new Error('Expected read_commitment_hints in PrivateKernelInputsOrdering deserialization');
}
if (o.nullifier_commitment_hints === undefined) {
throw new Error('Expected nullifier_commitment_hints in PrivateKernelInputsOrdering deserialization');
}
return new PrivateKernelInputsOrdering(
toPreviousKernelData(o.previous_kernel),
mapTuple(o.hint_to_commitments, (v: Buffer) => Fr.fromBuffer(v)),
mapTuple(o.read_commitment_hints, (v: Buffer) => Fr.fromBuffer(v)),
mapTuple(o.nullifier_commitment_hints, (v: Buffer) => Fr.fromBuffer(v)),
);
}

export function fromPrivateKernelInputsOrdering(o: PrivateKernelInputsOrdering): MsgpackPrivateKernelInputsOrdering {
if (o.previousKernel === undefined) {
throw new Error('Expected previousKernel in PrivateKernelInputsOrdering serialization');
}
if (o.hintToCommitments === undefined) {
throw new Error('Expected hintToCommitments in PrivateKernelInputsOrdering serialization');
if (o.readCommitmentHints === undefined) {
throw new Error('Expected readCommitmentHints in PrivateKernelInputsOrdering serialization');
}
if (o.nullifierCommitmentHints === undefined) {
throw new Error('Expected nullifierCommitmentHints in PrivateKernelInputsOrdering serialization');
}
return {
previous_kernel: fromPreviousKernelData(o.previousKernel),
hint_to_commitments: mapTuple(o.hintToCommitments, (v: Fr) => toBuffer(v)),
read_commitment_hints: mapTuple(o.readCommitmentHints, (v: Fr) => toBuffer(v)),
nullifier_commitment_hints: mapTuple(o.nullifierCommitmentHints, (v: Fr) => toBuffer(v)),
};
}

Expand Down
9 changes: 7 additions & 2 deletions yarn-project/circuits.js/src/structs/kernel/private_kernel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Tuple } from '@aztec/foundation/serialize';
import {
CONTRACT_TREE_HEIGHT,
FUNCTION_TREE_HEIGHT,
MAX_NEW_NULLIFIERS_PER_TX,
MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL,
MAX_READ_REQUESTS_PER_CALL,
MAX_READ_REQUESTS_PER_TX,
Expand Down Expand Up @@ -159,14 +160,18 @@ export class PrivateKernelInputsOrdering {
/**
* Contains hints for the transient read requests to localize corresponding commitments.
*/
public hintToCommitments: Tuple<Fr, typeof MAX_READ_REQUESTS_PER_TX>,
public readCommitmentHints: Tuple<Fr, typeof MAX_READ_REQUESTS_PER_TX>,
/**
* Contains hints for the transient nullifiers to localize corresponding commitments.
*/
public nullifierCommitmentHints: Tuple<Fr, typeof MAX_NEW_NULLIFIERS_PER_TX>,
) {}

/**
* Serialize this as a buffer.
* @returns The buffer.
*/
toBuffer() {
return serializeToBuffer(this.previousKernel, this.hintToCommitments);
return serializeToBuffer(this.previousKernel, this.readCommitmentHints);
}
}