Skip to content

Commit

Permalink
feat(1090): validate that some arrays are zero-padded on the right (#…
Browse files Browse the repository at this point in the history
…2519)

Resolves #1090 

- Added a validation check for arrays to be zero-padded in
utils::array.hpp
- Activate this validation check in native private init and inner kernel
circuit on the following private_circuit_public_input members:
    - read_requests
    - return_values
    - new_commitments
    - new_nullifiers
    - nullified_commitments
    - private_call_stack
    - public_call_stack
    - new_l2_to_l1_msgs
- Unit tests on the validation routine and native private kernel
circuits
  • Loading branch information
jeanmon authored Sep 29, 2023
1 parent 0b62207 commit 0327b54
Show file tree
Hide file tree
Showing 13 changed files with 450 additions and 12 deletions.
33 changes: 29 additions & 4 deletions circuits/cpp/src/aztec3/circuits/kernel/private/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "aztec3/circuits/abis/function_data.hpp"
#include "aztec3/circuits/abis/kernel_circuit_public_inputs.hpp"
#include "aztec3/circuits/abis/new_contract_data.hpp"
#include "aztec3/circuits/abis/private_circuit_public_inputs.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"
Expand All @@ -16,6 +17,7 @@

using DummyBuilder = aztec3::utils::DummyCircuitBuilder;

using aztec3::circuits::abis::CompleteAddress;
using aztec3::circuits::abis::ContractDeploymentData;
using aztec3::circuits::abis::ContractLeafPreimage;
using aztec3::circuits::abis::FunctionData;
Expand All @@ -26,6 +28,7 @@ using aztec3::circuits::abis::ReadRequestMembershipWitness;
using aztec3::utils::array_push;
using aztec3::utils::is_array_empty;
using aztec3::utils::push_array_to_array;
using aztec3::utils::validate_array;
using DummyBuilder = aztec3::utils::DummyCircuitBuilder;
using CircuitErrorCode = aztec3::utils::CircuitErrorCode;
using aztec3::circuits::abis::private_kernel::PrivateCallData;
Expand Down Expand Up @@ -114,6 +117,28 @@ void common_validate_read_requests(DummyBuilder& builder,
}
}

/**
* @brief We validate that relevant arrays assumed to be zero-padded on the right comply to this format.
*
* @param builder
* @param app_public_inputs Reference to the private_circuit_public_inputs of the current kernel iteration.
*/
void common_validate_arrays(DummyBuilder& builder, PrivateCircuitPublicInputs<NT> const& app_public_inputs)
{
// Each of the following arrays is expected to be zero-padded.
// In addition, some of the following arrays (new_commitments, etc...) are passed
// to push_array_to_array() routines which rely on the passed arrays to be well-formed.
validate_array(builder, app_public_inputs.return_values, "Return values");
validate_array(builder, app_public_inputs.read_requests, "Read requests");
validate_array(builder, app_public_inputs.new_commitments, "New commitments");
validate_array(builder, app_public_inputs.new_nullifiers, "New nullifiers");
validate_array(builder, app_public_inputs.nullified_commitments, "Nullified commitments");
validate_array(builder, app_public_inputs.private_call_stack, "Private Call Stack");
validate_array(builder, app_public_inputs.public_call_stack, "Public Call Stack");
validate_array(builder, app_public_inputs.new_l2_to_l1_msgs, "New L2 to L1 messages");
// encrypted_logs_hash and unencrypted_logs_hash have their own integrity checks.
}

