Skip to content

Commit

Permalink
Align Merge and Root rollup (#166)
Browse files Browse the repository at this point in the history
* fix ci

* add rollup subtree height

* just use one type of public inputs for base/merge (#168)

* fix: Reuse merge logic in root when possible

* fix: root enforce left -> right + test fix

* fix: unused right due to assert

* fix: fix unused computed_root

* chore: make loop bonobo friendly

* fix: use `get_sibling_path` in tree

---------

Co-authored-by: Rahul Kothari <[email protected]>
  • Loading branch information
LHerskind and rahul-kothari authored Apr 6, 2023
1 parent f7672d2 commit fdd7a35
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 234 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ template <typename NCT> std::ostream& operator<<(std::ostream& os, BaseOrMergeRo
"start_nullifier_tree_snapshot:\n"
<< obj.start_nullifier_tree_snapshot
<< "\n"
"end_nullifier_tree_snapshots:\n"
"end_nullifier_tree_snapshot:\n"
<< obj.end_nullifier_tree_snapshot
<< "\n"
"start_contract_tree_snapshot:\n"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@ template <size_t N>
void check_membership(NT::fr leaf, NT::uint32 leafIndex, std::array<NT::fr, N> siblingPath, NT::fr root)
{
auto calculatedRoot = iterate_through_tree_via_sibling_path(leaf, leafIndex, siblingPath);
if (calculatedRoot != root) {
// throw std::runtime_error("Merkle membership check failed");
}
// TODO: update tests to build the correct trees, and then -> assert(calculatedRoot == root);
(void)calculatedRoot;
(void)root;
}

template <size_t N>
Expand Down
6 changes: 3 additions & 3 deletions cpp/src/aztec3/circuits/rollup/merge/.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ TEST_F(merge_rollup_tests, test_fail_if_previous_rollups_dont_follow_on)
.root = fr(1), .next_available_leaf_index = 0
};

EXPECT_DEATH(merge_rollup_circuit(inputA), ".*ensure_prev_rollups_follow_on_from_each_other.*");
EXPECT_DEATH(merge_rollup_circuit(inputA), ".*assert_prev_rollups_follow_on_from_each_other.*");

// do the same for nullifier tree
auto inputB = dummyInputs;
Expand All @@ -63,7 +63,7 @@ TEST_F(merge_rollup_tests, test_fail_if_previous_rollups_dont_follow_on)
inputB.previous_rollup_data[1].base_or_merge_rollup_public_inputs.start_nullifier_tree_snapshot = {
.root = fr(1), .next_available_leaf_index = 0
};
EXPECT_DEATH(merge_rollup_circuit(inputB), ".*ensure_prev_rollups_follow_on_from_each_other.*");
EXPECT_DEATH(merge_rollup_circuit(inputB), ".*assert_prev_rollups_follow_on_from_each_other.*");

// do the same for contract tree
auto inputC = dummyInputs;
Expand All @@ -73,7 +73,7 @@ TEST_F(merge_rollup_tests, test_fail_if_previous_rollups_dont_follow_on)
inputC.previous_rollup_data[1].base_or_merge_rollup_public_inputs.start_contract_tree_snapshot = {
.root = fr(1), .next_available_leaf_index = 0
};
EXPECT_DEATH(merge_rollup_circuit(inputC), ".*ensure_prev_rollups_follow_on_from_each_other.*");
EXPECT_DEATH(merge_rollup_circuit(inputC), ".*assert_prev_rollups_follow_on_from_each_other.*");
}
#endif

Expand Down
135 changes: 64 additions & 71 deletions cpp/src/aztec3/circuits/rollup/merge/native_merge_rollup_circuit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,41 +27,64 @@ namespace aztec3::circuits::rollup::native_merge_rollup {
* @param mergeRollupInputs
* @return AggregationObject
*/
AggregationObject aggregate_proofs(MergeRollupInputs mergeRollupInputs)
AggregationObject aggregate_proofs(BaseOrMergeRollupPublicInputs left, BaseOrMergeRollupPublicInputs right)
{
// TODO: NOTE: for now we simply return the aggregation object from the first proof
return mergeRollupInputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.end_aggregation_object;
(void)right;
return left.end_aggregation_object;
}

void assert_both_input_proofs_of_same_rollup_type(MergeRollupInputs mergeRollupInputs)
/**
* @brief Asserts that the rollup types are the same
*
* @param left - The public inputs of the left rollup (base or merge)
* @param right - The public inputs of the right rollup (base or merge)
*/
void assert_both_input_proofs_of_same_rollup_type(BaseOrMergeRollupPublicInputs left,
BaseOrMergeRollupPublicInputs right)
{
assert(mergeRollupInputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.rollup_type ==
mergeRollupInputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.rollup_type);
(void)mergeRollupInputs;
assert(left.rollup_type == right.rollup_type);
(void)left;
(void)right;
}

NT::fr assert_both_input_proofs_of_same_height_and_return(MergeRollupInputs mergeRollupInputs)
/**
* @brief Asserts that the rollup subtree heights are the same and returns the height
*
* @param left - The public inputs of the left rollup (base or merge)
* @param right - The public inputs of the right rollup (base or merge)
* @return NT::fr - The height of the rollup subtrees
*/
NT::fr assert_both_input_proofs_of_same_height_and_return(BaseOrMergeRollupPublicInputs left,
BaseOrMergeRollupPublicInputs right)
{
assert(mergeRollupInputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.rollup_subtree_height ==
mergeRollupInputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.rollup_subtree_height);
return mergeRollupInputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.rollup_subtree_height;
assert(left.rollup_subtree_height == right.rollup_subtree_height);
(void)left;
(void)right;
return left.rollup_subtree_height;
}

void assert_equal_constants(ConstantRollupData left, ConstantRollupData right)
/**
* @brief Asserts that the constants used in the left and right child are identical
*
* @param left - The public inputs of the left rollup (base or merge)
* @param right - The public inputs of the right rollup (base or merge)
*/
void assert_equal_constants(BaseOrMergeRollupPublicInputs left, BaseOrMergeRollupPublicInputs right)
{
assert(left == right);
assert(left.constants == right.constants);
(void)left;
(void)right;
}

// function that does sha256 hash of the calldata from each previous rollup data
std::array<fr, 2> compute_calldata_hash(MergeRollupInputs mergeRollupInputs)
// Generates a 512 bit input from right and left 256 bit hashes. Then computes the sha256, and splits the hash into two
// field elements, a high and a low that is returned.
std::array<fr, 2> compute_calldata_hash(std::array<abis::PreviousRollupData<NT>, 2> previous_rollup_data)
{
// Compute the calldata hash
// Generate a 512 bit input from right and left 256 bit hashes
std::array<uint8_t, 2 * 32> calldata_hash_input_bytes;
for (uint8_t i = 0; i < 2; i++) {
std::array<fr, 2> calldata_hash_fr =
mergeRollupInputs.previous_rollup_data[i].base_or_merge_rollup_public_inputs.calldata_hash;
std::array<fr, 2> calldata_hash_fr = previous_rollup_data[i].base_or_merge_rollup_public_inputs.calldata_hash;

auto high_buffer = calldata_hash_fr[0].to_buffer();
auto low_buffer = calldata_hash_fr[1].to_buffer();
Expand All @@ -72,9 +95,9 @@ std::array<fr, 2> compute_calldata_hash(MergeRollupInputs mergeRollupInputs)
}
}

// Compute the sha256
std::vector<uint8_t> calldata_hash_input_bytes_vec(calldata_hash_input_bytes.begin(),
calldata_hash_input_bytes.end());

auto h = sha256::sha256(calldata_hash_input_bytes_vec);

// Split the hash into two fields, a high and a low
Expand All @@ -92,79 +115,49 @@ std::array<fr, 2> compute_calldata_hash(MergeRollupInputs mergeRollupInputs)
}

// asserts that the end snapshot of previous_rollup 0 equals the start snapshot of previous_rollup 1 (i.e. ensure they
// follow on from one-another).
void ensure_prev_rollups_follow_on_from_each_other(MergeRollupInputs mergeRollupInputs)
// follow on from one-another). Ensures that right uses the tres that was updated by left.
void assert_prev_rollups_follow_on_from_each_other(BaseOrMergeRollupPublicInputs left,
BaseOrMergeRollupPublicInputs right)
{
auto privateDataEndSnapshot0 =
mergeRollupInputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.end_private_data_tree_snapshot;
auto privateDataStartSnapshot1 =
mergeRollupInputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.start_private_data_tree_snapshot;

auto nullifierEndSnapshot0 =
mergeRollupInputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.end_nullifier_tree_snapshot;
auto nullifierStartSnapshot1 =
mergeRollupInputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.start_nullifier_tree_snapshot;

auto contractEndSnapshot0 =
mergeRollupInputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.end_contract_tree_snapshot;
auto contractStartSnapshot1 =
mergeRollupInputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.start_contract_tree_snapshot;

assert(privateDataEndSnapshot0 == privateDataStartSnapshot1 && nullifierEndSnapshot0 == nullifierStartSnapshot1 &&
contractEndSnapshot0 == contractStartSnapshot1);
assert(left.end_private_data_tree_snapshot == right.start_private_data_tree_snapshot);
assert(left.end_nullifier_tree_snapshot == right.start_nullifier_tree_snapshot);
assert(left.end_contract_tree_snapshot == right.start_contract_tree_snapshot);
// void variables since despite using in assert, it says, "unused variable"
(void)privateDataEndSnapshot0;
(void)privateDataStartSnapshot1;
(void)nullifierEndSnapshot0;
(void)nullifierStartSnapshot1;
(void)contractEndSnapshot0;
(void)contractStartSnapshot1;
(void)left;
(void)right;
}

BaseOrMergeRollupPublicInputs merge_rollup_circuit(MergeRollupInputs mergeRollupInputs)
{
// Verify the previous rollup proofs

// check that both input proofs are either both "BASE" or "MERGE" and not a mix!
// this prevents having wonky commitment, nullifier and contract subtrees.
assert_both_input_proofs_of_same_rollup_type(mergeRollupInputs);
auto current_height = assert_both_input_proofs_of_same_height_and_return(mergeRollupInputs);

// TODO: Verify the previous rollup proofs
// TODO: Check both previous rollup vks (in previous_rollup_data) against the permitted set of kernel vks.
// we don't have a set of permitted kernel vks yet.

// Check that the constants are the same in both proofs
auto left = mergeRollupInputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs;
auto right = mergeRollupInputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs;
assert_equal_constants(left.constants, right.constants);

// Ensure the end snapshot of previous_rollup 0 equals the start snapshot of previous_rollup 1 (i.e. ensure they
// follow on from one-another). This ensures the low_leaves which were updated in rollup 0 are being used as the
// 'starting' pointers in rollup 1.
ensure_prev_rollups_follow_on_from_each_other(mergeRollupInputs);
// check that both input proofs are either both "BASE" or "MERGE" and not a mix!
// this prevents having wonky commitment, nullifier and contract subtrees.
AggregationObject aggregation_object = aggregate_proofs(left, right);
assert_both_input_proofs_of_same_rollup_type(left, right);
auto current_height = assert_both_input_proofs_of_same_height_and_return(left, right);
assert_equal_constants(left, right);
assert_prev_rollups_follow_on_from_each_other(left, right);

// compute calldata hash:
auto new_calldata_hash = compute_calldata_hash(mergeRollupInputs);

AggregationObject aggregation_object = aggregate_proofs(mergeRollupInputs);
auto new_calldata_hash = compute_calldata_hash(mergeRollupInputs.previous_rollup_data);

BaseOrMergeRollupPublicInputs public_inputs = {
.rollup_type = abis::MERGE_ROLLUP_TYPE,
.rollup_subtree_height = current_height + 1,
.end_aggregation_object = aggregation_object,
.constants = left.constants,
.start_private_data_tree_snapshot = mergeRollupInputs.previous_rollup_data[0]
.base_or_merge_rollup_public_inputs.start_private_data_tree_snapshot,
.end_private_data_tree_snapshot =
mergeRollupInputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.end_private_data_tree_snapshot,
.start_nullifier_tree_snapshot =
mergeRollupInputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.start_nullifier_tree_snapshot,
.end_nullifier_tree_snapshot =
mergeRollupInputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.end_nullifier_tree_snapshot,
.start_contract_tree_snapshot =
mergeRollupInputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.start_contract_tree_snapshot,
.end_contract_tree_snapshot =
mergeRollupInputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.end_contract_tree_snapshot,
.start_private_data_tree_snapshot = left.start_private_data_tree_snapshot,
.end_private_data_tree_snapshot = right.end_private_data_tree_snapshot,
.start_nullifier_tree_snapshot = left.start_nullifier_tree_snapshot,
.end_nullifier_tree_snapshot = right.end_nullifier_tree_snapshot,
.start_contract_tree_snapshot = left.start_contract_tree_snapshot,
.end_contract_tree_snapshot = right.end_contract_tree_snapshot,
.calldata_hash = new_calldata_hash,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,10 @@ namespace aztec3::circuits::rollup::native_merge_rollup {

BaseOrMergeRollupPublicInputs merge_rollup_circuit(MergeRollupInputs mergeRollupInputs);

std::array<fr, 2> compute_calldata_hash(std::array<abis::PreviousRollupData<NT>, 2> previous_rollup_data);
void assert_prev_rollups_follow_on_from_each_other(BaseOrMergeRollupPublicInputs left, BaseOrMergeRollupPublicInputs right);
void assert_both_input_proofs_of_same_rollup_type(BaseOrMergeRollupPublicInputs left, BaseOrMergeRollupPublicInputs right);
NT::fr assert_both_input_proofs_of_same_height_and_return(BaseOrMergeRollupPublicInputs left, BaseOrMergeRollupPublicInputs right);
void assert_equal_constants(BaseOrMergeRollupPublicInputs left, BaseOrMergeRollupPublicInputs right);
AggregationObject aggregate_proofs(BaseOrMergeRollupPublicInputs left, BaseOrMergeRollupPublicInputs right);
} // namespace aztec3::circuits::rollup::native_merge_rollup
Loading

0 comments on commit fdd7a35

Please sign in to comment.