Skip to content

Commit

Permalink
feat(simulator): simulator allows noir to read pending commitments (#926
Browse files Browse the repository at this point in the history
)

* ACIR simulator tracks pending notes and allows Noir contracts to "get" them
* Kernel rejects all transient reads for now (until read<>commitment matching logic is there)
* New test contract added along with ACIR simulator tests and e2e tests for pending commitments
* New type ReadRequestMembershipWitness added which is a normal membership witness with a "kernel hint"
* Temporary hack in Noir to force strict dependency and ordering between notifyCreatedNote and subsequent getNotes oracles
  • Loading branch information
dbanks12 authored Jul 12, 2023
1 parent 4125abe commit 322e9a6
Show file tree
Hide file tree
Showing 43 changed files with 1,532 additions and 138 deletions.
14 changes: 14 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,18 @@ jobs:
name: "Test"
command: cond_spot_run_tests end-to-end e2e_account_contract.test.ts

e2e-pending-commitments-contract:
docker:
- image: aztecprotocol/alpine-build-image
resource_class: small
steps:
- *checkout
- *setup_env
- run:
name: "Test"
command: cond_spot_run_tests end-to-end e2e_pending_commitments_contract.test.ts


uniswap-trade-on-l1-from-l2:
docker:
- image: aztecprotocol/alpine-build-image
Expand Down Expand Up @@ -795,6 +807,7 @@ workflows:
- e2e-public-cross-chain-messaging: *e2e_test
- e2e-public-to-private-messaging: *e2e_test
- e2e-account-contract: *e2e_test
- e2e-pending-commitments-contract: *e2e_test
- uniswap-trade-on-l1-from-l2: *e2e_test
- integration-l1-publisher: *e2e_test
- integration-archiver-l1-to-l2: *e2e_test
Expand All @@ -811,6 +824,7 @@ workflows:
- e2e-public-cross-chain-messaging
- e2e-public-to-private-messaging
- e2e-account-contract
- e2e-pending-commitments-contract
- uniswap-trade-on-l1-from-l2
- integration-l1-publisher
- integration-archiver-l1-to-l2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ template <typename NCT, unsigned int N> struct MembershipWitness {
using fr = typename NCT::fr;
using boolean = typename NCT::boolean;

fr leaf_index;
fr leaf_index = 0;
std::array<fr, N> sibling_path{};

MSGPACK_FIELDS(leaf_index, sibling_path);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#include "call_context_reconciliation_data.hpp"
#include "../call_stack_item.hpp"
#include "../membership_witness.hpp"
#include "../read_request_membership_witness.hpp"
#include "../types.hpp"

#include "aztec3/constants.hpp"
Expand Down Expand Up @@ -37,7 +37,7 @@ template <typename NCT> struct PrivateCallData {
MembershipWitness<NCT, FUNCTION_TREE_HEIGHT> function_leaf_membership_witness{};
MembershipWitness<NCT, CONTRACT_TREE_HEIGHT> contract_leaf_membership_witness{};

std::array<MembershipWitness<NCT, PRIVATE_DATA_TREE_HEIGHT>, READ_REQUESTS_LENGTH>
std::array<ReadRequestMembershipWitness<NCT, PRIVATE_DATA_TREE_HEIGHT>, READ_REQUESTS_LENGTH>
read_request_membership_witnesses{};

fr portal_contract_address = 0; // an ETH address
Expand Down Expand Up @@ -77,7 +77,7 @@ template <typename NCT> struct PrivateCallData {
to_circuit_type(function_leaf_membership_witness),
to_circuit_type(contract_leaf_membership_witness),

aztec3::utils::types::to_ct<Builder, MembershipWitness<CT, PRIVATE_DATA_TREE_HEIGHT>>(
aztec3::utils::types::to_ct<Builder, ReadRequestMembershipWitness<CT, PRIVATE_DATA_TREE_HEIGHT>>(
builder, read_request_membership_witnesses),

to_ct(portal_contract_address),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#pragma once

#include "aztec3/circuits/abis/membership_witness.hpp"
#include "aztec3/utils/types/circuit_types.hpp"
#include "aztec3/utils/types/convert.hpp"

namespace aztec3::circuits::abis {

using aztec3::utils::types::CircuitTypes;
using aztec3::utils::types::NativeTypes;
using std::is_same;

/**
* A ReadRequestMembershipWitness is similar to a MembershipWitness but includes
* some additional fields used to direct the kernel regarding whether a read is transient
* and if so which commitment it corresponds to.
*/
template <typename NCT, unsigned int N> struct ReadRequestMembershipWitness {
using fr = typename NCT::fr;
using boolean = typename NCT::boolean;

fr leaf_index = 0;
std::array<fr, N> sibling_path{};
boolean is_transient = false; // whether or not the read request corresponds to a pending commitment
fr hint_to_commitment = 0; // hint to point kernel to the commitment this rr corresponds to

MSGPACK_FIELDS(leaf_index, sibling_path, is_transient, hint_to_commitment);

boolean operator==(ReadRequestMembershipWitness<NCT, N> const& other) const
{
return leaf_index == other.leaf_index && sibling_path == other.sibling_path &&
is_transient == other.is_transient && hint_to_commitment == other.hint_to_commitment;
};

template <typename Builder>
ReadRequestMembershipWitness<CircuitTypes<Builder>, N> to_circuit_type(Builder& builder) const
{
static_assert((std::is_same<NativeTypes, NCT>::value));

// Capture the circuit builder:
auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); };

ReadRequestMembershipWitness<CircuitTypes<Builder>, N> witness = {
to_ct(leaf_index), to_ct(sibling_path), to_ct(is_transient), to_ct(hint_to_commitment)
};

return witness;
}
};

template <typename NCT, unsigned int N> void read(uint8_t const*& it, ReadRequestMembershipWitness<NCT, N>& obj)
{
using serialize::read;

read(it, obj.leaf_index);
read(it, obj.sibling_path);
read(it, obj.is_transient);
read(it, obj.hint_to_commitment);
};

template <typename NCT, unsigned int N>
void write(std::vector<uint8_t>& buf, ReadRequestMembershipWitness<NCT, N> const& obj)
{
using serialize::write;

write(buf, obj.leaf_index);
write(buf, obj.sibling_path);
write(buf, obj.is_transient);
write(buf, obj.hint_to_commitment);
};

template <typename NCT, unsigned int N>
std::ostream& operator<<(std::ostream& os, ReadRequestMembershipWitness<NCT, N> const& obj)
{
return os << "leaf_index: " << obj.leaf_index << "\n"
<< "sibling_path: " << obj.sibling_path << "\n"
<< "is_transient: " << obj.is_transient << "\n"
<< "hint_to_commitment_index: " << obj.hint_to_commitment << "\n";
}

} // namespace aztec3::circuits::abis
27 changes: 20 additions & 7 deletions circuits/cpp/src/aztec3/circuits/kernel/private/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
#include "aztec3/circuits/abis/contract_deployment_data.hpp"
#include "aztec3/circuits/abis/function_data.hpp"
#include "aztec3/circuits/abis/kernel_circuit_public_inputs.hpp"
#include "aztec3/circuits/abis/membership_witness.hpp"
#include "aztec3/circuits/abis/new_contract_data.hpp"
#include "aztec3/circuits/abis/previous_kernel_data.hpp"
#include "aztec3/circuits/abis/private_kernel/private_call_data.hpp"
#include "aztec3/circuits/abis/read_request_membership_witness.hpp"
#include "aztec3/circuits/hash.hpp"
#include "aztec3/constants.hpp"
#include "aztec3/utils/array.hpp"
Expand All @@ -20,9 +20,9 @@ using aztec3::circuits::abis::ContractDeploymentData;
using aztec3::circuits::abis::ContractLeafPreimage;
using aztec3::circuits::abis::FunctionData;
using aztec3::circuits::abis::KernelCircuitPublicInputs;
using aztec3::circuits::abis::MembershipWitness;
using aztec3::circuits::abis::NewContractData;
using aztec3::circuits::abis::PreviousKernelData;
using aztec3::circuits::abis::ReadRequestMembershipWitness;

using aztec3::utils::array_push;
using aztec3::utils::is_array_empty;
Expand Down Expand Up @@ -69,15 +69,14 @@ void common_validate_call_stack(DummyBuilder& builder, PrivateCallData<NT> const
void common_validate_read_requests(DummyBuilder& builder,
NT::fr const& storage_contract_address,
std::array<fr, READ_REQUESTS_LENGTH> const& read_requests,
std::array<MembershipWitness<NT, PRIVATE_DATA_TREE_HEIGHT>,
std::array<ReadRequestMembershipWitness<NT, PRIVATE_DATA_TREE_HEIGHT>,
READ_REQUESTS_LENGTH> const& read_request_membership_witnesses,
NT::fr const& historic_private_data_tree_root)
{
// membership witnesses must resolve to the same private data root
// for every request in all kernel iterations
for (size_t rr_idx = 0; rr_idx < aztec3::READ_REQUESTS_LENGTH; rr_idx++) {
const auto& read_request = read_requests[rr_idx];

// the read request comes un-siloed from the app circuit so we must silo it here
// so that it matches the private data tree leaf that we are membership checking
const auto leaf = silo_commitment<NT>(storage_contract_address, read_request);
Expand All @@ -88,9 +87,7 @@ void common_validate_read_requests(DummyBuilder& builder,
// We determine if it is a transient read depending on the leaf index from the membership witness
// Note that the Merkle membership proof would be null and void in case of an transient read
// but we use the leaf index as a placeholder to detect a transient read.
const auto is_transient_read = (witness.leaf_index == NT::fr(-1));

if (read_request != 0 && !is_transient_read) {
if (read_request != 0 && !witness.is_transient) {
const auto& root_for_read_request =
root_from_sibling_path<NT>(leaf, witness.leaf_index, witness.sibling_path);
builder.do_assert(
Expand All @@ -110,6 +107,22 @@ void common_validate_read_requests(DummyBuilder& builder,
"and merkle-hashing to a root using membership witness"),
CircuitErrorCode::PRIVATE_KERNEL__READ_REQUEST_PRIVATE_DATA_ROOT_MISMATCH);
}
if (witness.is_transient) {
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/906):
// kernel must ensure that transient reads are either matched to a
// commitment or forwarded to the next iteration to handle it.
builder.do_assert(
false,
format("kernel could not match read_request[",
rr_idx,
"] with a commitment and does not yet support forwarding read requests.",
"\n\tread_request: ",
read_request,
"\n\tsiloed-rr* (leaf): ",
leaf,
"\n\t* got leaf by siloing read_request (compressing with storage_contract_address)"),
CircuitErrorCode::PRIVATE_KERNEL__TRANSIENT_READ_REQUEST_NO_MATCH);
}
}
}

Expand Down
6 changes: 3 additions & 3 deletions circuits/cpp/src/aztec3/circuits/kernel/private/common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
#include "aztec3/circuits/abis/contract_deployment_data.hpp"
#include "aztec3/circuits/abis/function_data.hpp"
#include "aztec3/circuits/abis/kernel_circuit_public_inputs.hpp"
#include "aztec3/circuits/abis/membership_witness.hpp"
#include "aztec3/circuits/abis/previous_kernel_data.hpp"
#include "aztec3/circuits/abis/private_kernel/private_call_data.hpp"
#include "aztec3/circuits/abis/read_request_membership_witness.hpp"
#include "aztec3/utils/dummy_circuit_builder.hpp"


Expand All @@ -17,8 +17,8 @@ using aztec3::circuits::abis::ContractDeploymentData;
using DummyBuilder = aztec3::utils::DummyCircuitBuilder;
using aztec3::circuits::abis::FunctionData;
using aztec3::circuits::abis::KernelCircuitPublicInputs;
using aztec3::circuits::abis::MembershipWitness;
using aztec3::circuits::abis::PreviousKernelData;
using aztec3::circuits::abis::ReadRequestMembershipWitness;
using aztec3::circuits::abis::private_kernel::PrivateCallData;


Expand All @@ -28,7 +28,7 @@ void common_validate_call_stack(DummyBuilder& builder, PrivateCallData<NT> const
void common_validate_read_requests(DummyBuilder& builder,
NT::fr const& storage_contract_address,
std::array<fr, READ_REQUESTS_LENGTH> const& read_requests,
std::array<MembershipWitness<NT, PRIVATE_DATA_TREE_HEIGHT>,
std::array<ReadRequestMembershipWitness<NT, PRIVATE_DATA_TREE_HEIGHT>,
READ_REQUESTS_LENGTH> const& read_request_membership_witnesses,
NT::fr const& historic_private_data_tree_root);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "testing_harness.hpp"

#include "aztec3/circuits/abis/read_request_membership_witness.hpp"
#include "aztec3/circuits/apps/test_apps/basic_contract_deployment/basic_contract_deployment.hpp"
#include "aztec3/circuits/apps/test_apps/escrow/deposit.hpp"
#include "aztec3/circuits/kernel/private/init.hpp"
Expand Down Expand Up @@ -395,7 +396,7 @@ TEST_F(native_private_kernel_init_tests, native_read_request_root_mismatch)
private_inputs.private_call.call_stack_item.public_inputs.historic_private_data_tree_root = root;
auto [read_requests1, read_request_membership_witnesses1, _root] = get_random_reads(contract_address, 2);
std::array<NT::fr, READ_REQUESTS_LENGTH> bad_requests{};
std::array<MembershipWitness<NT, PRIVATE_DATA_TREE_HEIGHT>, READ_REQUESTS_LENGTH> bad_witnesses;
std::array<ReadRequestMembershipWitness<NT, PRIVATE_DATA_TREE_HEIGHT>, READ_REQUESTS_LENGTH> bad_witnesses;
// note we are using read_requests0 for some and read_requests1 for others
bad_requests[0] = read_requests0[0];
bad_requests[1] = read_requests0[1];
Expand Down Expand Up @@ -429,7 +430,7 @@ TEST_F(native_private_kernel_init_tests, native_no_read_requests_works)

// empty requests
std::array<fr, READ_REQUESTS_LENGTH> const read_requests{};
std::array<MembershipWitness<NT, PRIVATE_DATA_TREE_HEIGHT>, READ_REQUESTS_LENGTH> const
std::array<ReadRequestMembershipWitness<NT, PRIVATE_DATA_TREE_HEIGHT>, READ_REQUESTS_LENGTH> const
read_request_membership_witnesses{};
private_inputs.private_call.call_stack_item.public_inputs.read_requests = read_requests;
private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses;
Expand Down Expand Up @@ -542,7 +543,9 @@ TEST_F(native_private_kernel_init_tests, native_max_read_requests_works)
// https://github.com/AztecProtocol/aztec-packages/issues/786


TEST_F(native_private_kernel_init_tests, native_one_transient_read_requests_works)
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/906): re-enable once kernel supports forwarding/matching
// of transient reads.
TEST_F(native_private_kernel_init_tests, skip_native_one_transient_read_requests_works)
{
// one transient read request should work

Expand All @@ -556,8 +559,9 @@ TEST_F(native_private_kernel_init_tests, native_one_transient_read_requests_work
private_inputs.private_call.call_stack_item.public_inputs.read_requests = read_requests;

// Make the read request transient
read_request_membership_witnesses[0].leaf_index = NT::fr(-1);
read_request_membership_witnesses[0].leaf_index = NT::fr(0);
read_request_membership_witnesses[0].sibling_path = std::array<fr, PRIVATE_DATA_TREE_HEIGHT>{};
read_request_membership_witnesses[0].is_transient = true;
private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses;

DummyBuilder builder = DummyBuilder("native_private_kernel_init_tests__native_one_transient_read_requests_works");
Expand All @@ -572,7 +576,9 @@ TEST_F(native_private_kernel_init_tests, native_one_transient_read_requests_work
ASSERT_FALSE(builder.failed());
}

TEST_F(native_private_kernel_init_tests, native_max_read_requests_one_transient_works)
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/906): re-enable once kernel supports forwarding/matching
// of transient reads.
TEST_F(native_private_kernel_init_tests, skip_native_max_read_requests_one_transient_works)
{
// max read requests with one transient should work

Expand All @@ -587,8 +593,9 @@ TEST_F(native_private_kernel_init_tests, native_max_read_requests_one_transient_
private_inputs.private_call.call_stack_item.public_inputs.read_requests = read_requests;

// Make the read request at position 1 transient
read_request_membership_witnesses[1].leaf_index = NT::fr(-1);
read_request_membership_witnesses[1].leaf_index = NT::fr(0);
read_request_membership_witnesses[1].sibling_path = std::array<fr, PRIVATE_DATA_TREE_HEIGHT>{};
read_request_membership_witnesses[1].is_transient = true;
private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses;

DummyBuilder builder =
Expand All @@ -604,4 +611,32 @@ TEST_F(native_private_kernel_init_tests, native_max_read_requests_one_transient_
ASSERT_FALSE(builder.failed());
}

// TODO(https://github.com/AztecProtocol/aztec-packages/issues/906): remove/rework once kernel supports
// forwarding/matching of transient reads.
TEST_F(native_private_kernel_init_tests, native_expect_error_transient_read_request_no_match)
{
// read request without match should fail
auto private_inputs = do_private_call_get_kernel_inputs_init(false, deposit, standard_test_args());

auto const& contract_address =
private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address;

auto [read_requests, read_request_membership_witnesses, root] = get_random_reads(contract_address, 1);
private_inputs.private_call.call_stack_item.public_inputs.historic_private_data_tree_root = root;
private_inputs.private_call.call_stack_item.public_inputs.read_requests = read_requests;

// Make the read request transient
read_request_membership_witnesses[0].leaf_index = NT::fr(0);
read_request_membership_witnesses[0].sibling_path = std::array<fr, PRIVATE_DATA_TREE_HEIGHT>{};
read_request_membership_witnesses[0].is_transient = true;
private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses;

DummyBuilder builder =
DummyBuilder("native_private_kernel_init_tests__native_expect_error_transient_read_request_no_match");
auto const& public_inputs = native_private_kernel_circuit_initial(builder, private_inputs);

ASSERT(builder.failed());
EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::PRIVATE_KERNEL__TRANSIENT_READ_REQUEST_NO_MATCH);
}

} // namespace aztec3::circuits::kernel::private_kernel
Loading

0 comments on commit 322e9a6

Please sign in to comment.