void common_validate_0th_nullifier(DummyBuilder& builder, CombinedAccumulatedData<NT> const& end)
{
builder.do_assert(end.new_nullifiers[0] != 0,
Expand Down Expand Up @@ -296,10 +321,10 @@ void common_contract_logic(DummyBuilder& builder,
auto constructor_hash =
compute_constructor_hash(function_data, private_call_public_inputs.args_hash, private_call_vk_hash);

auto const new_contract_address = abis::CompleteAddress<NT>::compute(contract_dep_data.deployer_public_key,
contract_dep_data.contract_address_salt,
contract_dep_data.function_tree_root,
constructor_hash)
auto const new_contract_address = CompleteAddress<NT>::compute(contract_dep_data.deployer_public_key,
contract_dep_data.contract_address_salt,
contract_dep_data.function_tree_root,
constructor_hash)
.address;

// Add new contract data if its a contract deployment function
Expand Down
3 changes: 3 additions & 0 deletions circuits/cpp/src/aztec3/circuits/kernel/private/common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "aztec3/circuits/abis/function_data.hpp"
#include "aztec3/circuits/abis/kernel_circuit_public_inputs.hpp"
#include "aztec3/circuits/abis/previous_kernel_data.hpp"
#include "aztec3/circuits/abis/private_circuit_public_inputs.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 @@ -19,6 +20,7 @@ using aztec3::circuits::abis::ContractDeploymentData;
using aztec3::circuits::abis::FunctionData;
using aztec3::circuits::abis::KernelCircuitPublicInputs;
using aztec3::circuits::abis::PreviousKernelData;
using aztec3::circuits::abis::PrivateCircuitPublicInputs;
using aztec3::circuits::abis::ReadRequestMembershipWitness;
using aztec3::circuits::abis::private_kernel::PrivateCallData;

Expand All @@ -34,6 +36,7 @@ void common_validate_read_requests(DummyBuilder& builder,
std::array<ReadRequestMembershipWitness<NT, PRIVATE_DATA_TREE_HEIGHT>,
MAX_READ_REQUESTS_PER_CALL> const& read_request_membership_witnesses);

void common_validate_arrays(DummyBuilder& builder, PrivateCircuitPublicInputs<NT> const& app_public_inputs);
void common_validate_0th_nullifier(DummyBuilder& builder, CombinedAccumulatedData<NT> const& end);

void common_update_end_values(DummyBuilder& builder,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ KernelCircuitPublicInputs<NT> native_private_kernel_circuit_initial(DummyCircuit

validate_inputs(builder, private_inputs);

common_validate_arrays(builder, private_inputs.private_call.call_stack_item.public_inputs);

validate_this_private_call_against_tx_request(builder, private_inputs);

common_validate_call_stack(builder, private_inputs.private_call);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,123 @@ TEST_F(native_private_kernel_init_tests, basic_contract_deployment)
EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::NO_ERROR);
}

TEST_F(native_private_kernel_init_tests, input_validation_malformed_arrays_return_values)
{
auto private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args());

std::array<fr, RETURN_VALUES_LENGTH> malformed_return_values{ fr(0), fr(0), fr(553) };
private_inputs.private_call.call_stack_item.public_inputs.return_values = malformed_return_values;

DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_return_values");
native_private_kernel_circuit_initial(builder, private_inputs);

EXPECT_EQ(builder.failed(), true);
EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED);
}

TEST_F(native_private_kernel_init_tests, input_validation_malformed_arrays_read_requests)
{
auto private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args());

std::array<fr, MAX_READ_REQUESTS_PER_CALL> malformed_read_requests{ fr(0), fr(9123), fr(0), fr(12) };
private_inputs.private_call.call_stack_item.public_inputs.read_requests = malformed_read_requests;

DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_read_requests");
native_private_kernel_circuit_initial(builder, private_inputs);

EXPECT_EQ(builder.failed(), true);
EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED);
}

TEST_F(native_private_kernel_init_tests, input_validation_malformed_arrays_commitments)
{
auto private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args());

std::array<fr, MAX_NEW_COMMITMENTS_PER_CALL> malformed_commitments{ fr(0), fr(9123) };
private_inputs.private_call.call_stack_item.public_inputs.new_commitments = malformed_commitments;

DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_commitments");
native_private_kernel_circuit_initial(builder, private_inputs);

EXPECT_EQ(builder.failed(), true);
EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED);
}

TEST_F(native_private_kernel_init_tests, input_validation_malformed_arrays_nullifiers)
{
auto private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args());

std::array<fr, MAX_NEW_NULLIFIERS_PER_CALL> malformed_nullifiers{};
malformed_nullifiers[MAX_NEW_NULLIFIERS_PER_CALL - 1] = fr(12);
private_inputs.private_call.call_stack_item.public_inputs.new_nullifiers = malformed_nullifiers;

DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_nullifiers");
native_private_kernel_circuit_initial(builder, private_inputs);

