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

Reusable rollup components #183

Merged
merged 4 commits into from
Apr 12, 2023
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
139 changes: 36 additions & 103 deletions cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <algorithm>
#include <array>
Expand Down Expand Up @@ -100,54 +101,7 @@ std::vector<NT::fr> calculate_contract_leaves(BaseRollupInputs const& baseRollup
return contract_leaves;
}

template <size_t N>
NT::fr iterate_through_tree_via_sibling_path(NT::fr leaf,
NT::uint32 const& leafIndex,
std::array<NT::fr, N> 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 <size_t N>
void check_membership(DummyComposer& composer,
NT::fr const& leaf,
NT::uint32 const& leafIndex,
std::array<NT::fr, N> 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 <size_t N>
AppendOnlySnapshot insert_subtree_to_snapshot_tree(std::array<NT::fr, N> 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<NT::fr> const& contract_leaves)
NT::fr calculate_contract_subtree(std::vector<NT::fr> contract_leaves)
{
MerkleTree contracts_tree = MerkleTree(CONTRACT_SUBTREE_DEPTH);

Expand Down Expand Up @@ -260,7 +214,7 @@ void perform_historical_private_data_tree_membership_checks(DummyComposer& compo
abis::MembershipWitness<NT, PRIVATE_DATA_TREE_ROOTS_TREE_HEIGHT> 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);
}
}
Expand All @@ -275,7 +229,7 @@ void perform_historical_contract_data_tree_membership_checks(DummyComposer& comp
abis::MembershipWitness<NT, PRIVATE_DATA_TREE_ROOTS_TREE_HEIGHT> 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);
}
}
Expand Down Expand Up @@ -389,19 +343,19 @@ AppendOnlySnapshot check_nullifier_tree_non_membership_and_insert_to_tree(DummyC
};

// perform membership check for the low nullifier against the original root
check_membership<NULLIFIER_TREE_HEIGHT>(composer,
original_low_nullifier.hash(),
witness.leaf_index,
witness.sibling_path,
current_nullifier_tree_root);
components::check_membership<NULLIFIER_TREE_HEIGHT>(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,
.nextIndex = new_index,
.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);
}

Expand All @@ -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 {
Expand All @@ -453,57 +407,36 @@ BaseOrMergeRollupPublicInputs base_rollup_circuit(DummyComposer& composer, BaseR
// First we compute the contract tree leaves
std::vector<NT::fr> 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);

Expand Down
128 changes: 128 additions & 0 deletions cpp/src/aztec3/circuits/rollup/components/components.cpp
Original file line number Diff line number Diff line change
@@ -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 <algorithm>
#include <array>
#include <cassert>
#include <cstdint>
#include <tuple>
#include <vector>

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<fr, 2> compute_calldata_hash(std::array<abis::PreviousRollupData<NT>, 2> previous_rollup_data)
{
// 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 = 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<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
std::array<uint8_t, 32> 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
Loading