Further improve Merkle root algorithm, implement unit test #266

This commit redefines the hash h of a node in the Merkle tree as:

h(unary_node) = unary_node
h(binary_node) = H(left_child + right_child)

Previous code in c0b9af9a99 defined hash as:

h(unary_node) = H(unary_node + digest_type())
h(binary_node) = H(left_child + right_child)

The improved definition in this commit saves some hash computations.
This commit is contained in:
theoreticalbts 2015-08-24 13:22:20 -04:00
parent d1484fb41e
commit 0bcfc69da2
2 changed files with 190 additions and 30 deletions

View file

@ -61,41 +61,24 @@ namespace graphene { namespace chain {
if( transactions.size() == 0 )
return checksum_type();
vector<digest_type> ids;
ids.resize( ((transactions.size() + 1)/2)*2 );
vector<digest_type> ids;
ids.resize( transactions.size() );
for( uint32_t i = 0; i < transactions.size(); ++i )
ids[i] = transactions[i].merkle_digest();
vector<digest_type>::size_type current_number_of_hashes = ids.size();
while( true )
while( current_number_of_hashes > 1 )
{
#define AUG_20_TESTNET_COMPATIBLE
#ifdef AUG_20_TESTNET_COMPATIBLE
for( uint32_t i = 0; i < transactions.size(); i += 2 )
#else
for( uint32_t i = 0; i < current_number_of_hashes; i += 2 )
#endif
ids[i/2] = digest_type::hash( std::make_pair( ids[i], ids[i+1] ) );
// since we're processing hashes in pairs, we need to ensure that we always
// have an even number of hashes in the ids list. If we would end up with
// an odd number, add a default-initialized hash to compensate
current_number_of_hashes /= 2;
#ifdef AUG_20_TESTNET_COMPATIBLE
if (current_number_of_hashes <= 1)
break;
#else
if (current_number_of_hashes == 1)
break;
if (current_number_of_hashes % 2)
{
++current_number_of_hashes;
// TODO: HARD FORK: we should probably enable the next line the next time we fire
// up a new testnet; it will change the merkle roots we generate, but will
// give us a better-defined algorithm for calculating them
//
ids[current_number_of_hashes - 1] = digest_type();
}
#endif
// hash ID's in pairs
uint32_t i_max = current_number_of_hashes - (current_number_of_hashes&1);
uint32_t k = 0;
for( uint32_t i = 0; i < i_max; i += 2 )
ids[k++] = digest_type::hash( std::make_pair( ids[i], ids[i+1] ) );
if( current_number_of_hashes&1 )
ids[k++] = ids[i_max];
current_number_of_hashes = k;
}
return checksum_type::hash( ids[0] );
}

View file

@ -350,4 +350,181 @@ BOOST_AUTO_TEST_CASE( scaled_precision )
GRAPHENE_CHECK_THROW( asset::scaled_precision(19), fc::exception );
}
BOOST_AUTO_TEST_CASE( merkle_root )
{
signed_block block;
vector<processed_transaction> tx;
vector<digest_type> t;
const uint32_t num_tx = 10;
for( uint32_t i=0; i<num_tx; i++ )
{
tx.emplace_back();
tx.back().ref_block_prefix = i;
t.push_back( tx.back().merkle_digest() );
}
auto c = []( const digest_type& digest ) -> checksum_type
{ return checksum_type::hash( digest ); };
auto d = []( const digest_type& left, const digest_type& right ) -> digest_type
{ return digest_type::hash( std::make_pair( left, right ) ); };
BOOST_CHECK( block.calculate_merkle_root() == checksum_type() );
block.transactions.push_back( tx[0] );
BOOST_CHECK( block.calculate_merkle_root() ==
c(t[0])
);
digest_type dA, dB, dC, dD, dE, dI, dJ, dK, dM, dN, dO;
/*
A=d(0,1)
/ \
0 1
*/
dA = d(t[0], t[1]);
block.transactions.push_back( tx[1] );
BOOST_CHECK( block.calculate_merkle_root() == c(dA) );
/*
I=d(A,B)
/ \
A=d(0,1) B=2
/ \ /
0 1 2
*/
dB = t[2];
dI = d(dA, dB);
block.transactions.push_back( tx[2] );
BOOST_CHECK( block.calculate_merkle_root() == c(dI) );
/*
I=d(A,B)
/ \
A=d(0,1) B=d(2,3)
/ \ / \
0 1 2 3
*/
dB = d(t[2], t[3]);
dI = d(dA, dB);
block.transactions.push_back( tx[3] );
BOOST_CHECK( block.calculate_merkle_root() == c(dI) );
/*
__M=d(I,J)__
/ \
I=d(A,B) J=C
/ \ /
A=d(0,1) B=d(2,3) C=4
/ \ / \ /
0 1 2 3 4
*/
dC = t[4];
dJ = dC;
dM = d(dI, dJ);
block.transactions.push_back( tx[4] );
BOOST_CHECK( block.calculate_merkle_root() == c(dM) );
/*
__M=d(I,J)__
/ \
I=d(A,B) J=C
/ \ /
A=d(0,1) B=d(2,3) C=d(4,5)
/ \ / \ / \
0 1 2 3 4 5
*/
dC = d(t[4], t[5]);
dJ = dC;
dM = d(dI, dJ);
block.transactions.push_back( tx[5] );
BOOST_CHECK( block.calculate_merkle_root() == c(dM) );
/*
__M=d(I,J)__
/ \
I=d(A,B) J=d(C,D)
/ \ / \
A=d(0,1) B=d(2,3) C=d(4,5) D=6
/ \ / \ / \ /
0 1 2 3 4 5 6
*/
dD = t[6];
dJ = d(dC, dD);
dM = d(dI, dJ);
block.transactions.push_back( tx[6] );
BOOST_CHECK( block.calculate_merkle_root() == c(dM) );
/*
__M=d(I,J)__
/ \
I=d(A,B) J=d(C,D)
/ \ / \
A=d(0,1) B=d(2,3) C=d(4,5) D=d(6,7)
/ \ / \ / \ / \
0 1 2 3 4 5 6 7
*/
dD = d(t[6], t[7]);
dJ = d(dC, dD);
dM = d(dI, dJ);
block.transactions.push_back( tx[7] );
BOOST_CHECK( block.calculate_merkle_root() == c(dM) );
/*
_____________O=d(M,N)______________
/ \
__M=d(I,J)__ N=K
/ \ /
I=d(A,B) J=d(C,D) K=E
/ \ / \ /
A=d(0,1) B=d(2,3) C=d(4,5) D=d(6,7) E=8
/ \ / \ / \ / \ /
0 1 2 3 4 5 6 7 8
*/
dE = t[8];
dK = dE;
dN = dK;
dO = d(dM, dN);
block.transactions.push_back( tx[8] );
BOOST_CHECK( block.calculate_merkle_root() == c(dO) );
/*
_____________O=d(M,N)______________
/ \
__M=d(I,J)__ N=K
/ \ /
I=d(A,B) J=d(C,D) K=E
/ \ / \ /
A=d(0,1) B=d(2,3) C=d(4,5) D=d(6,7) E=d(8,9)
/ \ / \ / \ / \ / \
0 1 2 3 4 5 6 7 8 9
*/
dE = d(t[8], t[9]);
dK = dE;
dN = dK;
dO = d(dM, dN);
block.transactions.push_back( tx[9] );
BOOST_CHECK( block.calculate_merkle_root() == c(dO) );
}
BOOST_AUTO_TEST_SUITE_END()