Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 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
1 change: 1 addition & 0 deletions barretenberg/cpp/pil/avm/constants_gen.pil
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace constants;
pol MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL = 16;
pol MAX_L1_TO_L2_MSG_READ_REQUESTS_PER_CALL = 16;
pol MAX_PUBLIC_LOGS_PER_CALL = 4;
pol PUBLIC_DATA_TREE_HEIGHT = 40;
pol MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 3000;
pol MEM_TAG_FF = 0;
pol MEM_TAG_U1 = 1;
Expand Down
1 change: 1 addition & 0 deletions barretenberg/cpp/pil/vm2/constants_gen.pil
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace constants;
pol MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL = 16;
pol MAX_L1_TO_L2_MSG_READ_REQUESTS_PER_CALL = 16;
pol MAX_PUBLIC_LOGS_PER_CALL = 4;
pol PUBLIC_DATA_TREE_HEIGHT = 40;
pol MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 3000;
pol MEM_TAG_FF = 0;
pol MEM_TAG_U1 = 1;
Expand Down
1 change: 1 addition & 0 deletions barretenberg/cpp/pil/vm2/execution.pil
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ include "to_radix.pil";
include "ff_gt.pil";
include "context.pil";
include "context_stack.pil";
include "public_data_read.pil";

namespace execution;

Expand Down
81 changes: 81 additions & 0 deletions barretenberg/cpp/pil/vm2/public_data_read.pil
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
include "./merkle_check.pil";
include "./ff_gt.pil";
include "./poseidon2_hash.pil";
include "./constants_gen.pil";
include "./precomputed.pil";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't need ./ ?


namespace public_data_read;
pol commit sel;
sel * (1 - sel) = 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sirasistant We certainly can add a skippable condition.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yes pleaaase! I didn't pay attention but basically we shouldn't merge any gadget without skippable

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh, my bad, forgot about the skippable condition

// Inputs to the gadget
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usage of this gagdet is not clear at first sight.
Is there any output expected?
@sirasistant Could you please add a line of a caller would use this gadget?
Maybe a little description of what it does would be useful.

pol commit value;
pol commit slot;
pol commit root;

// Hints
pol commit low_leaf_slot;
pol commit low_leaf_value;
pol commit low_leaf_next_index;
pol commit low_leaf_next_slot;

pol commit low_leaf_index;

// ========= LOW LEAF MEMBERSHIP =========
pol commit low_leaf_hash;
// TODO: We need this temporarily while we dont allow for aliases in the lookup tuple
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// TODO: We need this temporarily while we dont allow for aliases in the lookup tuple
// TODO: We need this temporarily while we do not allow for aliases in the lookup tuple

pol commit tree_height;
sel * (tree_height - constants.PUBLIC_DATA_TREE_HEIGHT) = 0;

#[LOW_LEAF_POSEIDON2_0]
sel { low_leaf_slot, low_leaf_value, low_leaf_next_index, low_leaf_hash }
in poseidon2_hash.start { poseidon2_hash.input_0, poseidon2_hash.input_1, poseidon2_hash.input_2, poseidon2_hash.output };

#[LOW_LEAF_POSEIDON2_1]
sel { low_leaf_next_slot, precomputed.zero, precomputed.zero, low_leaf_hash }
in poseidon2_hash.end { poseidon2_hash.input_0, poseidon2_hash.input_1, poseidon2_hash.input_2, poseidon2_hash.output };

#[LOW_LEAF_MEMBERSHIP]
sel { low_leaf_hash, low_leaf_index, tree_height, root }
in merkle_check.start { merkle_check.read_node, merkle_check.index, merkle_check.path_len, merkle_check.read_root };

// ========= LOW LEAF VALIDATION =========
// We commit leaf not exists instead of leaf exists since it'll be used as a selector for a lookup
pol commit leaf_not_exists;
leaf_not_exists * (1 - leaf_not_exists) = 0;
pol LEAF_EXISTS = 1 - leaf_not_exists;

pol commit slot_low_leaf_slot_diff_inv;
pol SLOT_LOW_LEAF_SLOT_DIFF = slot - low_leaf_slot;

#[EXISTS_FLAG_CHECK]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment like: SLOT_LOW_LEAF_SLOT_DIFF == 0 <==> LEAF_EXISTS == 0

