From 7022041def2354d35e2dbb97701b6a9011c94597 Mon Sep 17 00:00:00 2001 From: Rahul Kothari Date: Tue, 4 Apr 2023 10:11:05 +0000 Subject: [PATCH 1/4] add rollup subtree height --- .../rollup/base/base_or_merge_rollup_public_inputs.hpp | 8 ++++++++ .../circuits/rollup/base/native_base_rollup_circuit.cpp | 1 + 2 files changed, 9 insertions(+) diff --git a/cpp/src/aztec3/circuits/abis/rollup/base/base_or_merge_rollup_public_inputs.hpp b/cpp/src/aztec3/circuits/abis/rollup/base/base_or_merge_rollup_public_inputs.hpp index 88923618..e5a1042b 100644 --- a/cpp/src/aztec3/circuits/abis/rollup/base/base_or_merge_rollup_public_inputs.hpp +++ b/cpp/src/aztec3/circuits/abis/rollup/base/base_or_merge_rollup_public_inputs.hpp @@ -27,6 +27,9 @@ template struct BaseOrMergeRollupPublicInputs { AggregationObject end_aggregation_object; ConstantRollupData constants; + // subtree height is always 0 for base. + // so that we always pass-in two base/merge circuits of the same height into the next level of recursion + fr rollup_subtree_height; AppendOnlyTreeSnapshot start_private_data_tree_snapshot; AppendOnlyTreeSnapshot end_private_data_tree_snapshot; @@ -52,6 +55,7 @@ template void read(uint8_t const*& it, BaseOrMergeRollupPublicInp read(it, obj.rollup_subtree_height); read(it, obj.end_aggregation_object); read(it, obj.constants); + read(it, obj.rollup_subtree_height); read(it, obj.start_private_data_tree_snapshot); read(it, obj.end_private_data_tree_snapshot); read(it, obj.start_nullifier_tree_snapshot); @@ -69,6 +73,7 @@ template void write(std::vector& buf, BaseOrMergeRollupP write(buf, obj.rollup_subtree_height); write(buf, obj.end_aggregation_object); write(buf, obj.constants); + write(buf, obj.rollup_subtree_height); write(buf, obj.start_private_data_tree_snapshot); write(buf, obj.end_private_data_tree_snapshot); write(buf, obj.start_nullifier_tree_snapshot); @@ -89,6 +94,9 @@ template std::ostream& operator<<(std::ostream& os, BaseOrMergeRo << "\n" "constants:\n" << obj.constants + << "\n" + "rollup_subtree_height:\n" + << obj.rollup_subtree_height << "\n" "start_private_data_tree_snapshot:\n" << obj.start_private_data_tree_snapshot diff --git a/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.cpp b/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.cpp index 8d249eea..b9077829 100644 --- a/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.cpp +++ b/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.cpp @@ -521,6 +521,7 @@ BaseOrMergeRollupPublicInputs base_rollup_circuit(DummyComposer& composer, BaseR .rollup_subtree_height = fr(0), .end_aggregation_object = aggregation_object, .constants = baseRollupInputs.constants, + .rollup_subtree_height = fr(0), .start_private_data_tree_snapshot = baseRollupInputs.start_private_data_tree_snapshot, .end_private_data_tree_snapshot = end_private_data_tree_snapshot, .start_nullifier_tree_snapshot = baseRollupInputs.start_nullifier_tree_snapshot, From 675a994fdf9ed4efae43a2172f76fa1e11dca940 Mon Sep 17 00:00:00 2001 From: Rahul Kothari Date: Wed, 5 Apr 2023 16:36:33 +0100 Subject: [PATCH 2/4] just use one type of public inputs for base/merge (#168) --- .../rollup/base/base_or_merge_rollup_public_inputs.hpp | 8 -------- .../circuits/rollup/base/native_base_rollup_circuit.cpp | 1 - 2 files changed, 9 deletions(-) diff --git a/cpp/src/aztec3/circuits/abis/rollup/base/base_or_merge_rollup_public_inputs.hpp b/cpp/src/aztec3/circuits/abis/rollup/base/base_or_merge_rollup_public_inputs.hpp index e5a1042b..88923618 100644 --- a/cpp/src/aztec3/circuits/abis/rollup/base/base_or_merge_rollup_public_inputs.hpp +++ b/cpp/src/aztec3/circuits/abis/rollup/base/base_or_merge_rollup_public_inputs.hpp @@ -27,9 +27,6 @@ template struct BaseOrMergeRollupPublicInputs { AggregationObject end_aggregation_object; ConstantRollupData constants; - // subtree height is always 0 for base. - // so that we always pass-in two base/merge circuits of the same height into the next level of recursion - fr rollup_subtree_height; AppendOnlyTreeSnapshot start_private_data_tree_snapshot; AppendOnlyTreeSnapshot end_private_data_tree_snapshot; @@ -55,7 +52,6 @@ template void read(uint8_t const*& it, BaseOrMergeRollupPublicInp read(it, obj.rollup_subtree_height); read(it, obj.end_aggregation_object); read(it, obj.constants); - read(it, obj.rollup_subtree_height); read(it, obj.start_private_data_tree_snapshot); read(it, obj.end_private_data_tree_snapshot); read(it, obj.start_nullifier_tree_snapshot); @@ -73,7 +69,6 @@ template void write(std::vector& buf, BaseOrMergeRollupP write(buf, obj.rollup_subtree_height); write(buf, obj.end_aggregation_object); write(buf, obj.constants); - write(buf, obj.rollup_subtree_height); write(buf, obj.start_private_data_tree_snapshot); write(buf, obj.end_private_data_tree_snapshot); write(buf, obj.start_nullifier_tree_snapshot); @@ -94,9 +89,6 @@ template std::ostream& operator<<(std::ostream& os, BaseOrMergeRo << "\n" "constants:\n" << obj.constants - << "\n" - "rollup_subtree_height:\n" - << obj.rollup_subtree_height << "\n" "start_private_data_tree_snapshot:\n" << obj.start_private_data_tree_snapshot diff --git a/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.cpp b/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.cpp index b9077829..8d249eea 100644 --- a/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.cpp +++ b/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.cpp @@ -521,7 +521,6 @@ BaseOrMergeRollupPublicInputs base_rollup_circuit(DummyComposer& composer, BaseR .rollup_subtree_height = fr(0), .end_aggregation_object = aggregation_object, .constants = baseRollupInputs.constants, - .rollup_subtree_height = fr(0), .start_private_data_tree_snapshot = baseRollupInputs.start_private_data_tree_snapshot, .end_private_data_tree_snapshot = end_private_data_tree_snapshot, .start_nullifier_tree_snapshot = baseRollupInputs.start_nullifier_tree_snapshot, From 9e0bf01db9ca357a9872c57ffa8d5754f93abc28 Mon Sep 17 00:00:00 2001 From: LHerskind Date: Thu, 6 Apr 2023 11:01:37 +0000 Subject: [PATCH 3/4] fix: re-usable rollup components --- .../base/native_base_rollup_circuit.cpp | 139 +++++------------- .../circuits/rollup/components/components.cpp | 128 ++++++++++++++++ .../circuits/rollup/components/components.hpp | 72 +++++++++ .../circuits/rollup/components/init.hpp | 29 ++++ .../merge/native_merge_rollup_circuit.cpp | 124 +--------------- .../merge/native_merge_rollup_circuit.hpp | 16 -- cpp/src/aztec3/circuits/rollup/root/init.hpp | 2 - .../root/native_root_rollup_circuit.cpp | 88 +++-------- 8 files changed, 295 insertions(+), 303 deletions(-) create mode 100644 cpp/src/aztec3/circuits/rollup/components/components.cpp create mode 100644 cpp/src/aztec3/circuits/rollup/components/components.hpp create mode 100644 cpp/src/aztec3/circuits/rollup/components/init.hpp diff --git a/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.cpp b/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.cpp index 8d249eea..9fac1b9b 100644 --- a/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.cpp +++ b/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.cpp @@ -7,6 +7,7 @@ #include "barretenberg/stdlib/merkle_tree/memory_tree.hpp" #include "barretenberg/stdlib/merkle_tree/merkle_tree.hpp" #include "init.hpp" +#include "aztec3/circuits/rollup/components/components.hpp" #include #include @@ -100,54 +101,7 @@ std::vector calculate_contract_leaves(BaseRollupInputs const& baseRollup return contract_leaves; } -template -NT::fr iterate_through_tree_via_sibling_path(NT::fr leaf, - NT::uint32 const& leafIndex, - std::array const& siblingPath) -{ - for (size_t i = 0; i < siblingPath.size(); i++) { - if (leafIndex & (1 << i)) { - leaf = proof_system::plonk::stdlib::merkle_tree::hash_pair_native(siblingPath[i], leaf); - } else { - leaf = proof_system::plonk::stdlib::merkle_tree::hash_pair_native(leaf, siblingPath[i]); - } - } - return leaf; -} - -template -void check_membership(DummyComposer& composer, - NT::fr const& leaf, - NT::uint32 const& leafIndex, - std::array const& siblingPath, - NT::fr const& root) -{ - auto calculatedRoot = iterate_through_tree_via_sibling_path(leaf, leafIndex, siblingPath); - // TODO: update tests to build the correct trees - composer.do_assert(calculatedRoot == root, "Membership check failed"); -} - -template -AppendOnlySnapshot insert_subtree_to_snapshot_tree(std::array const& siblingPath, - NT::uint32 const& nextAvailableLeafIndex, - NT::fr const& subtreeRootToInsert, - uint8_t const& subtreeDepth) -{ - // TODO: Sanity check len of siblingPath > height of subtree - // TODO: Ensure height of subtree is correct (eg 3 for commitments, 1 for contracts) - - // if index of leaf is x, index of its parent is x/2 or x >> 1. We need to find the parent `subtreeDepth` levels up. - auto leafIndexAtDepth = nextAvailableLeafIndex >> subtreeDepth; - auto new_root = iterate_through_tree_via_sibling_path(subtreeRootToInsert, leafIndexAtDepth, siblingPath); - // 2^subtreeDepth is the number of leaves added. 2^x = 1 << x - auto new_next_available_leaf_index = nextAvailableLeafIndex + (uint8_t(1) << subtreeDepth); - - AppendOnlySnapshot newTreeSnapshot = { .root = new_root, - .next_available_leaf_index = new_next_available_leaf_index }; - return newTreeSnapshot; -} - -NT::fr calculate_contract_subtree(std::vector const& contract_leaves) +NT::fr calculate_contract_subtree(std::vector contract_leaves) { MerkleTree contracts_tree = MerkleTree(CONTRACT_SUBTREE_DEPTH); @@ -260,7 +214,7 @@ void perform_historical_private_data_tree_membership_checks(DummyComposer& compo abis::MembershipWitness historic_root_witness = baseRollupInputs.historic_private_data_tree_root_membership_witnesses[i]; - check_membership( + components::check_membership( composer, leaf, historic_root_witness.leaf_index, historic_root_witness.sibling_path, historic_root); } } @@ -275,7 +229,7 @@ void perform_historical_contract_data_tree_membership_checks(DummyComposer& comp abis::MembershipWitness historic_root_witness = baseRollupInputs.historic_contract_tree_root_membership_witnesses[i]; - check_membership( + components::check_membership( composer, leaf, historic_root_witness.leaf_index, historic_root_witness.sibling_path, historic_root); } } @@ -389,11 +343,11 @@ AppendOnlySnapshot check_nullifier_tree_non_membership_and_insert_to_tree(DummyC }; // perform membership check for the low nullifier against the original root - check_membership(composer, - original_low_nullifier.hash(), - witness.leaf_index, - witness.sibling_path, - current_nullifier_tree_root); + components::check_membership(composer, + original_low_nullifier.hash(), + witness.leaf_index, + witness.sibling_path, + current_nullifier_tree_root); // Calculate the new value of the low_nullifier_leaf NullifierLeaf updated_low_nullifier = NullifierLeaf{ .value = low_nullifier_preimage.leaf_value, @@ -401,7 +355,7 @@ AppendOnlySnapshot check_nullifier_tree_non_membership_and_insert_to_tree(DummyC .nextValue = nullifier }; // We need another set of witness values for this - current_nullifier_tree_root = iterate_through_tree_via_sibling_path( + current_nullifier_tree_root = components::iterate_through_tree_via_sibling_path( updated_low_nullifier.hash(), witness.leaf_index, witness.sibling_path); } @@ -428,8 +382,8 @@ AppendOnlySnapshot check_nullifier_tree_non_membership_and_insert_to_tree(DummyC // Calculate the new root // We are inserting a subtree rather than a full tree here auto subtree_index = start_insertion_index >> (NULLIFIER_SUBTREE_DEPTH); - auto new_root = - iterate_through_tree_via_sibling_path(nullifier_subtree_root, subtree_index, nullifier_sibling_path); + auto new_root = components::iterate_through_tree_via_sibling_path( + nullifier_subtree_root, subtree_index, nullifier_sibling_path); // Return the new state of the nullifier tree return { @@ -453,57 +407,36 @@ BaseOrMergeRollupPublicInputs base_rollup_circuit(DummyComposer& composer, BaseR // First we compute the contract tree leaves std::vector contract_leaves = calculate_contract_leaves(baseRollupInputs); - // Perform merkle membership check with the provided sibling path up to the root - // Note - the subtree hasn't been created (i.e. it is empty) so you check that the sibling path corresponds to - // an empty tree - - // check for commitments/private_data - // next_available_leaf_index is at the leaf level. We need at the subtree level (say height 3). So divide by 8. - // (if leaf is at index x, its parent is at index floor >> depth) - auto leafIndexAtSubtreeDepth = - baseRollupInputs.start_private_data_tree_snapshot.next_available_leaf_index >> (PRIVATE_DATA_SUBTREE_DEPTH); - check_membership(composer, - EMPTY_COMMITMENTS_SUBTREE_ROOT, - leafIndexAtSubtreeDepth, - baseRollupInputs.new_commitments_subtree_sibling_path, - baseRollupInputs.start_private_data_tree_snapshot.root); - - // check for contracts - auto leafIndexContractsSubtreeDepth = - baseRollupInputs.start_contract_tree_snapshot.next_available_leaf_index >> CONTRACT_SUBTREE_DEPTH; - check_membership(composer, - EMPTY_CONTRACTS_SUBTREE_ROOT, - leafIndexContractsSubtreeDepth, - baseRollupInputs.new_contracts_subtree_sibling_path, - baseRollupInputs.start_contract_tree_snapshot.root); - - // check for nullifiers - auto leafIndexNullifierSubtreeDepth = - baseRollupInputs.start_nullifier_tree_snapshot.next_available_leaf_index >> NULLIFIER_SUBTREE_DEPTH; - check_membership(composer, - EMPTY_NULLIFIER_SUBTREE_ROOT, - leafIndexNullifierSubtreeDepth, - baseRollupInputs.new_nullifiers_subtree_sibling_path, - baseRollupInputs.start_nullifier_tree_snapshot.root); - // Check contracts and commitments subtrees NT::fr contracts_tree_subroot = calculate_contract_subtree(contract_leaves); NT::fr commitments_tree_subroot = calculate_commitments_subtree(composer, baseRollupInputs); - // Insert subtrees to the tree: + // Insert commitment subtrees: auto end_private_data_tree_snapshot = - insert_subtree_to_snapshot_tree(baseRollupInputs.new_commitments_subtree_sibling_path, - baseRollupInputs.start_private_data_tree_snapshot.next_available_leaf_index, - commitments_tree_subroot, - PRIVATE_DATA_SUBTREE_DEPTH); - + components::insert_subtree_to_snapshot_tree(composer, + baseRollupInputs.start_private_data_tree_snapshot, + baseRollupInputs.new_commitments_subtree_sibling_path, + EMPTY_COMMITMENTS_SUBTREE_ROOT, + commitments_tree_subroot, + PRIVATE_DATA_SUBTREE_DEPTH); + + // Insert contract subtrees: auto end_contract_tree_snapshot = - insert_subtree_to_snapshot_tree(baseRollupInputs.new_contracts_subtree_sibling_path, - baseRollupInputs.start_contract_tree_snapshot.next_available_leaf_index, - contracts_tree_subroot, - CONTRACT_SUBTREE_DEPTH); - - // Check nullifiers and check new subtree insertion + components::insert_subtree_to_snapshot_tree(composer, + baseRollupInputs.start_contract_tree_snapshot, + baseRollupInputs.new_contracts_subtree_sibling_path, + EMPTY_CONTRACTS_SUBTREE_ROOT, + contracts_tree_subroot, + CONTRACT_SUBTREE_DEPTH); + + // Update nullifier tree and insert new subtree + auto leafIndexNullifierSubtreeDepth = + baseRollupInputs.start_nullifier_tree_snapshot.next_available_leaf_index >> NULLIFIER_SUBTREE_DEPTH; + components::check_membership(composer, + EMPTY_NULLIFIER_SUBTREE_ROOT, + leafIndexNullifierSubtreeDepth, + baseRollupInputs.new_nullifiers_subtree_sibling_path, + baseRollupInputs.start_nullifier_tree_snapshot.root); AppendOnlySnapshot end_nullifier_tree_snapshot = check_nullifier_tree_non_membership_and_insert_to_tree(composer, baseRollupInputs); diff --git a/cpp/src/aztec3/circuits/rollup/components/components.cpp b/cpp/src/aztec3/circuits/rollup/components/components.cpp new file mode 100644 index 00000000..94f9c52c --- /dev/null +++ b/cpp/src/aztec3/circuits/rollup/components/components.cpp @@ -0,0 +1,128 @@ +#include "aztec3/circuits/abis/rollup/base/base_or_merge_rollup_public_inputs.hpp" +#include "aztec3/constants.hpp" +#include "barretenberg/crypto/pedersen_hash/pedersen.hpp" +#include "barretenberg/crypto/sha256/sha256.hpp" +#include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "barretenberg/stdlib/hash/pedersen/pedersen.hpp" +#include "init.hpp" + +#include +#include +#include +#include +#include +#include + +namespace aztec3::circuits::rollup::components { + +/** + * @brief Create an aggregation object for the proofs that are provided + * - We add points P0 for each of our proofs + * - We add points P1 for each of our proofs + * - We concat our public inputs + * + * @param mergeRollupInputs + * @return AggregationObject + */ +AggregationObject aggregate_proofs(BaseOrMergeRollupPublicInputs const& left, + BaseOrMergeRollupPublicInputs const& right) +{ + // TODO: NOTE: for now we simply return the aggregation object from the first proof + (void)right; + return left.end_aggregation_object; +} + +/** + * @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(DummyComposer& composer, + BaseOrMergeRollupPublicInputs const& left, + BaseOrMergeRollupPublicInputs const& right) +{ + composer.do_assert(left.rollup_type == right.rollup_type, "input proofs are of different rollup types"); +} + +/** + * @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(DummyComposer& composer, + BaseOrMergeRollupPublicInputs const& left, + BaseOrMergeRollupPublicInputs const& right) +{ + composer.do_assert(left.rollup_subtree_height == right.rollup_subtree_height, + "input proofs are of different rollup heights"); + return left.rollup_subtree_height; +} + +/** + * @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(DummyComposer& composer, + BaseOrMergeRollupPublicInputs const& left, + BaseOrMergeRollupPublicInputs const& right) +{ + composer.do_assert(left.constants == right.constants, "input proofs have different constants"); +} + +// 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 compute_calldata_hash(std::array, 2> previous_rollup_data) +{ + // Generate a 512 bit input from right and left 256 bit hashes + std::array calldata_hash_input_bytes; + for (uint8_t i = 0; i < 2; i++) { + std::array 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(); + + for (uint8_t j = 0; j < 16; ++j) { + calldata_hash_input_bytes[i * 32 + j] = high_buffer[16 + j]; + calldata_hash_input_bytes[i * 32 + 16 + j] = low_buffer[16 + j]; + } + } + + // Compute the sha256 + std::vector 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 + std::array buf_1, buf_2; + for (uint8_t i = 0; i < 16; i++) { + buf_1[i] = 0; + buf_1[16 + i] = h[i]; + buf_2[i] = 0; + buf_2[16 + i] = h[i + 16]; + } + auto high = fr::serialize_from_buffer(buf_1.data()); + auto low = fr::serialize_from_buffer(buf_2.data()); + + return { high, low }; +} + +// 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). Ensures that right uses the tres that was updated by left. +void assert_prev_rollups_follow_on_from_each_other(DummyComposer& composer, + BaseOrMergeRollupPublicInputs const& left, + BaseOrMergeRollupPublicInputs const& right) +{ + composer.do_assert(left.end_private_data_tree_snapshot == right.start_private_data_tree_snapshot, + "input proofs have different private data tree snapshots"); + composer.do_assert(left.end_nullifier_tree_snapshot == right.start_nullifier_tree_snapshot, + "input proofs have different nullifier tree snapshots"); + composer.do_assert(left.end_contract_tree_snapshot == right.start_contract_tree_snapshot, + "input proofs have different contract tree snapshots"); +} + +} // namespace aztec3::circuits::rollup::components \ No newline at end of file diff --git a/cpp/src/aztec3/circuits/rollup/components/components.hpp b/cpp/src/aztec3/circuits/rollup/components/components.hpp new file mode 100644 index 00000000..170129f3 --- /dev/null +++ b/cpp/src/aztec3/circuits/rollup/components/components.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include "init.hpp" + +namespace aztec3::circuits::rollup::components { +std::array compute_calldata_hash(std::array, 2> previous_rollup_data); +void assert_prev_rollups_follow_on_from_each_other(DummyComposer& composer, + BaseOrMergeRollupPublicInputs const& left, + BaseOrMergeRollupPublicInputs const& right); +void assert_both_input_proofs_of_same_rollup_type(DummyComposer& composer, + BaseOrMergeRollupPublicInputs const& left, + BaseOrMergeRollupPublicInputs const& right); +NT::fr assert_both_input_proofs_of_same_height_and_return(DummyComposer& composer, + BaseOrMergeRollupPublicInputs const& left, + BaseOrMergeRollupPublicInputs const& right); +void assert_equal_constants(DummyComposer& composer, + BaseOrMergeRollupPublicInputs const& left, + BaseOrMergeRollupPublicInputs const& right); + +AggregationObject aggregate_proofs(BaseOrMergeRollupPublicInputs const& left, BaseOrMergeRollupPublicInputs const& right); + +template +NT::fr iterate_through_tree_via_sibling_path(NT::fr leaf, NT::uint32 leafIndex, std::array siblingPath) +{ + for (size_t i = 0; i < siblingPath.size(); i++) { + if (leafIndex & (1 << i)) { + leaf = crypto::pedersen_hash::hash_multiple({ siblingPath[i], leaf }); + } else { + leaf = crypto::pedersen_hash::hash_multiple({ leaf, siblingPath[i] }); + } + } + return leaf; +} + +template +void check_membership(DummyComposer& composer, + NT::fr const& leaf, + NT::uint32 const& leafIndex, + std::array const& siblingPath, + NT::fr const& root) +{ + auto calculatedRoot = iterate_through_tree_via_sibling_path(leaf, leafIndex, siblingPath); + // TODO: update tests to build the correct trees + composer.do_assert(calculatedRoot == root, "Membership check failed"); +} + +template +AppendOnlySnapshot insert_subtree_to_snapshot_tree(DummyComposer& composer, + AppendOnlySnapshot snapshot, + std::array siblingPath, + NT::fr emptySubtreeRoot, + NT::fr subtreeRootToInsert, + uint8_t subtreeDepth) +{ + // TODO: Sanity check len of siblingPath > height of subtree + // TODO: Ensure height of subtree is correct (eg 3 for commitments, 1 for contracts) + auto leafIndexAtDepth = snapshot.next_available_leaf_index >> subtreeDepth; + + // Check that the current root is correct and that there is an empty subtree at the insertion location + check_membership(composer, emptySubtreeRoot, leafIndexAtDepth, siblingPath, snapshot.root); + + // if index of leaf is x, index of its parent is x/2 or x >> 1. We need to find the parent `subtreeDepth` levels up. + auto new_root = iterate_through_tree_via_sibling_path(subtreeRootToInsert, leafIndexAtDepth, siblingPath); + + // 2^subtreeDepth is the number of leaves added. 2^x = 1 << x + auto new_next_available_leaf_index = snapshot.next_available_leaf_index + (uint8_t(1) << subtreeDepth); + + AppendOnlySnapshot newTreeSnapshot = { .root = new_root, + .next_available_leaf_index = new_next_available_leaf_index }; + return newTreeSnapshot; +} +} // namespace aztec3::circuits::rollup::components \ No newline at end of file diff --git a/cpp/src/aztec3/circuits/rollup/components/init.hpp b/cpp/src/aztec3/circuits/rollup/components/init.hpp new file mode 100644 index 00000000..77f8138d --- /dev/null +++ b/cpp/src/aztec3/circuits/rollup/components/init.hpp @@ -0,0 +1,29 @@ + +#pragma once + +#include "aztec3/circuits/abis/append_only_tree_snapshot.hpp" +#include "aztec3/circuits/abis/rollup/constant_rollup_data.hpp" +#include "aztec3/circuits/abis/rollup/base/base_or_merge_rollup_public_inputs.hpp" +#include "aztec3/circuits/abis/rollup/merge/merge_rollup_inputs.hpp" +#include +#include +#include "aztec3/utils/dummy_composer.hpp" + +#include +#include +#include +#include +#include + +namespace aztec3::circuits::rollup::components { + +using NT = aztec3::utils::types::NativeTypes; + +// Params +using NT = aztec3::utils::types::NativeTypes; +using AggregationObject = aztec3::utils::types::NativeTypes::AggregationObject; +using BaseOrMergeRollupPublicInputs = aztec3::circuits::abis::BaseOrMergeRollupPublicInputs; +using AppendOnlySnapshot = abis::AppendOnlyTreeSnapshot; +using DummyComposer = aztec3::utils::DummyComposer; + +} // namespace aztec3::circuits::rollup::components \ No newline at end of file diff --git a/cpp/src/aztec3/circuits/rollup/merge/native_merge_rollup_circuit.cpp b/cpp/src/aztec3/circuits/rollup/merge/native_merge_rollup_circuit.cpp index 1d70f858..4368235f 100644 --- a/cpp/src/aztec3/circuits/rollup/merge/native_merge_rollup_circuit.cpp +++ b/cpp/src/aztec3/circuits/rollup/merge/native_merge_rollup_circuit.cpp @@ -9,6 +9,8 @@ #include "barretenberg/stdlib/merkle_tree/merkle_tree.hpp" #include "init.hpp" +#include + #include #include #include @@ -18,116 +20,6 @@ namespace aztec3::circuits::rollup::native_merge_rollup { -/** - * @brief Create an aggregation object for the proofs that are provided - * - We add points P0 for each of our proofs - * - We add points P1 for each of our proofs - * - We concat our public inputs - * - * @param mergeRollupInputs - * @return AggregationObject - */ -AggregationObject aggregate_proofs(BaseOrMergeRollupPublicInputs const& left, - BaseOrMergeRollupPublicInputs const& right) -{ - // TODO: NOTE: for now we simply return the aggregation object from the first proof - (void)right; - return left.end_aggregation_object; -} - -/** - * @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(DummyComposer& composer, - BaseOrMergeRollupPublicInputs const& left, - BaseOrMergeRollupPublicInputs const& right) -{ - composer.do_assert(left.rollup_type == right.rollup_type, "input proofs are of different rollup types"); -} - -/** - * @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(DummyComposer& composer, - BaseOrMergeRollupPublicInputs const& left, - BaseOrMergeRollupPublicInputs const& right) -{ - composer.do_assert(left.rollup_subtree_height == right.rollup_subtree_height, - "input proofs are of different rollup heights"); - return left.rollup_subtree_height; -} - -/** - * @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(DummyComposer& composer, - BaseOrMergeRollupPublicInputs const& left, - BaseOrMergeRollupPublicInputs const& right) -{ - composer.do_assert(left.constants == right.constants, "input proofs have different constants"); -} - -// 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 compute_calldata_hash(std::array, 2> const& previous_rollup_data) -{ - // Generate a 512 bit input from right and left 256 bit hashes - std::array calldata_hash_input_bytes; - for (uint8_t i = 0; i < 2; i++) { - std::array 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(); - - for (uint8_t j = 0; j < 16; ++j) { - calldata_hash_input_bytes[i * 32 + j] = high_buffer[16 + j]; - calldata_hash_input_bytes[i * 32 + 16 + j] = low_buffer[16 + j]; - } - } - - // Compute the sha256 - std::vector 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 - std::array buf_1, buf_2; - for (uint8_t i = 0; i < 16; i++) { - buf_1[i] = 0; - buf_1[16 + i] = h[i]; - buf_2[i] = 0; - buf_2[16 + i] = h[i + 16]; - } - auto high = fr::serialize_from_buffer(buf_1.data()); - auto low = fr::serialize_from_buffer(buf_2.data()); - - return { high, low }; -} - -// 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). Ensures that right uses the tres that was updated by left. -void assert_prev_rollups_follow_on_from_each_other(DummyComposer& composer, - BaseOrMergeRollupPublicInputs const& left, - BaseOrMergeRollupPublicInputs const& right) -{ - composer.do_assert(left.end_private_data_tree_snapshot == right.start_private_data_tree_snapshot, - "input proofs have different private data tree snapshots"); - composer.do_assert(left.end_nullifier_tree_snapshot == right.start_nullifier_tree_snapshot, - "input proofs have different nullifier tree snapshots"); - composer.do_assert(left.end_contract_tree_snapshot == right.start_contract_tree_snapshot, - "input proofs have different contract tree snapshots"); -} - BaseOrMergeRollupPublicInputs merge_rollup_circuit(DummyComposer& composer, MergeRollupInputs const& mergeRollupInputs) { // TODO: Verify the previous rollup proofs @@ -139,14 +31,14 @@ BaseOrMergeRollupPublicInputs merge_rollup_circuit(DummyComposer& composer, Merg // 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(composer, left, right); - auto current_height = assert_both_input_proofs_of_same_height_and_return(composer, left, right); - assert_equal_constants(composer, left, right); - assert_prev_rollups_follow_on_from_each_other(composer, left, right); + AggregationObject aggregation_object = components::aggregate_proofs(left, right); + components::assert_both_input_proofs_of_same_rollup_type(composer, left, right); + auto current_height = components::assert_both_input_proofs_of_same_height_and_return(composer, left, right); + components::assert_equal_constants(composer, left, right); + components::assert_prev_rollups_follow_on_from_each_other(composer, left, right); // compute calldata hash: - auto new_calldata_hash = compute_calldata_hash(mergeRollupInputs.previous_rollup_data); + auto new_calldata_hash = components::compute_calldata_hash(mergeRollupInputs.previous_rollup_data); BaseOrMergeRollupPublicInputs public_inputs = { .rollup_type = abis::MERGE_ROLLUP_TYPE, diff --git a/cpp/src/aztec3/circuits/rollup/merge/native_merge_rollup_circuit.hpp b/cpp/src/aztec3/circuits/rollup/merge/native_merge_rollup_circuit.hpp index a7bda0ec..ec7f5097 100644 --- a/cpp/src/aztec3/circuits/rollup/merge/native_merge_rollup_circuit.hpp +++ b/cpp/src/aztec3/circuits/rollup/merge/native_merge_rollup_circuit.hpp @@ -13,20 +13,4 @@ namespace aztec3::circuits::rollup::native_merge_rollup { BaseOrMergeRollupPublicInputs merge_rollup_circuit(DummyComposer& composer, MergeRollupInputs const& mergeRollupInputs); - -std::array compute_calldata_hash(std::array, 2> const& previous_rollup_data); -void assert_prev_rollups_follow_on_from_each_other(DummyComposer& composer, - BaseOrMergeRollupPublicInputs const& left, - BaseOrMergeRollupPublicInputs const& right); -void assert_both_input_proofs_of_same_rollup_type(DummyComposer& composer, - BaseOrMergeRollupPublicInputs const& left, - BaseOrMergeRollupPublicInputs const& right); -NT::fr assert_both_input_proofs_of_same_height_and_return(DummyComposer& composer, - BaseOrMergeRollupPublicInputs const& left, - BaseOrMergeRollupPublicInputs const& right); -void assert_equal_constants(DummyComposer& composer, - BaseOrMergeRollupPublicInputs const& left, - BaseOrMergeRollupPublicInputs const& right); -AggregationObject aggregate_proofs(BaseOrMergeRollupPublicInputs const& left, - BaseOrMergeRollupPublicInputs const& right); } // namespace aztec3::circuits::rollup::native_merge_rollup \ No newline at end of file diff --git a/cpp/src/aztec3/circuits/rollup/root/init.hpp b/cpp/src/aztec3/circuits/rollup/root/init.hpp index bd8f1e9b..5b8776ca 100644 --- a/cpp/src/aztec3/circuits/rollup/root/init.hpp +++ b/cpp/src/aztec3/circuits/rollup/root/init.hpp @@ -26,7 +26,5 @@ using RootRollupInputs = abis::RootRollupInputs; using RootRollupPublicInputs = abis::RootRollupPublicInputs; using Aggregator = aztec3::circuits::recursion::Aggregator; -using AggregationObject = utils::types::NativeTypes::AggregationObject; -using AppendOnlySnapshot = abis::AppendOnlyTreeSnapshot; } // namespace aztec3::circuits::rollup::native_root_rollup \ No newline at end of file diff --git a/cpp/src/aztec3/circuits/rollup/root/native_root_rollup_circuit.cpp b/cpp/src/aztec3/circuits/rollup/root/native_root_rollup_circuit.cpp index 10adc7f1..75e07a4e 100644 --- a/cpp/src/aztec3/circuits/rollup/root/native_root_rollup_circuit.cpp +++ b/cpp/src/aztec3/circuits/rollup/root/native_root_rollup_circuit.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -26,55 +26,7 @@ namespace aztec3::circuits::rollup::native_root_rollup { // Access Native types through NT namespace -template -NT::fr iterate_through_tree_via_sibling_path(NT::fr leaf, - NT::uint32 const& leafIndex, - std::array const& siblingPath) -{ - for (size_t i = 0; i < siblingPath.size(); i++) { - if (leafIndex & (1 << i)) { - leaf = proof_system::plonk::stdlib::merkle_tree::hash_pair_native(siblingPath[i], leaf); - } else { - leaf = proof_system::plonk::stdlib::merkle_tree::hash_pair_native(leaf, siblingPath[i]); - } - } - return leaf; -} - -template -void check_membership(DummyComposer& composer, - NT::fr const& leaf, - NT::uint32 const& leafIndex, - std::array const& siblingPath, - NT::fr const& root) -{ - auto computed_root = iterate_through_tree_via_sibling_path(leaf, leafIndex, siblingPath); - composer.do_assert(root == computed_root, "Membership check failed"); -} - -template -AppendOnlySnapshot insert_at_empty_in_snapshot_tree(DummyComposer& composer, - AppendOnlySnapshot const& old_snapshot, - std::array const& siblingPath, - NT::fr leaf) -{ - // check that the value is zero at the path (unused) - // TODO: We should be able to actually skip this, because the contract will be indirectly enforce it through - // old_snapshot.next_available_leaf_index - check_membership(composer, fr::zero(), old_snapshot.next_available_leaf_index, siblingPath, old_snapshot.root); - - // Compute the new root after the update - auto new_root = iterate_through_tree_via_sibling_path(leaf, old_snapshot.next_available_leaf_index, siblingPath); - - return { .root = new_root, .next_available_leaf_index = old_snapshot.next_available_leaf_index + 1 }; -} - -// Important types: -// - BaseRollupPublicInputs - where we want to put our return values -// -// TODO: replace auto -RootRollupPublicInputs root_rollup_circuit(aztec3::utils::DummyComposer& composer, - RootRollupInputs const& rootRollupInputs) +RootRollupPublicInputs root_rollup_circuit(DummyComposer& composer, RootRollupInputs const& rootRollupInputs) { // TODO: Verify the previous rollup proofs // TODO: Check both previous rollup vks (in previous_rollup_data) against the permitted set of kernel vks. @@ -83,25 +35,29 @@ RootRollupPublicInputs root_rollup_circuit(aztec3::utils::DummyComposer& compose auto left = rootRollupInputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs; auto right = rootRollupInputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs; - AggregationObject aggregation_object = native_merge_rollup::aggregate_proofs(left, right); - native_merge_rollup::assert_both_input_proofs_of_same_rollup_type(composer, left, right); - native_merge_rollup::assert_both_input_proofs_of_same_height_and_return(composer, left, right); - native_merge_rollup::assert_equal_constants(composer, left, right); - native_merge_rollup::assert_prev_rollups_follow_on_from_each_other(composer, left, right); + auto aggregation_object = components::aggregate_proofs(left, right); + components::assert_both_input_proofs_of_same_rollup_type(composer, left, right); + components::assert_both_input_proofs_of_same_height_and_return(composer, left, right); + components::assert_equal_constants(composer, left, right); + components::assert_prev_rollups_follow_on_from_each_other(composer, left, right); // Update the historic private data tree - AppendOnlySnapshot end_tree_of_historic_private_data_tree_roots_snapshot = - insert_at_empty_in_snapshot_tree(composer, - left.constants.start_tree_of_historic_private_data_tree_roots_snapshot, - rootRollupInputs.new_historic_private_data_tree_root_sibling_path, - right.end_private_data_tree_snapshot.root); + auto end_tree_of_historic_private_data_tree_roots_snapshot = components::insert_subtree_to_snapshot_tree( + composer, + left.constants.start_tree_of_historic_private_data_tree_roots_snapshot, + rootRollupInputs.new_historic_private_data_tree_root_sibling_path, + fr::zero(), + right.end_private_data_tree_snapshot.root, + 0); // Update the historic private data tree - AppendOnlySnapshot end_tree_of_historic_contract_tree_roots_snapshot = - insert_at_empty_in_snapshot_tree(composer, - left.constants.start_tree_of_historic_contract_tree_roots_snapshot, - rootRollupInputs.new_historic_contract_tree_root_sibling_path, - right.end_contract_tree_snapshot.root); + auto end_tree_of_historic_contract_tree_roots_snapshot = + components::insert_subtree_to_snapshot_tree(composer, + left.constants.start_tree_of_historic_contract_tree_roots_snapshot, + rootRollupInputs.new_historic_contract_tree_root_sibling_path, + fr::zero(), + right.end_contract_tree_snapshot.root, + 0); RootRollupPublicInputs public_inputs = { .end_aggregation_object = aggregation_object, @@ -117,7 +73,7 @@ RootRollupPublicInputs root_rollup_circuit(aztec3::utils::DummyComposer& compose .start_tree_of_historic_contract_tree_roots_snapshot = left.constants.start_tree_of_historic_contract_tree_roots_snapshot, .end_tree_of_historic_contract_tree_roots_snapshot = end_tree_of_historic_contract_tree_roots_snapshot, - .calldata_hash = native_merge_rollup::compute_calldata_hash(rootRollupInputs.previous_rollup_data), + .calldata_hash = components::compute_calldata_hash(rootRollupInputs.previous_rollup_data), }; return public_inputs; From 8f361a4327a9ff6c519c0fdf3a3e8472bfa225ef Mon Sep 17 00:00:00 2001 From: LHerskind Date: Wed, 12 Apr 2023 15:43:00 +0000 Subject: [PATCH 4/4] fix: hash_multiple -> hash_pair_native --- cpp/src/aztec3/circuits/rollup/components/components.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cpp/src/aztec3/circuits/rollup/components/components.hpp b/cpp/src/aztec3/circuits/rollup/components/components.hpp index 170129f3..f19d623b 100644 --- a/cpp/src/aztec3/circuits/rollup/components/components.hpp +++ b/cpp/src/aztec3/circuits/rollup/components/components.hpp @@ -17,16 +17,17 @@ void assert_equal_constants(DummyComposer& composer, BaseOrMergeRollupPublicInputs const& left, BaseOrMergeRollupPublicInputs const& right); -AggregationObject aggregate_proofs(BaseOrMergeRollupPublicInputs const& left, BaseOrMergeRollupPublicInputs const& right); +AggregationObject aggregate_proofs(BaseOrMergeRollupPublicInputs const& left, + BaseOrMergeRollupPublicInputs const& right); template NT::fr iterate_through_tree_via_sibling_path(NT::fr leaf, NT::uint32 leafIndex, std::array siblingPath) { for (size_t i = 0; i < siblingPath.size(); i++) { if (leafIndex & (1 << i)) { - leaf = crypto::pedersen_hash::hash_multiple({ siblingPath[i], leaf }); + leaf = proof_system::plonk::stdlib::merkle_tree::hash_pair_native(siblingPath[i], leaf); } else { - leaf = crypto::pedersen_hash::hash_multiple({ leaf, siblingPath[i] }); + leaf = proof_system::plonk::stdlib::merkle_tree::hash_pair_native(leaf, siblingPath[i]); } } return leaf;