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:
parent
d1484fb41e
commit
0bcfc69da2
2 changed files with 190 additions and 30 deletions
|
|
@ -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] );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in a new issue