sel * (SLOT_LOW_LEAF_SLOT_DIFF * (LEAF_EXISTS * (1 - slot_low_leaf_slot_diff_inv) + slot_low_leaf_slot_diff_inv) - 1 + LEAF_EXISTS) = 0;

// If doesn't exist, we need to validate that the slot is greater than the low leaf slot
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// If doesn't exist, we need to validate that the slot is greater than the low leaf slot
// If it doesn't exist, we need to validate that the slot is greater than the low leaf slot


// TODO: We need this temporarily while we dont allow for aliases in the lookup tuple
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// TODO: We need this temporarily while we dont allow for aliases in the lookup tuple
// TODO: We need this temporarily while we do not allow for aliases in the lookup tuple

pol commit one;
sel * (1 - one) = 0;

#[LOW_LEAF_SLOT_VALIDATION]
leaf_not_exists { slot, low_leaf_slot, one }
in ff_gt.sel_gt { ff_gt.a, ff_gt.b, ff_gt.result };

// If next slot is not zero (which would be infinity), it has to be greater than the slot.
// We commit next_slot_is_nonzero instead of next_slot_is_zero since it'll be used as a selector for a lookup
pol commit next_slot_is_nonzero;
next_slot_is_nonzero * (1 - next_slot_is_nonzero) = 0;
pol NEXT_SLOT_IS_ZERO = 1 - next_slot_is_nonzero;

pol commit next_slot_inv;
#[NEXT_SLOT_IS_ZERO_CHECK]
leaf_not_exists * (low_leaf_next_slot * (NEXT_SLOT_IS_ZERO * (1 - next_slot_inv) + next_slot_inv) - 1 + NEXT_SLOT_IS_ZERO) = 0;

#[LOW_LEAF_NEXT_SLOT_VALIDATION]
next_slot_is_nonzero { low_leaf_next_slot, slot, one }
in ff_gt.sel_gt { ff_gt.a, ff_gt.b, ff_gt.result };

// ========= VALUE VALIDATION =========
Copy link
Copy Markdown
Contributor

@jeanmon jeanmon Mar 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For readability, I would prefer to have this relation just after the definition of LEAF_EXISTS.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one?

    #[VALUE_IS_CORRECT]
    low_leaf_value * LEAF_EXISTS - value = 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

// value = LEAF_EXISTS ? low_leaf_value : 0
#[VALUE_IS_CORRECT]
low_leaf_value * LEAF_EXISTS - value = 0;
1 change: 1 addition & 0 deletions barretenberg/cpp/src/barretenberg/vm/aztec_constants.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#define MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL 16
#define MAX_L1_TO_L2_MSG_READ_REQUESTS_PER_CALL 16
#define MAX_PUBLIC_LOGS_PER_CALL 4
#define PUBLIC_DATA_TREE_HEIGHT 40
#define MAX_NOTE_HASHES_PER_TX 64
#define MAX_NULLIFIERS_PER_TX 64
#define MAX_ENQUEUED_CALLS_PER_TX 32
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ std::vector<TestParams> comparison_tests = {
TestParams{ 0, -1, false }
};

class BasicTest : public TestWithParam<TestParams> {};
class FieldGreaterThanBasicTest : public TestWithParam<TestParams> {};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we were actually calling them SomethingConstrainingTest?

Copy link
Copy Markdown
Contributor Author

@sirasistant sirasistant Mar 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes but this class name is appended to FieldGreaterThanConstrainingTest in

INSTANTIATE_TEST_SUITE_P(FieldGreaterThanConstrainingTest,
                         FieldGreaterThanBasicTest,
                         ::testing::ValuesIn(comparison_tests));

results in something like this
[ RUN ] PublicDataTreeReadConstrainingTest/PublicDataReadPositiveTests.Positive/2
I changed the name to make class name not clash with other tests


TEST_P(BasicTest, BasicComparison)
TEST_P(FieldGreaterThanBasicTest, BasicComparison)
{
const auto& param = GetParam();

Expand All @@ -92,11 +92,13 @@ TEST_P(BasicTest, BasicComparison)
check_relation<ff_gt>(trace);
}

INSTANTIATE_TEST_SUITE_P(FieldGreaterThanConstrainingTest, BasicTest, ::testing::ValuesIn(comparison_tests));
INSTANTIATE_TEST_SUITE_P(FieldGreaterThanConstrainingTest,
FieldGreaterThanBasicTest,
::testing::ValuesIn(comparison_tests));

