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(1090): validate that some arrays are zero-padded on the right #2519

Merged
merged 8 commits into from
Sep 29, 2023
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