Skip to content

Commit

Permalink
Experimental code for incremental merkle hash
Browse files Browse the repository at this point in the history
  • Loading branch information
theoreticalbts committed Mar 1, 2017
1 parent a134b6b commit a2b2d8e
Show file tree
Hide file tree
Showing 4 changed files with 408 additions and 0 deletions.
1 change: 1 addition & 0 deletions libraries/chain/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ add_library( steemit_chain
steem_objects.cpp
shared_authority.cpp
block_log.cpp
incremental_merkle_hash.cpp

util/reward.cpp

Expand Down
147 changes: 147 additions & 0 deletions libraries/chain/include/steemit/chain/incremental_merkle_hash.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@

#pragma once

#include <fc/crypto/sha256.hpp>
#include <fc/io/raw.hpp>
#include <fc/reflect/reflect.hpp>
#include <fc/reflect/variant.hpp>

#include <cstdint>
#include <vector>

namespace steemit { namespace chain {

/**
* Contains a data structure which allows incremental computation of Merkle root.
*/

class incremental_merkle_hash
{
public:
typedef fc::sha256 hash_type;

incremental_merkle_hash() {}
virtual ~incremental_merkle_hash() {}

/**
* The algorithm used to compute inner node hashes.
*/
static hash_type hash_pair( const hash_type& left, const hash_type& right )
{
return hash_type::hash( std::make_pair( left, right ) );
}

/**
* The definition of the root hash of an empty tree.
*/
static hash_type empty_tree_hash();

/**
* Add a node hash.
* Worst-case performance is O(log(n)) hash_pair() operations.
* Average-case performance for building a tree is O(1) hash_pair() operations.
*/
void add_node_hash( const hash_type& node );

/**
* Compute the root hash. Requires O(log(n)) hash_apri
*/
hash_type compute_root_hash()const;

/**
* Convenience method to hash and add a node of any FC serializable type.
*/
template< typename T >
void add_node( const T& node )
{
add_node_hash( hash_type::hash( node ) );
}

/**
* The number of leaf nodes that have been added.
*/
uint64_t leaf_count = 0;

/**
* The hash of the root of each L-branch.
*
* An L-branch is a subtree with the following two conditions:
* - The L-branch root's parent is an ancestor node of the next leaf node.
* - The L-branch root is not an ancestor node of the next leaf node.
*
* Since the tree's filled left-to-right, L-branch roots are the left-child siblings
* of the right-child ancestors of the next leaf node.
*/
std::vector< hash_type > lbranches;
};

/**
* Incremental Merkle hash which remembers the last computed hash value.
* Avoids expensive recomputation of hashes. Has the same
* binary and JSON serialization as incremental_merkle_hash.
*/

class annotated_incremental_merkle_hash
{
typedef incremental_merkle_hash::hash_type hash_type;

annotated_incremental_merkle_hash() {}
virtual ~annotated_incremental_merkle_hash() {}

hash_type get_root_hash()
{
if( !root_hash.valid() )
root_hash = ihash.compute_root_hash();
return *root_hash;
}

void add_node_hash( const hash_type& node )
{
root_hash.reset();
ihash.add_node_hash( node );
}

template< typename T >
void add_node( const T& node )
{
add_node_hash( hash_type::hash( node ) );
}

incremental_merkle_hash ihash;
fc::optional< hash_type > root_hash;
};

} }

FC_REFLECT( steemit::chain::incremental_merkle_hash, (leaf_count)(lbranches) )

namespace fc {

template<typename T>
void to_variant( const steemit::chain::annotated_incremental_merkle_hash& h, variant& vo )
{
to_variant( h.ihash, vo );
}

template<typename T>
void from_variant( const variant& vo, steemit::chain::annotated_incremental_merkle_hash& h )
{
from_variant( vo, h.ihash );
}

namespace raw {

template< typename Stream >
inline void pack( Stream& s, const steemit::chain::annotated_incremental_merkle_hash& h )
{
fc::raw::pack( s, h.ihash );
}

template< typename Stream >
inline void unpack( Stream& s, steemit::chain::annotated_incremental_merkle_hash& h )
{
h.root_hash.reset();
unpack( s, h.ihash );
}

} }
63 changes: 63 additions & 0 deletions libraries/chain/incremental_merkle_hash.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@

#include <steemit/chain/incremental_merkle_hash.hpp>

namespace steemit { namespace chain {

incremental_merkle_hash::hash_type incremental_merkle_hash::empty_tree_hash()
{
return incremental_merkle_hash::hash_type::hash("");
}

void incremental_merkle_hash::add_node_hash( const incremental_merkle_hash::hash_type& node )
{
//
// Every 1 in the binary representation of leaf_count is an L-branch.
//
// An even-numbered leaf will become a new L-branch consisting of a single node.
//
// For an odd-numbered leaf, we can follow some number of right-child links in
// its ancestry until we reach the lowest left-child link (if we traverse all the
// way to the root, we simply reparent the tree as the left child of a new root).
// Call this the R-path. The number of links in the R-path is the number of 1
// bits at the end of leaf_count.
//
// The root of the right-child links is a new L-branch obtained by combining the
// L-branches along the R-path with the new node.
//

uint64_t lbits = leaf_count ^ (leaf_count+1);
// leaf_count=...0 -> lbits=1
// leaf_count=...01 -> lbits=11
// leaf_count=...011 -> lbits=111
// leaf_count=...0111 -> lbits=1111
// leaf_count=...01^K -> lbits=1^(K+1)

lbranches.push_back( node );
size_t n = lbranches.size();

while( lbits > 1 )
{
lbranches[n-2] = hash_pair( lbranches[n-2], lbranches[n-1] );
lbranches.pop_back();
--n;
lbits >>= 1;
}
++leaf_count;
}

incremental_merkle_hash::hash_type incremental_merkle_hash::compute_root_hash()const
{
size_t n = lbranches.size();
if( n == 0 )
return empty_tree_hash();

hash_type h = lbranches[--n];
while( n > 0 )
{
--n;
h = hash_pair( lbranches[n], h );
}
return h;
}

} } // steemit::chain
Loading

0 comments on commit a2b2d8e

Please sign in to comment.