class InteractionsTests : public TestWithParam<TestParams> {};
class FieldGreaterThanInteractionsTests : public TestWithParam<TestParams> {};

TEST_P(InteractionsTests, InteractionsWithRangeCheck)
TEST_P(FieldGreaterThanInteractionsTests, InteractionsWithRangeCheck)
{
const auto& param = GetParam();

Expand Down Expand Up @@ -125,7 +127,9 @@ TEST_P(InteractionsTests, InteractionsWithRangeCheck)
check_interaction<lookup_a_lo_range>(trace);
}

INSTANTIATE_TEST_SUITE_P(FieldGreaterThanConstrainingTest, InteractionsTests, ::testing::ValuesIn(comparison_tests));
INSTANTIATE_TEST_SUITE_P(FieldGreaterThanConstrainingTest,
FieldGreaterThanInteractionsTests,
::testing::ValuesIn(comparison_tests));

TEST(FieldGreaterThanConstrainingTest, NegativeManipulatedDecompositions)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include <cmath>
#include <cstdint>

#include "barretenberg/crypto/poseidon2/poseidon2.hpp"
#include "barretenberg/vm2/constraining/flavor_settings.hpp"
#include "barretenberg/vm2/constraining/testing/check_relation.hpp"
#include "barretenberg/vm2/generated/relations/merkle_check.hpp"
#include "barretenberg/vm2/generated/relations/public_data_read.hpp"
#include "barretenberg/vm2/simulation/events/event_emitter.hpp"
#include "barretenberg/vm2/simulation/events/public_data_tree_read_event.hpp"
#include "barretenberg/vm2/simulation/field_gt.hpp"
#include "barretenberg/vm2/simulation/lib/merkle.hpp"
#include "barretenberg/vm2/simulation/poseidon2.hpp"
#include "barretenberg/vm2/simulation/public_data_tree_check.hpp"
#include "barretenberg/vm2/simulation/testing/mock_range_check.hpp"
#include "barretenberg/vm2/testing/fixtures.hpp"
#include "barretenberg/vm2/testing/macros.hpp"
#include "barretenberg/vm2/tracegen/field_gt_trace.hpp"
#include "barretenberg/vm2/tracegen/lib/lookup_builder.hpp"
#include "barretenberg/vm2/tracegen/merkle_check_trace.hpp"
#include "barretenberg/vm2/tracegen/poseidon2_trace.hpp"
#include "barretenberg/vm2/tracegen/public_data_tree_read_trace.hpp"
#include "barretenberg/vm2/tracegen/test_trace_container.hpp"

