Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
100 changes: 100 additions & 0 deletions barretenberg/cpp/pil/vm2/public_data_read.pil
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
include "merkle_check.pil";
include "ff_gt.pil";
include "poseidon2_hash.pil";
include "constants_gen.pil";
include "precomputed.pil";

// This gadget checks reads in the public data tree. The public data tree is an indexed tree where leaves
// have slot and value. Slot is the "key" and value is the stored data. When we read from the public data tree,
// we assume that a slot that has not been written before has value zero.
// For this we perform a low leaf membership proof and:
// - if the low leaf slot is equal to the slot, that means that slot has been written before, and we assert that
// the low leaf value is equal to the value we are reading.
// - if the low leaf slot is not equal to the slot, we assert that the low leaf is indeed a valid low leaf for the
// requested slot, proving non existence of the slot in the tree. In that case we check value to be zero.
// In order to validate that a leaf is a low leaf of the slot, we need to check that the low_leaf.slot is < slot
// and that low_leaf.next_slot is > slot. However, we need to consider the case where next_slot is zero, which
// means "infinity". The highest slot inserted in the tree will point to infinity as the "next_slot".
//
// Usage:
// sel { value, leaf_slot, public_data_tree_root }
// in public_data_read.sel { public_data_read.value, public_data_read.slot, public_data_read.root };
//
namespace public_data_read;
pol commit sel;
sel * (1 - sel) = 0;

Copy link
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
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
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

#[skippable_if]
sel = 0;

// Inputs to the gadget
Copy link
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 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;

// SLOT_LOW_LEAF_SLOT_DIFF == 0 <==> LEAF_EXISTS == 1
#[EXISTS_FLAG_CHECK]
Copy link
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;

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

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

// 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 };
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
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
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 All @@ -123,7 +125,9 @@ TEST_P(InteractionsTests, InteractionsWithRangeCheck)
check_relation<ff_gt>(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
Loading