EXPECT_EQ(builder.failed(), true);
EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED);
}

TEST_F(native_private_kernel_init_tests, input_validation_malformed_arrays_nullified_commitments)
{
auto private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args());

std::array<fr, MAX_NEW_NULLIFIERS_PER_CALL> malformed_nullified_commitments{ fr(0),
fr(0),
EMPTY_NULLIFIED_COMMITMENT };
private_inputs.private_call.call_stack_item.public_inputs.nullified_commitments = malformed_nullified_commitments;

DummyBuilder builder =
DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_nullified_commitments");
native_private_kernel_circuit_initial(builder, private_inputs);

EXPECT_EQ(builder.failed(), true);
EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED);
}

TEST_F(native_private_kernel_init_tests, input_validation_malformed_arrays_private_call_stack)
{
auto private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args());

std::array<fr, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL> malformed_private_call_stack{ fr(0), fr(888) };
private_inputs.private_call.call_stack_item.public_inputs.private_call_stack = malformed_private_call_stack;

DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_private_call_stack");
native_private_kernel_circuit_initial(builder, private_inputs);

EXPECT_EQ(builder.failed(), true);
EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED);
}

TEST_F(native_private_kernel_init_tests, input_validation_malformed_arrays_public_call_stack)
{
auto private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args());

std::array<fr, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL> malformed_public_call_stack{ fr(0), fr(888) };
private_inputs.private_call.call_stack_item.public_inputs.public_call_stack = malformed_public_call_stack;

DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_public_call_stack");
native_private_kernel_circuit_initial(builder, private_inputs);

EXPECT_EQ(builder.failed(), true);
EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED);
}

TEST_F(native_private_kernel_init_tests, input_validation_malformed_arrays_new_l2_to_l1_msgs)
{
auto private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args());

std::array<fr, MAX_NEW_L2_TO_L1_MSGS_PER_CALL> malformed_new_l2_to_l1_msgs{};
malformed_new_l2_to_l1_msgs[MAX_NEW_L2_TO_L1_MSGS_PER_CALL - 1] = fr(1);
private_inputs.private_call.call_stack_item.public_inputs.new_l2_to_l1_msgs = malformed_new_l2_to_l1_msgs;

DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_new_l2_to_l1_msgs");
native_private_kernel_circuit_initial(builder, private_inputs);

EXPECT_EQ(builder.failed(), true);
EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED);
}

TEST_F(native_private_kernel_init_tests, contract_deployment_call_stack_item_hash_mismatch_fails)
{
auto private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ KernelCircuitPublicInputs<NT> native_private_kernel_circuit_inner(DummyCircuitBu

validate_inputs(builder, private_inputs);

common_validate_arrays(builder, private_inputs.private_call.call_stack_item.public_inputs);

pop_and_validate_this_private_call_hash(builder, private_inputs.private_call, public_inputs.end.private_call_stack);

common_validate_call_stack(builder, private_inputs.private_call);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,123 @@ TEST_F(native_private_kernel_inner_tests, private_function_incorrect_call_stack_
CircuitErrorCode::PRIVATE_KERNEL__CALCULATED_PRIVATE_CALL_HASH_AND_PROVIDED_PRIVATE_CALL_HASH_MISMATCH);
}

TEST_F(native_private_kernel_inner_tests, input_validation_malformed_arrays_return_values)
{
auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args());

std::array<fr, RETURN_VALUES_LENGTH> malformed_return_values{ fr(0), fr(0), fr(553) };
private_inputs.private_call.call_stack_item.public_inputs.return_values = malformed_return_values;

DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_return_values");
native_private_kernel_circuit_inner(builder, private_inputs);

EXPECT_EQ(builder.failed(), true);
EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED);
}

TEST_F(native_private_kernel_inner_tests, input_validation_malformed_arrays_read_requests)
{
auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args());

std::array<fr, MAX_READ_REQUESTS_PER_CALL> malformed_read_requests{ fr(0), fr(9123), fr(0), fr(12) };
private_inputs.private_call.call_stack_item.public_inputs.read_requests = malformed_read_requests;

DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_read_requests");
native_private_kernel_circuit_inner(builder, private_inputs);

