Skip to content

Commit

Permalink
GH-1942 Add calculate_merkle that does not use left/right bit flip im…
Browse files Browse the repository at this point in the history
…plementation
  • Loading branch information
heifner committed Dec 5, 2023
1 parent 7580c77 commit 8f2e525
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 1 deletion.
7 changes: 7 additions & 0 deletions libraries/chain/include/eosio/chain/merkle.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,14 @@ namespace eosio { namespace chain {

/**
* Calculates the merkle root of a set of digests, if ids is odd it will duplicate the last id.
* Uses make_canonical_pair which before hashing sets the first bit of the previous hashes
* to 0 or 1 to indicate the side it is on.
*/
digest_type canonical_merkle( deque<digest_type> ids );

/**
* Calculates the merkle root of a set of digests. Does not manipulate the digests.
*/
digest_type calculate_merkle( deque<digest_type> ids );

} } /// eosio::chain
17 changes: 17 additions & 0 deletions libraries/chain/merkle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,21 @@ digest_type canonical_merkle(deque<digest_type> ids) {
return ids.front();
}

digest_type calculate_merkle( deque<digest_type> ids ) {
if( 0 == ids.size() ) { return digest_type(); }

while( ids.size() > 1 ) {
if( ids.size() % 2 )
ids.push_back(ids.back());

for (size_t i = 0; i < ids.size() / 2; ++i) {
ids[i] = digest_type::hash(std::make_pair(std::cref(ids[2 * i]), std::cref(ids[(2 * i) + 1])));
}

ids.resize(ids.size() / 2);
}

return ids.front();
}

} } // eosio::chain
27 changes: 26 additions & 1 deletion unittests/merkle_tree_tests.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <eosio/chain/incremental_merkle.hpp>
#include <eosio/chain/merkle.hpp>
#include <boost/test/unit_test.hpp>
#include <fc/crypto/sha256.hpp>

Expand All @@ -12,7 +13,8 @@ BOOST_AUTO_TEST_CASE(basic_append_and_root_check_canonical) {

auto node1 = fc::sha256::hash("Node1");
tree.append(node1);
BOOST_CHECK_EQUAL(tree.get_root(), node1);
BOOST_CHECK_EQUAL(tree.get_root().str(), node1.str());
BOOST_CHECK_EQUAL(canonical_merkle({node1}).str(), node1.str());
}

BOOST_AUTO_TEST_CASE(multiple_appends_canonical) {
Expand All @@ -29,20 +31,26 @@ BOOST_AUTO_TEST_CASE(multiple_appends_canonical) {

tree.append(node1);
BOOST_CHECK_EQUAL(tree.get_root().str(), node1.str());
BOOST_CHECK_EQUAL(canonical_merkle({node1}).str(), node1.str());

tree.append(node2);
BOOST_CHECK_EQUAL(tree.get_root().str(), fc::sha256::hash(make_canonical_pair(node1, node2)).str());
BOOST_CHECK_EQUAL(canonical_merkle({node1, node2}).str(), fc::sha256::hash(make_canonical_pair(node1, node2)).str());

tree.append(node3);
BOOST_CHECK_EQUAL(tree.get_root().str(), fc::sha256::hash(make_canonical_pair(
fc::sha256::hash(make_canonical_pair(node1, node2)),
fc::sha256::hash(make_canonical_pair(node3, node3)))).str());
BOOST_CHECK_EQUAL(canonical_merkle({node1, node2, node3}).str(), fc::sha256::hash(make_canonical_pair(
fc::sha256::hash(make_canonical_pair(node1, node2)),
fc::sha256::hash(make_canonical_pair(node3, node3)))).str());

tree.append(node4);
auto calculated_root = fc::sha256::hash(make_canonical_pair(
fc::sha256::hash(make_canonical_pair(node1, node2)),
fc::sha256::hash(make_canonical_pair(node3, node4))));
BOOST_CHECK_EQUAL(tree.get_root().str(), calculated_root.str());
BOOST_CHECK_EQUAL(canonical_merkle({node1, node2, node3, node4}).str(), calculated_root.str());

tree.append(node5);
calculated_root = fc::sha256::hash(
Expand All @@ -58,6 +66,7 @@ BOOST_AUTO_TEST_CASE(multiple_appends_canonical) {
)
);
BOOST_CHECK_EQUAL(tree.get_root().str(), calculated_root.str());
BOOST_CHECK_EQUAL(canonical_merkle({node1, node2, node3, node4, node5}).str(), calculated_root.str());

tree.append(node6);
calculated_root = fc::sha256::hash(
Expand All @@ -73,6 +82,7 @@ BOOST_AUTO_TEST_CASE(multiple_appends_canonical) {
)
);
BOOST_CHECK_EQUAL(tree.get_root().str(), calculated_root.str());
BOOST_CHECK_EQUAL(canonical_merkle({node1, node2, node3, node4, node5, node6}).str(), calculated_root.str());

tree.append(node7);
calculated_root = fc::sha256::hash(
Expand All @@ -88,6 +98,7 @@ BOOST_AUTO_TEST_CASE(multiple_appends_canonical) {
)
);
BOOST_CHECK_EQUAL(tree.get_root().str(), calculated_root.str());
BOOST_CHECK_EQUAL(canonical_merkle({node1, node2, node3, node4, node5, node6, node7}).str(), calculated_root.str());

tree.append(node8);
calculated_root = fc::sha256::hash(
Expand All @@ -103,6 +114,7 @@ BOOST_AUTO_TEST_CASE(multiple_appends_canonical) {
)
);
BOOST_CHECK_EQUAL(tree.get_root().str(), calculated_root.str());
BOOST_CHECK_EQUAL(canonical_merkle({node1, node2, node3, node4, node5, node6, node7, node8}).str(), calculated_root.str());

tree.append(node9);
calculated_root = fc::sha256::hash(make_canonical_pair(
Expand Down Expand Up @@ -131,6 +143,7 @@ BOOST_AUTO_TEST_CASE(multiple_appends_canonical) {
)
) ));
BOOST_CHECK_EQUAL(tree.get_root().str(), calculated_root.str());
BOOST_CHECK_EQUAL(canonical_merkle({node1, node2, node3, node4, node5, node6, node7, node8, node9}).str(), calculated_root.str());
}

