Skip to content

Commit

Permalink
Address PR comments, templatize calculate_merkle.
Browse files Browse the repository at this point in the history
  • Loading branch information
greg7mdp committed Mar 30, 2024
1 parent 198e512 commit 89e3d8d
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 36 deletions.
4 changes: 2 additions & 2 deletions libraries/chain/block_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ block_state_ptr block_state::create_if_genesis_block(const block_state_legacy& b
result.valid_qc = {}; // best qc received from the network inside block extension, empty until first savanna proper IF block

// Calculate Merkle tree root in Savanna way so that it is stored in Leaf Node when building block_state.
auto digests = *bsp.action_receipt_digests_savanna;
auto action_mroot_svnn = calculate_merkle(std::move(digests));
const auto& digests = *bsp.action_receipt_digests_savanna;
auto action_mroot_svnn = calculate_merkle(digests);

// build leaf_node and validation_tree
valid_t::finality_leaf_node_t leaf_node {
Expand Down
16 changes: 8 additions & 8 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -708,13 +708,13 @@ struct building_block {
auto [transaction_mroot, action_mroot] = std::visit(
overloaded{[&](digests_t& trx_receipts) { // calculate the two merkle roots in separate threads
auto trx_merkle_fut =
post_async_task(ioc, [&]() { return calculate_merkle(std::move(trx_receipts)); });
post_async_task(ioc, [&]() { return calculate_merkle(trx_receipts); });
auto action_merkle_fut =
post_async_task(ioc, [&]() { return calculate_merkle(std::move(*action_receipts.digests_s)); });
post_async_task(ioc, [&]() { return calculate_merkle(*action_receipts.digests_s); });
return std::make_pair(trx_merkle_fut.get(), action_merkle_fut.get());
},
[&](const checksum256_type& trx_checksum) {
return std::make_pair(trx_checksum, calculate_merkle(std::move(*action_receipts.digests_s)));
return std::make_pair(trx_checksum, calculate_merkle(*action_receipts.digests_s));
}},
trx_mroot_or_receipt_digests());

Expand Down Expand Up @@ -1308,8 +1308,8 @@ struct controller_impl {
// IRREVERSIBLE applies (validates) blocks when irreversible, new_valid will be done after apply in log_irreversible
assert(read_mode == db_read_mode::IRREVERSIBLE || legacy->action_receipt_digests_savanna);
if (legacy->action_receipt_digests_savanna) {
auto digests = *legacy->action_receipt_digests_savanna;
auto action_mroot = calculate_merkle(std::move(digests));
const auto& digests = *legacy->action_receipt_digests_savanna;
auto action_mroot = calculate_merkle(digests);
// Create the valid structure for producing
new_bsp->valid = prev->new_valid(*new_bsp, action_mroot, new_bsp->strong_digest);
}
Expand Down Expand Up @@ -1522,8 +1522,8 @@ struct controller_impl {
validator_t{}, skip_validate_signee);
// legacy_branch is from head, all should be validated
assert(bspl->action_receipt_digests_savanna);
auto digests = *bspl->action_receipt_digests_savanna;
auto action_mroot = calculate_merkle(std::move(digests));
const auto& digests = *bspl->action_receipt_digests_savanna;
auto action_mroot = calculate_merkle(digests);
// Create the valid structure for producing
new_bsp->valid = prev->new_valid(*new_bsp, action_mroot, new_bsp->strong_digest);
prev = new_bsp;
Expand Down Expand Up @@ -4066,7 +4066,7 @@ struct controller_impl {
// @param if_active true if instant finality is active
static checksum256_type calc_merkle( deque<digest_type>&& digests, bool if_active ) {
if (if_active) {
return calculate_merkle( std::move(digests) );
return calculate_merkle( digests );
} else {
return calculate_merkle_legacy( std::move(digests) );
}
Expand Down
47 changes: 35 additions & 12 deletions libraries/chain/include/eosio/chain/merkle.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,6 @@ inline digest_type hash_combine(const digest_type& a, const digest_type& b) {
return digest_type::hash(std::make_pair(std::cref(a), std::cref(b)));
}

// does not overwrite passed sequence
//
// log2 recursion OK, uses less than 5KB stack space for 4 billion digests
// appended (or 0.25% of default 2MB thread stack size on Ubuntu).
// -----------------------------------------------------------------------
template <class It, bool async = false>
requires std::is_same_v<std::decay_t<typename std::iterator_traits<It>::value_type>, digest_type>
inline digest_type calculate_merkle_pow2(const It& start, const It& end) {
Expand Down Expand Up @@ -56,8 +51,26 @@ inline digest_type calculate_merkle_pow2(const It& start, const It& end) {
}
}

} // namespace detail

// ************* public interface starts here ************************************************

// ------------------------------------------------------------------------
// calculate_merkle:
// -----------------
// takes two random access iterators delimiting a sequence of `digest_type`,
// returns the root digest for the provided sequence.
//
// does not overwrite passed sequence
//
// log2 recursion OK, uses less than 5KB stack space for 4 billion digests
// appended (or 0.25% of default 2MB thread stack size on Ubuntu).
// ------------------------------------------------------------------------
template <class It>
requires std::is_same_v<std::decay_t<typename std::iterator_traits<It>::value_type>, digest_type>
#if __cplusplus >= 202002L
requires std::random_access_iterator<It> &&
std::is_same_v<std::decay_t<typename std::iterator_traits<It>::value_type>, digest_type>
#endif
inline digest_type calculate_merkle(const It& start, const It& end) {
assert(end >= start);
auto size = static_cast<size_t>(end - start);
Expand All @@ -66,16 +79,26 @@ inline digest_type calculate_merkle(const It& start, const It& end) {

auto midpoint = detail::bit_floor(size);
if (size == midpoint)
return calculate_merkle_pow2<It, true>(start, end);
return detail::calculate_merkle_pow2<It, true>(start, end);

auto mid = start + midpoint;
return hash_combine(calculate_merkle_pow2<It, true>(start, mid), calculate_merkle(mid, end));
return detail::hash_combine(detail::calculate_merkle_pow2<It, true>(start, mid),
calculate_merkle(mid, end));
}

}

inline digest_type calculate_merkle(const deque<digest_type>& ids) {
return detail::calculate_merkle(ids.cbegin(), ids.cend());
// --------------------------------------------------------------------------
// calculate_merkle:
// -----------------
// takes a container or `std::span` of `digest_type`, returns the root digest
// for the sequence of digests in the container.
// --------------------------------------------------------------------------
template <class Cont>
#if __cplusplus >= 202002L
requires std::random_access_iterator<decltype(Cont().begin())> &&
std::is_same_v<std::decay_t<typename Cont::value_type>, digest_type>
#endif
inline digest_type calculate_merkle(const Cont& ids) {
return calculate_merkle(ids.begin(), ids.end()); // cbegin not supported for std::span until C++23.
}


Expand Down
2 changes: 1 addition & 1 deletion libraries/chain/include/eosio/chain/merkle_legacy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ namespace detail {
return make_pair(make_legacy_left_digest(l), make_legacy_right_digest(r));
};

}
} // namespace detail

/**
* Calculates the merkle root of a set of digests, if ids is odd it will duplicate the last id.
Expand Down
26 changes: 13 additions & 13 deletions unittests/merkle_tree_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ BOOST_AUTO_TEST_CASE(basic_append_and_root_check) {
auto node1 = fc::sha256::hash("Node1");
tree.append(node1);
BOOST_CHECK_EQUAL(tree.get_root(), node1);
BOOST_CHECK_EQUAL(calculate_merkle({node1}), node1);
BOOST_CHECK_EQUAL(calculate_merkle(std::span(&node1, 1)), node1);
}

BOOST_AUTO_TEST_CASE(multiple_appends) {
Expand All @@ -124,48 +124,48 @@ BOOST_AUTO_TEST_CASE(multiple_appends) {

tree.append(node1);
BOOST_CHECK_EQUAL(tree.get_root(), node1);
BOOST_CHECK_EQUAL(calculate_merkle({node1}), node1);
BOOST_CHECK_EQUAL(calculate_merkle(std::span(first, 1)), node1);

tree.append(node2);
BOOST_CHECK_EQUAL(tree.get_root(), hash(node1, node2));
BOOST_CHECK_EQUAL(calculate_merkle({first, first + 2}), hash(node1, node2));
BOOST_CHECK_EQUAL(calculate_merkle(std::span(first, 2)), hash(node1, node2));

tree.append(node3);
auto calculated_root = hash(hash(node1, node2), node3);
BOOST_CHECK_EQUAL(tree.get_root(), calculated_root);
BOOST_CHECK_EQUAL(calculate_merkle({first, first + 3}), calculated_root);
BOOST_CHECK_EQUAL(calculate_merkle(std::span(first, 3)), calculated_root);

tree.append(node4);
auto first_four_tree = hash(hash(node1, node2), hash(node3, node4));
calculated_root = first_four_tree;
BOOST_CHECK_EQUAL(tree.get_root(), calculated_root);
BOOST_CHECK_EQUAL(calculate_merkle({first, first + 4}), calculated_root);
BOOST_CHECK_EQUAL(calculate_merkle(std::span(first, 4)), calculated_root);

tree.append(node5);
calculated_root = hash(first_four_tree, node5);
BOOST_CHECK_EQUAL(tree.get_root(), calculated_root);
BOOST_CHECK_EQUAL(calculate_merkle({first, first + 5}), calculated_root);
BOOST_CHECK_EQUAL(calculate_merkle(std::span(first, 5)), calculated_root);

tree.append(node6);
calculated_root = hash(first_four_tree, hash(node5, node6));
BOOST_CHECK_EQUAL(tree.get_root(), calculated_root);
BOOST_CHECK_EQUAL(calculate_merkle({first, first + 6}), calculated_root);
BOOST_CHECK_EQUAL(calculate_merkle(std::span(first, 6)), calculated_root);

tree.append(node7);
calculated_root = hash(first_four_tree, hash(hash(node5, node6), node7));
BOOST_CHECK_EQUAL(tree.get_root(), calculated_root);
BOOST_CHECK_EQUAL(calculate_merkle({first, first + 7}), calculated_root);
BOOST_CHECK_EQUAL(calculate_merkle(std::span(first, 7)), calculated_root);

tree.append(node8);
auto next_four_tree = hash(hash(node5, node6), hash(node7, node8));
calculated_root = hash(first_four_tree, next_four_tree);
BOOST_CHECK_EQUAL(tree.get_root(), calculated_root);
BOOST_CHECK_EQUAL(calculate_merkle({first, first + 8}), calculated_root);
BOOST_CHECK_EQUAL(calculate_merkle(std::span(first, 8)), calculated_root);

tree.append(node9);
calculated_root = hash(hash(first_four_tree, next_four_tree), node9);
BOOST_CHECK_EQUAL(tree.get_root(), calculated_root);
BOOST_CHECK_EQUAL(calculate_merkle({first, first + 9}), calculated_root);
BOOST_CHECK_EQUAL(calculate_merkle(std::span(first, 9)), calculated_root);
}

BOOST_AUTO_TEST_CASE(consistency_over_large_range) {
Expand All @@ -177,7 +177,7 @@ BOOST_AUTO_TEST_CASE(consistency_over_large_range) {
for (size_t j=0; j<i; ++j)
tree.append(digests[j]);
BOOST_CHECK_EQUAL(tree.num_digests_appended(), i);
BOOST_CHECK_EQUAL(calculate_merkle({digests.begin(), digests.begin() + i}), tree.get_root());
BOOST_CHECK_EQUAL(calculate_merkle(std::span(digests.begin(), i)), tree.get_root());
}
}

Expand Down Expand Up @@ -225,7 +225,7 @@ BOOST_AUTO_TEST_CASE(perf_test_one_large) {
};

{
auto [incr_root, calc_root] = perf_test("savanna", incremental_merkle_tree(), calculate_merkle);
auto [incr_root, calc_root] = perf_test("savanna", incremental_merkle_tree(), calculate_merkle<deque<digest_type>>);
BOOST_CHECK_EQUAL(incr_root, calc_root);
}

Expand Down Expand Up @@ -277,7 +277,7 @@ BOOST_AUTO_TEST_CASE(perf_test_many_small) {
};

{
auto [incr_root, calc_root] = perf_test("savanna", incremental_merkle_tree(), calculate_merkle);
auto [incr_root, calc_root] = perf_test("savanna", incremental_merkle_tree(), calculate_merkle<deque<digest_type>>);
BOOST_CHECK_EQUAL(incr_root, calc_root);
}

Expand Down

0 comments on commit 89e3d8d

Please sign in to comment.