EXPECT_EQ(builder.failed(), true);
EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED);
}

TEST_F(native_private_kernel_inner_tests, input_validation_malformed_arrays_commitments)
{
auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args());

std::array<fr, MAX_NEW_COMMITMENTS_PER_CALL> malformed_commitments{ fr(0), fr(9123) };
private_inputs.private_call.call_stack_item.public_inputs.new_commitments = malformed_commitments;

DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_commitments");
native_private_kernel_circuit_inner(builder, private_inputs);

EXPECT_EQ(builder.failed(), true);
EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED);
}

TEST_F(native_private_kernel_inner_tests, input_validation_malformed_arrays_nullifiers)
{
auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args());

std::array<fr, MAX_NEW_NULLIFIERS_PER_CALL> malformed_nullifiers{};
malformed_nullifiers[MAX_NEW_NULLIFIERS_PER_CALL - 1] = fr(12);
private_inputs.private_call.call_stack_item.public_inputs.new_nullifiers = malformed_nullifiers;

DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_nullifiers");
native_private_kernel_circuit_inner(builder, private_inputs);

EXPECT_EQ(builder.failed(), true);
EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED);
}

TEST_F(native_private_kernel_inner_tests, input_validation_malformed_arrays_nullified_commitments)
{
auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args());

std::array<fr, MAX_NEW_NULLIFIERS_PER_CALL> malformed_nullified_commitments{ fr(0),
fr(0),
EMPTY_NULLIFIED_COMMITMENT };
private_inputs.private_call.call_stack_item.public_inputs.nullified_commitments = malformed_nullified_commitments;

DummyBuilder builder =
DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_nullified_commitments");
native_private_kernel_circuit_inner(builder, private_inputs);

EXPECT_EQ(builder.failed(), true);
EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED);
}

TEST_F(native_private_kernel_inner_tests, input_validation_malformed_arrays_private_call_stack)
{
auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args());

std::array<fr, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL> malformed_private_call_stack{ fr(0), fr(888) };
private_inputs.private_call.call_stack_item.public_inputs.private_call_stack = malformed_private_call_stack;

DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_private_call_stack");
native_private_kernel_circuit_inner(builder, private_inputs);

EXPECT_EQ(builder.failed(), true);
EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED);
}

TEST_F(native_private_kernel_inner_tests, input_validation_malformed_arrays_public_call_stack)
{
auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args());

std::array<fr, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL> malformed_public_call_stack{ fr(0), fr(888) };
private_inputs.private_call.call_stack_item.public_inputs.public_call_stack = malformed_public_call_stack;

DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_public_call_stack");
native_private_kernel_circuit_inner(builder, private_inputs);

EXPECT_EQ(builder.failed(), true);
EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED);
}

TEST_F(native_private_kernel_inner_tests, input_validation_malformed_arrays_new_l2_to_l1_msgs)
{
auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args());

std::array<fr, MAX_NEW_L2_TO_L1_MSGS_PER_CALL> malformed_new_l2_to_l1_msgs{};
malformed_new_l2_to_l1_msgs[MAX_NEW_L2_TO_L1_MSGS_PER_CALL - 1] = fr(1);
private_inputs.private_call.call_stack_item.public_inputs.new_l2_to_l1_msgs = malformed_new_l2_to_l1_msgs;

DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_new_l2_to_l1_msgs");
native_private_kernel_circuit_inner(builder, private_inputs);

EXPECT_EQ(builder.failed(), true);
EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED);
}

TEST_F(native_private_kernel_inner_tests, private_kernel_should_fail_if_aggregating_too_many_commitments)
{
// Negative test to check if push_array_to_array fails if two many commitments are merged together
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,6 @@ void match_reads_to_commitments(DummyCircuitBuilder& builder,
}
}

// TODO(https://github.com/AztecProtocol/aztec-packages/issues/837): optimized based on hints
// regarding matching a nullifier to a commitment
// i.e., we get pairs i,j such that new_nullifiers[i] == new_commitments[j]

/**
* @brief This function matches transient nullifiers to commitments and squashes (deletes) them both.
*
Expand Down
Loading

0 comments on commit 0327b54

Please sign in to comment.