BOOST_AUTO_TEST_CASE(basic_append_and_root_check) {
Expand All @@ -140,6 +153,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}).str(), node1.str());
}

BOOST_AUTO_TEST_CASE(multiple_appends) {
Expand All @@ -156,20 +170,26 @@ BOOST_AUTO_TEST_CASE(multiple_appends) {

tree.append(node1);
BOOST_CHECK_EQUAL(tree.get_root().str(), node1.str());
BOOST_CHECK_EQUAL(calculate_merkle({node1}).str(), node1.str());

tree.append(node2);
BOOST_CHECK_EQUAL(tree.get_root().str(), fc::sha256::hash(std::make_pair(node1, node2)).str());
BOOST_CHECK_EQUAL(calculate_merkle({node1, node2}).str(), fc::sha256::hash(std::make_pair(node1, node2)).str());

tree.append(node3);
BOOST_CHECK_EQUAL(tree.get_root().str(), fc::sha256::hash(std::make_pair(
fc::sha256::hash(std::make_pair(node1, node2)),
fc::sha256::hash(std::make_pair(node3, node3)))).str());
BOOST_CHECK_EQUAL(calculate_merkle({node1, node2, node3}).str(), fc::sha256::hash(std::make_pair(
fc::sha256::hash(std::make_pair(node1, node2)),
fc::sha256::hash(std::make_pair(node3, node3)))).str());

tree.append(node4);
auto calculated_root = fc::sha256::hash(std::make_pair(
fc::sha256::hash(std::make_pair(node1, node2)),
fc::sha256::hash(std::make_pair(node3, node4))));
BOOST_CHECK_EQUAL(tree.get_root().str(), calculated_root.str());
BOOST_CHECK_EQUAL(calculate_merkle({node1, node2, node3, node4}).str(), calculated_root.str());

tree.append(node5);
calculated_root = fc::sha256::hash(
Expand All @@ -185,6 +205,7 @@ BOOST_AUTO_TEST_CASE(multiple_appends) {
)
);
BOOST_CHECK_EQUAL(tree.get_root().str(), calculated_root.str());
BOOST_CHECK_EQUAL(calculate_merkle({node1, node2, node3, node4, node5}).str(), calculated_root.str());

tree.append(node6);
calculated_root = fc::sha256::hash(
Expand All @@ -200,6 +221,7 @@ BOOST_AUTO_TEST_CASE(multiple_appends) {
)
);
BOOST_CHECK_EQUAL(tree.get_root().str(), calculated_root.str());
BOOST_CHECK_EQUAL(calculate_merkle({node1, node2, node3, node4, node5, node6}).str(), calculated_root.str());

tree.append(node7);
calculated_root = fc::sha256::hash(
Expand All @@ -215,6 +237,7 @@ BOOST_AUTO_TEST_CASE(multiple_appends) {
)
);
BOOST_CHECK_EQUAL(tree.get_root().str(), calculated_root.str());
BOOST_CHECK_EQUAL(calculate_merkle({node1, node2, node3, node4, node5, node6, node7}).str(), calculated_root.str());

tree.append(node8);
calculated_root = fc::sha256::hash(
Expand All @@ -230,6 +253,7 @@ BOOST_AUTO_TEST_CASE(multiple_appends) {
)
);
BOOST_CHECK_EQUAL(tree.get_root().str(), calculated_root.str());
BOOST_CHECK_EQUAL(calculate_merkle({node1, node2, node3, node4, node5, node6, node7, node8}).str(), calculated_root.str());

tree.append(node9);
calculated_root = fc::sha256::hash(std::make_pair(
Expand Down Expand Up @@ -258,6 +282,7 @@ BOOST_AUTO_TEST_CASE(multiple_appends) {
)
) ));
BOOST_CHECK_EQUAL(tree.get_root().str(), calculated_root.str());
BOOST_CHECK_EQUAL(calculate_merkle({node1, node2, node3, node4, node5, node6, node7, node8, node9}).str(), calculated_root.str());
}

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit 8f2e525

Please sign in to comment.