namespace bb::avm2::constraining {
namespace {

using ::testing::NiceMock;
using ::testing::TestWithParam;

using simulation::EventEmitter;
using simulation::FieldGreaterThan;
using simulation::FieldGreaterThanEvent;
using simulation::MerkleCheck;
using simulation::MerkleCheckEvent;
using simulation::MockRangeCheck;
using simulation::NoopEventEmitter;
using simulation::Poseidon2;
using simulation::Poseidon2HashEvent;
using simulation::Poseidon2PermutationEvent;
using simulation::PublicDataTreeCheck;
using simulation::PublicDataTreeLeafPreimage;
using simulation::PublicDataTreeReadEvent;
using simulation::root_from_path;

using tracegen::FieldGreaterThanTraceBuilder;
using tracegen::MerkleCheckTraceBuilder;
using tracegen::Poseidon2TraceBuilder;
using tracegen::PublicDataTreeReadTraceBuilder;
using tracegen::TestTraceContainer;

using FF = AvmFlavorSettings::FF;
using C = Column;
using public_data_read = bb::avm2::public_data_read<FF>;
using poseidon2 = crypto::Poseidon2<crypto::Poseidon2Bn254ScalarFieldParams>;
using PublicDataLeafValue = crypto::merkle_tree::PublicDataLeafValue;

TEST(PublicDataTreeReadConstrainingTest, EmptyRow)
{
check_relation<public_data_read>(testing::empty_trace());
}

struct TestParams {
FF leaf_slot;
FF value;
PublicDataTreeLeafPreimage low_leaf;
};

std::vector<TestParams> positive_tests = {
// Exists = true, leaf pointers to infinity
TestParams{
.leaf_slot = 42, .value = 27, .low_leaf = PublicDataTreeLeafPreimage(PublicDataLeafValue(42, 27), 0, 0) },
// Exists = true, leaf points to higher value
TestParams{
.leaf_slot = 42, .value = 27, .low_leaf = PublicDataTreeLeafPreimage(PublicDataLeafValue(42, 27), 28, 50) },
// Exists = false, low leaf points to infinity
TestParams{ .leaf_slot = 42, .value = 0, .low_leaf = PublicDataTreeLeafPreimage(PublicDataLeafValue(10, 0), 0, 0) },
// Exists = false, low leaf points to higher value
TestParams{
.leaf_slot = 42, .value = 0, .low_leaf = PublicDataTreeLeafPreimage(PublicDataLeafValue(10, 0), 28, 50) }
};

class PublicDataReadPositiveTests : public TestWithParam<TestParams> {};

TEST_P(PublicDataReadPositiveTests, Positive)
{
const auto& param = GetParam();

EventEmitter<Poseidon2HashEvent> hash_event_emitter;
NoopEventEmitter<Poseidon2PermutationEvent> perm_event_emitter;
Poseidon2 poseidon2(hash_event_emitter, perm_event_emitter);

EventEmitter<MerkleCheckEvent> merkle_event_emitter;
MerkleCheck merkle_check(poseidon2, merkle_event_emitter);

NiceMock<MockRangeCheck> range_check;

EventEmitter<FieldGreaterThanEvent> field_gt_event_emitter;
FieldGreaterThan field_gt(range_check, field_gt_event_emitter);

EventEmitter<PublicDataTreeReadEvent> public_data_tree_read_event_emitter;
PublicDataTreeCheck public_data_tree_check_simulator(
poseidon2, merkle_check, field_gt, public_data_tree_read_event_emitter);

TestTraceContainer trace({ { { C::precomputed_first_row, 1 } } });
Poseidon2TraceBuilder poseidon2_builder;
MerkleCheckTraceBuilder merkle_check_builder;
FieldGreaterThanTraceBuilder field_gt_builder;
PublicDataTreeReadTraceBuilder public_data_tree_read_builder;

FF low_leaf_hash = poseidon2.hash(param.low_leaf.get_hash_inputs());
uint64_t leaf_index = 30;
std::vector<FF> sibling_path;
sibling_path.reserve(PUBLIC_DATA_TREE_HEIGHT);
for (size_t i = 0; i < PUBLIC_DATA_TREE_HEIGHT; ++i) {
sibling_path.emplace_back(i);
}
FF root = root_from_path(low_leaf_hash, leaf_index, sibling_path);

public_data_tree_check_simulator.assert_read(
param.leaf_slot, param.value, param.low_leaf, leaf_index, sibling_path, root);

poseidon2_builder.process_hash(hash_event_emitter.dump_events(), trace);
merkle_check_builder.process(merkle_event_emitter.dump_events(), trace);
field_gt_builder.process(field_gt_event_emitter.dump_events(), trace);
public_data_tree_read_builder.process(public_data_tree_read_event_emitter.dump_events(), trace);

check_relation<public_data_read>(trace);
}

INSTANTIATE_TEST_SUITE_P(PublicDataTreeReadConstrainingTest,
PublicDataReadPositiveTests,
::testing::ValuesIn(positive_tests));

TEST(PublicDataTreeReadConstrainingTest, NegativeExistsFlagCheck)
{
// Test constraint: sel * (SLOT_LOW_LEAF_SLOT_DIFF * (LEAF_EXISTS * (1 - slot_low_leaf_slot_diff_inv) +
// slot_low_leaf_slot_diff_inv) - 1 + LEAF_EXISTS) = 0
TestTraceContainer trace({
{ { C::public_data_read_sel, 1 },
{ C::public_data_read_slot, 27 },
{ C::public_data_read_low_leaf_slot, 27 },
{ C::public_data_read_slot_low_leaf_slot_diff_inv, 0 },
{ C::public_data_read_leaf_not_exists, 0 } },
{ { C::public_data_read_sel, 1 },
{ C::public_data_read_slot, 28 },
{ C::public_data_read_low_leaf_slot, 27 },
{ C::public_data_read_slot_low_leaf_slot_diff_inv, FF(1).invert() },
{ C::public_data_read_leaf_not_exists, 1 } },
});

check_relation<public_data_read>(trace, public_data_read::SR_EXISTS_FLAG_CHECK);

trace.set(C::public_data_read_leaf_not_exists, 0, 1);

EXPECT_THROW_WITH_MESSAGE(check_relation<public_data_read>(trace, public_data_read::SR_EXISTS_FLAG_CHECK),
"EXISTS_FLAG_CHECK");

trace.set(C::public_data_read_leaf_not_exists, 0, 0);
trace.set(C::public_data_read_leaf_not_exists, 1, 0);

EXPECT_THROW_WITH_MESSAGE(check_relation<public_data_read>(trace, public_data_read::SR_EXISTS_FLAG_CHECK),
"EXISTS_FLAG_CHECK");
}

TEST(PublicDataTreeReadConstrainingTest, NegativeNextSlotIsZero)
{
// Test constraint: leaf_not_exists * (low_leaf_next_slot * (NEXT_SLOT_IS_ZERO * (1 - next_slot_inv) +
// next_slot_inv) - 1 + NEXT_SLOT_IS_ZERO) = 0
TestTraceContainer trace({
{
{ C::public_data_read_leaf_not_exists, 1 },
{ C::public_data_read_low_leaf_next_slot, 0 },
{ C::public_data_read_next_slot_inv, 0 },
{ C::public_data_read_next_slot_is_nonzero, 0 },
},
{
{ C::public_data_read_leaf_not_exists, 1 },
{ C::public_data_read_low_leaf_next_slot, 1 },
{ C::public_data_read_next_slot_inv, FF(1).invert() },
{ C::public_data_read_next_slot_is_nonzero, 1 },
},
});

check_relation<public_data_read>(trace, public_data_read::SR_NEXT_SLOT_IS_ZERO_CHECK);

trace.set(C::public_data_read_next_slot_is_nonzero, 0, 1);

EXPECT_THROW_WITH_MESSAGE(check_relation<public_data_read>(trace, public_data_read::SR_NEXT_SLOT_IS_ZERO_CHECK),
"NEXT_SLOT_IS_ZERO_CHECK");

trace.set(C::public_data_read_next_slot_is_nonzero, 0, 0);
trace.set(C::public_data_read_next_slot_is_nonzero, 1, 0);

EXPECT_THROW_WITH_MESSAGE(check_relation<public_data_read>(trace, public_data_read::SR_NEXT_SLOT_IS_ZERO_CHECK),
"NEXT_SLOT_IS_ZERO_CHECK");
}

TEST(PublicDataTreeReadConstrainingTest, NegativeValueIsCorrect)
{
// Test constraint: leaf_not_exists * (low_leaf_next_slot * (NEXT_SLOT_IS_ZERO * (1 - next_slot_inv) +
// next_slot_inv) - 1 + NEXT_SLOT_IS_ZERO) = 0
TestTraceContainer trace({
{
{ C::public_data_read_low_leaf_value, 27 },
{ C::public_data_read_leaf_not_exists, 0 },
{ C::public_data_read_value, 27 },
},
{
{ C::public_data_read_low_leaf_value, 27 },
{ C::public_data_read_leaf_not_exists, 1 },
{ C::public_data_read_value, 0 },
},
});

check_relation<public_data_read>(trace, public_data_read::SR_VALUE_IS_CORRECT);

// Invalid, if leaf exists, the value should be the low leaf value
trace.set(C::public_data_read_value, 0, 0);

EXPECT_THROW_WITH_MESSAGE(check_relation<public_data_read>(trace, public_data_read::SR_VALUE_IS_CORRECT),
"VALUE_IS_CORRECT");

trace.set(C::public_data_read_value, 0, 27);
// Invalid, if leaf does not exists, the value should be zero
trace.set(C::public_data_read_value, 1, 27);

EXPECT_THROW_WITH_MESSAGE(check_relation<public_data_read>(trace, public_data_read::SR_VALUE_IS_CORRECT),
"VALUE_IS_CORRECT");
}

} // namespace
} // namespace bb::avm2::constraining
8 changes: 4 additions & 4 deletions barretenberg/cpp/src/barretenberg/vm2/generated/columns.hpp

Large diffs are not rendered by default.

Loading
Loading