/*
This file is part of cpp - ethereum .
cpp - ethereum is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
cpp - ethereum is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with cpp - ethereum . If not , see < http : //www.gnu.org/licenses/>.
*/
/** @file TrieDB.h
* @ author Gav Wood < i @ gavwood . com >
* @ date 2014
*/
# pragma once
# pragma warning(push)
# pragma warning(disable: 4100 4267)
# include <leveldb/db.h>
# pragma warning(pop)
# include <map>
# include <memory>
# include <libdevcore/Common.h>
# include <libdevcore/Log.h>
# include <libdevcore/Exceptions.h>
# include <libdevcrypto/SHA3.h>
# include "MemoryDB.h"
# include "OverlayDB.h"
# include "TrieCommon.h"
namespace ldb = leveldb ;
namespace dev
{
struct TrieDBChannel : public LogChannel { static const char * name ( ) { return " -T- " ; } static const int verbosity = 17 ; } ;
# define tdebug clog(TrieDBChannel)
struct InvalidTrie : virtual dev : : Exception { } ;
extern const h256 c_shaNull ;
extern const h256 EmptyTrie ;
/**
* @ brief Merkle Patricia Tree " Trie " : a modifed base - 16 Radix tree .
* This version uses a database backend .
* Usage :
* @ code
* GenericTrieDB < MyDB > t ( & myDB ) ;
* assert ( t . isNull ( ) ) ;
* t . init ( ) ;
* assert ( t . isEmpty ( ) ) ;
* t . insert ( x , y ) ;
* assert ( t . at ( x ) = = y . toString ( ) ) ;
* t . remove ( x ) ;
* assert ( t . isEmpty ( ) ) ;
* @ endcode
*/
template < class DB >
class GenericTrieDB
{
public :
GenericTrieDB ( DB * _db ) : m_db ( _db ) { }
GenericTrieDB ( DB * _db , h256 _root ) { open ( _db , _root ) ; }
~ GenericTrieDB ( ) { }
void open ( DB * _db , h256 _root ) { m_db = _db ; setRoot ( _root ) ; }
void init ( ) ;
void setRoot ( h256 _root )
{
m_root = _root ;
if ( m_root = = c_shaNull & & ! m_db - > exists ( m_root ) )
init ( ) ;
/*std::cout << "Setting root to " << _root << " (patched to " << m_root << ")" << std::endl;*/
if ( ! node ( m_root ) . size ( ) )
BOOST_THROW_EXCEPTION ( RootNotFound ( ) ) ;
}
bool haveRoot ( h256 _root , bool _enforceRefs = true ) { return _root = = c_shaNull ? true : m_db - > lookup ( _root , _enforceRefs ) . size ( ) ; }
/// True if the trie is uninitialised (i.e. that the DB doesn't contain the root node).
bool isNull ( ) const { return ! node ( m_root ) . size ( ) ; }
/// True if the trie is initialised but empty (i.e. that the DB contains the root node which is empty).
bool isEmpty ( ) const { return m_root = = c_shaNull & & node ( m_root ) . size ( ) ; }
h256 root ( ) const { assert ( node ( m_root ) . size ( ) ) ; /*std::cout << "Returning root as " << ret << " (really " << m_root << ")" << std::endl;*/ return m_root ; } // patch the root in the case of the empty trie. TODO: handle this properly.
void debugPrint ( ) { }
void descendKey ( h256 _k , std : : set < h256 > & _keyMask , bool _wasExt , std : : ostream * _out , int _indent = 0 ) const
{
_keyMask . erase ( _k ) ;
if ( _k = = m_root & & _k = = c_shaNull ) // root allowed to be empty
return ;
descendList ( RLP ( node ( _k ) ) , _keyMask , _wasExt , _out , _indent ) ; // if not, it must be a list
}
void descendEntry ( RLP const & _r , std : : set < h256 > & _keyMask , bool _wasExt , std : : ostream * _out , int _indent ) const
{
if ( _r . isData ( ) & & _r . size ( ) = = 32 )
descendKey ( _r . toHash < h256 > ( ) , _keyMask , _wasExt , _out , _indent ) ;
else if ( _r . isList ( ) )
descendList ( _r , _keyMask , _wasExt , _out , _indent ) ;
else
BOOST_THROW_EXCEPTION ( InvalidTrie ( ) ) ;
}
void descendList ( RLP const & _r , std : : set < h256 > & _keyMask , bool _wasExt , std : : ostream * _out , int _indent ) const
{
if ( _r . isList ( ) & & _r . itemCount ( ) = = 2 & & ( ! _wasExt | | _out ) )
{
if ( _out )
( * _out ) < < std : : string ( _indent * 2 , ' ' ) < < ( _wasExt ? " !2 " : " 2 " ) < < sha3 ( _r . data ( ) ) . abridged ( ) < < " : " < < _r < < " \n " ;
if ( ! isLeaf ( _r ) ) // don't go down leaves
descendEntry ( _r [ 1 ] , _keyMask , true , _out , _indent + 1 ) ;
}
else if ( _r . isList ( ) & & _r . itemCount ( ) = = 17 )
{
if ( _out )
( * _out ) < < std : : string ( _indent * 2 , ' ' ) < < " 17 " < < sha3 ( _r . data ( ) ) . abridged ( ) < < " : " < < _r < < " \n " ;
for ( unsigned i = 0 ; i < 16 ; + + i )
if ( ! _r [ i ] . isEmpty ( ) ) // 16 branches are allowed to be empty
descendEntry ( _r [ i ] , _keyMask , false , _out , _indent + 1 ) ;
}
else
BOOST_THROW_EXCEPTION ( InvalidTrie ( ) ) ;
}
std : : set < h256 > leftOvers ( std : : ostream * _out = nullptr ) const
{
std : : set < h256 > k = m_db - > keys ( ) ;
descendKey ( m_root , k , false , _out ) ;
return k ;
}
void debugStructure ( std : : ostream & _out ) const
{
leftOvers ( & _out ) ;
}
bool check ( bool _requireNoLeftOvers ) const
{
try
{
return leftOvers ( ) . empty ( ) | | ! _requireNoLeftOvers ;
}
catch ( . . . )
{
cwarn < < boost : : current_exception_diagnostic_information ( ) ;
return false ;
}
}
std : : string at ( bytesConstRef _key ) const ;
void insert ( bytesConstRef _key , bytesConstRef _value ) ;
void remove ( bytesConstRef _key ) ;
void contains ( bytesConstRef _key ) { return ! at ( _key ) . empty ( ) ; }
class iterator
{
public :
using value_type = std : : pair < bytesConstRef , bytesConstRef > ;
iterator ( ) { }
iterator ( GenericTrieDB const * _db ) ;
iterator ( GenericTrieDB const * _db , bytesConstRef _key ) ;
iterator & operator + + ( ) { next ( ) ; return * this ; }
value_type operator * ( ) const { return at ( ) ; }
value_type operator - > ( ) const { return at ( ) ; }
bool operator = = ( iterator const & _c ) const { return _c . m_trail = = m_trail ; }
bool operator ! = ( iterator const & _c ) const { return _c . m_trail ! = m_trail ; }
value_type at ( ) const ;
private :
void next ( ) ;
void next ( NibbleSlice _key ) ;
struct Node
{
std : : string rlp ;
std : : string key ; // as hexPrefixEncoding.
byte child ; // 255 -> entering, 16 -> actually at the node, 17 -> exiting, 0-15 -> actual children.
// 255 -> 16 -> 0 -> 1 -> ... -> 15 -> 17
void setChild ( unsigned _i ) { child = _i ; }
void setFirstChild ( ) { child = 16 ; }
void incrementChild ( ) { child = child = = 16 ? 0 : child = = 15 ? 17 : ( child + 1 ) ; }
bool operator = = ( Node const & _c ) const { return rlp = = _c . rlp & & key = = _c . key & & child = = _c . child ; }
bool operator ! = ( Node const & _c ) const { return ! operator = = ( _c ) ; }
} ;
std : : vector < Node > m_trail ;
GenericTrieDB < DB > const * m_that ;
} ;
iterator begin ( ) const { return this ; }
iterator end ( ) const { return iterator ( ) ; }
iterator lower_bound ( bytesConstRef _key ) const { return iterator ( this , _key ) ; }
private :
RLPStream & streamNode ( RLPStream & _s , bytes const & _b ) ;
std : : string atAux ( RLP const & _here , NibbleSlice _key ) const ;
void mergeAtAux ( RLPStream & _out , RLP const & _replace , NibbleSlice _key , bytesConstRef _value ) ;
bytes mergeAt ( RLP const & _replace , NibbleSlice _k , bytesConstRef _v , bool _inLine = false ) ;
bool deleteAtAux ( RLPStream & _out , RLP const & _replace , NibbleSlice _key ) ;
bytes deleteAt ( RLP const & _replace , NibbleSlice _k ) ;
// in: null (DEL) -- OR -- [_k, V] (DEL)
// out: [_k, _s]
// -- OR --
// in: [V0, ..., V15, S16] (DEL) AND _k == {}
// out: [V0, ..., V15, _s]
bytes place ( RLP const & _orig , NibbleSlice _k , bytesConstRef _s ) ;
// in: [K, S] (DEL)
// out: null
// -- OR --
// in: [V0, ..., V15, S] (DEL)
// out: [V0, ..., V15, null]
bytes remove ( RLP const & _orig ) ;
// in: [K1 & K2, V] (DEL) : nibbles(K1) == _s, 0 < _s <= nibbles(K1 & K2)
// out: [K1, H] ; [K2, V] => H (INS) (being [K1, [K2, V]] if necessary)
bytes cleve ( RLP const & _orig , unsigned _s ) ;
// in: [K1, H] (DEL) ; H <= [K2, V] (DEL) (being [K1, [K2, V]] (DEL) if necessary)
// out: [K1 & K2, V]
bytes graft ( RLP const & _orig ) ;
// in: [V0, ... V15, S] (DEL)
// out1: [k{i}, Vi] where i < 16
// out2: [k{}, S] where i == 16
bytes merge ( RLP const & _orig , byte _i ) ;
// in: [k{}, S] (DEL)
// out: [null ** 16, S]
// -- OR --
// in: [k{i}, N] (DEL)
// out: [null ** i, N, null ** (16 - i)]
// -- OR --
// in: [k{i}K, V] (DEL)
// out: [null ** i, H, null ** (16 - i)] ; [K, V] => H (INS) (being [null ** i, [K, V], null ** (16 - i)] if necessary)
bytes branch ( RLP const & _orig ) ;
bool isTwoItemNode ( RLP const & _n ) const ;
std : : string deref ( RLP const & _n ) const ;
std : : string node ( h256 _h ) const { return m_db - > lookup ( _h ) ; }
void insertNode ( h256 _h , bytesConstRef _v ) { m_db - > insert ( _h , _v ) ; }
void killNode ( h256 _h ) { m_db - > kill ( _h ) ; }
h256 insertNode ( bytesConstRef _v ) { auto h = sha3 ( _v ) ; insertNode ( h , _v ) ; return h ; }
void killNode ( RLP const & _d ) { if ( _d . data ( ) . size ( ) > = 32 ) killNode ( sha3 ( _d . data ( ) ) ) ; }
h256 m_root ;
DB * m_db = nullptr ;
} ;
template < class DB >
std : : ostream & operator < < ( std : : ostream & _out , GenericTrieDB < DB > const & _db )
{
for ( auto const & i : _db )
_out < < escaped ( i . first . toString ( ) , false ) < < " : " < < escaped ( i . second . toString ( ) , false ) < < std : : endl ;
return _out ;
}
template < class KeyType , class DB >
class TrieDB : public GenericTrieDB < DB >
{
public :
TrieDB ( DB * _db ) : GenericTrieDB < DB > ( _db ) { }
TrieDB ( DB * _db , h256 _root ) : GenericTrieDB < DB > ( _db , _root ) { }
std : : string operator [ ] ( KeyType _k ) const { return at ( _k ) ; }
bool contains ( KeyType _k ) const { return GenericTrieDB < DB > : : contains ( bytesConstRef ( ( byte const * ) & _k , sizeof ( KeyType ) ) ) ; }
std : : string at ( KeyType _k ) const { return GenericTrieDB < DB > : : at ( bytesConstRef ( ( byte const * ) & _k , sizeof ( KeyType ) ) ) ; }
void insert ( KeyType _k , bytesConstRef _value ) { GenericTrieDB < DB > : : insert ( bytesConstRef ( ( byte const * ) & _k , sizeof ( KeyType ) ) , _value ) ; }
void insert ( KeyType _k , bytes const & _value ) { insert ( _k , bytesConstRef ( & _value ) ) ; }
void remove ( KeyType _k ) { GenericTrieDB < DB > : : remove ( bytesConstRef ( ( byte const * ) & _k , sizeof ( KeyType ) ) ) ; }
class iterator : public GenericTrieDB < DB > : : iterator
{
public :
using Super = typename GenericTrieDB < DB > : : iterator ;
using value_type = std : : pair < KeyType , bytesConstRef > ;
iterator ( ) { }
iterator ( TrieDB const * _db ) : Super ( _db ) { }
iterator ( TrieDB const * _db , bytesConstRef _k ) : Super ( _db , _k ) { }
value_type operator * ( ) const { return at ( ) ; }
value_type operator - > ( ) const { return at ( ) ; }
value_type at ( ) const ;
} ;
iterator begin ( ) const { return this ; }
iterator end ( ) const { return iterator ( ) ; }
iterator lower_bound ( KeyType _k ) const { return iterator ( this , bytesConstRef ( ( byte const * ) & _k , sizeof ( KeyType ) ) ) ; }
} ;
template < class KeyType , class DB >
std : : ostream & operator < < ( std : : ostream & _out , TrieDB < KeyType , DB > const & _db )
{
for ( auto const & i : _db )
_out < < i . first < < " : " < < escaped ( i . second . toString ( ) , false ) < < std : : endl ;
return _out ;
}
}
// Template implementations...
namespace dev
{
template < class DB > GenericTrieDB < DB > : : iterator : : iterator ( GenericTrieDB const * _db )
{
m_that = _db ;
m_trail . push_back ( { _db - > node ( _db - > m_root ) , std : : string ( 1 , ' \0 ' ) , 255 } ) ; // one null byte is the HPE for the empty key.
next ( ) ;
}
template < class DB > GenericTrieDB < DB > : : iterator : : iterator ( GenericTrieDB const * _db , bytesConstRef _fullKey )
{
m_that = _db ;
m_trail . push_back ( { _db - > node ( _db - > m_root ) , std : : string ( 1 , ' \0 ' ) , 255 } ) ; // one null byte is the HPE for the empty key.
next ( _fullKey ) ;
}
template < class DB > typename GenericTrieDB < DB > : : iterator : : value_type GenericTrieDB < DB > : : iterator : : at ( ) const
{
assert ( m_trail . size ( ) ) ;
Node const & b = m_trail . back ( ) ;
assert ( b . key . size ( ) ) ;
assert ( ! ( b . key [ 0 ] & 0x10 ) ) ; // should be an integer number of bytes (i.e. not an odd number of nibbles).
RLP rlp ( b . rlp ) ;
return std : : make_pair ( bytesConstRef ( b . key ) . cropped ( 1 ) , rlp [ rlp . itemCount ( ) = = 2 ? 1 : 16 ] . payload ( ) ) ;
}
template < class DB > void GenericTrieDB < DB > : : iterator : : next ( NibbleSlice _key )
{
NibbleSlice k = _key ;
while ( true )
{
if ( m_trail . empty ( ) )
{
m_that = nullptr ;
return ;
}
Node const & b = m_trail . back ( ) ;
RLP rlp ( b . rlp ) ;
if ( m_trail . back ( ) . child = = 255 )
{
// Entering. Look for first...
if ( rlp . isEmpty ( ) )
{
// Kill our search as soon as we hit an empty node.
k . clear ( ) ;
m_trail . pop_back ( ) ;
continue ;
}
if ( ! rlp . isList ( ) | | ( rlp . itemCount ( ) ! = 2 & & rlp . itemCount ( ) ! = 17 ) )
{
# if ETH_PARANOIA
cwarn < < " BIG FAT ERROR. STATE TRIE CORRUPTED!!!!! " ;
cwarn < < b . rlp . size ( ) < < toHex ( b . rlp ) ;
cwarn < < rlp ;
auto c = rlp . itemCount ( ) ;
cwarn < < c ;
BOOST_THROW_EXCEPTION ( InvalidTrie ( ) ) ;
# else
m_that = nullptr ;
return ;
# endif
}
if ( rlp . itemCount ( ) = = 2 )
{
// Just turn it into a valid Branch
auto keyOfRLP = keyOf ( rlp ) ;
// TODO: do something different depending on how keyOfRLP compares to k.mid(0, std::min(k.size(), keyOfRLP.size()));
// if == all is good - continue descent.
// if > discard key and continue descent.
// if < discard key and skip node.
if ( ! k . contains ( keyOfRLP ) )
{
if ( ! k . isEarlierThan ( keyOfRLP ) )
{
k . clear ( ) ;
m_trail . pop_back ( ) ;
continue ;
}
k . clear ( ) ;
}
k = k . mid ( std : : min ( k . size ( ) , keyOfRLP . size ( ) ) ) ;
m_trail . back ( ) . key = hexPrefixEncode ( keyOf ( m_trail . back ( ) . key ) , keyOfRLP , false ) ;
if ( isLeaf ( rlp ) )
{
// leaf - exit now.
if ( k . empty ( ) )
{
m_trail . back ( ) . child = 0 ;
return ;
}
// Still data in key we're supposed to be looking for when we're at a leaf. Go for next one.
k . clear ( ) ;
m_trail . pop_back ( ) ;
continue ;
}
// enter child.
m_trail . back ( ) . rlp = m_that - > deref ( rlp [ 1 ] ) ;
// no need to set .child as 255 - it's already done.
continue ;
}
else
{
// Already a branch - look for first valid.
if ( k . size ( ) )
{
m_trail . back ( ) . setChild ( k [ 0 ] ) ;
k = k . mid ( 1 ) ;
}
else
m_trail . back ( ) . setChild ( 16 ) ;
// run through to...
}
}
else
{
// Continuing/exiting. Look for next...
if ( ! ( rlp . isList ( ) & & rlp . itemCount ( ) = = 17 ) )
{
k . clear ( ) ;
m_trail . pop_back ( ) ;
continue ;
}
// else run through to...
m_trail . back ( ) . incrementChild ( ) ;
}
// ...here. should only get here if we're a list.
assert ( rlp . isList ( ) & & rlp . itemCount ( ) = = 17 ) ;
for ( ; ; m_trail . back ( ) . incrementChild ( ) )
if ( m_trail . back ( ) . child = = 17 )
{
// finished here.
k . clear ( ) ;
m_trail . pop_back ( ) ;
break ;
}
else if ( ! rlp [ m_trail . back ( ) . child ] . isEmpty ( ) )
{
if ( m_trail . back ( ) . child = = 16 )
return ; // have a value at this node - exit now.
else
{
// lead-on to another node - enter child.
// fixed so that Node passed into push_back is constructed *before* m_trail is potentially resized (which invalidates back and rlp)
Node const & back = m_trail . back ( ) ;
m_trail . push_back ( Node {
m_that - > deref ( rlp [ back . child ] ) ,
hexPrefixEncode ( keyOf ( back . key ) , NibbleSlice ( bytesConstRef ( & back . child , 1 ) , 1 ) , false ) ,
255
} ) ;
break ;
}
}
else
k . clear ( ) ;
}
}
template < class DB > void GenericTrieDB < DB > : : iterator : : next ( )
{
while ( true )
{
if ( m_trail . empty ( ) )
{
m_that = nullptr ;
return ;
}
Node const & b = m_trail . back ( ) ;
RLP rlp ( b . rlp ) ;
if ( m_trail . back ( ) . child = = 255 )
{
// Entering. Look for first...
if ( rlp . isEmpty ( ) )
{
m_trail . pop_back ( ) ;
continue ;
}
if ( ! ( rlp . isList ( ) & & ( rlp . itemCount ( ) = = 2 | | rlp . itemCount ( ) = = 17 ) ) )
{
# if ETH_PARANOIA
cwarn < < " BIG FAT ERROR. STATE TRIE CORRUPTED!!!!! " ;
cwarn < < b . rlp . size ( ) < < toHex ( b . rlp ) ;
cwarn < < rlp ;
auto c = rlp . itemCount ( ) ;
cwarn < < c ;
BOOST_THROW_EXCEPTION ( InvalidTrie ( ) ) ;
# else
m_that = nullptr ;
return ;
# endif
}
if ( rlp . itemCount ( ) = = 2 )
{
// Just turn it into a valid Branch
m_trail . back ( ) . key = hexPrefixEncode ( keyOf ( m_trail . back ( ) . key ) , keyOf ( rlp ) , false ) ;
if ( isLeaf ( rlp ) )
{
// leaf - exit now.
m_trail . back ( ) . child = 0 ;
return ;
}
// enter child.
m_trail . back ( ) . rlp = m_that - > deref ( rlp [ 1 ] ) ;
// no need to set .child as 255 - it's already done.
continue ;
}
else
{
// Already a branch - look for first valid.
m_trail . back ( ) . setFirstChild ( ) ;
// run through to...
}
}
else
{
// Continuing/exiting. Look for next...
if ( ! ( rlp . isList ( ) & & rlp . itemCount ( ) = = 17 ) )
{
m_trail . pop_back ( ) ;
continue ;
}
// else run through to...
m_trail . back ( ) . incrementChild ( ) ;
}
// ...here. should only get here if we're a list.
assert ( rlp . isList ( ) & & rlp . itemCount ( ) = = 17 ) ;
for ( ; ; m_trail . back ( ) . incrementChild ( ) )
if ( m_trail . back ( ) . child = = 17 )
{
// finished here.
m_trail . pop_back ( ) ;
break ;
}
else if ( ! rlp [ m_trail . back ( ) . child ] . isEmpty ( ) )
{
if ( m_trail . back ( ) . child = = 16 )
return ; // have a value at this node - exit now.
else
{
// lead-on to another node - enter child.
// fixed so that Node passed into push_back is constructed *before* m_trail is potentially resized (which invalidates back and rlp)
Node const & back = m_trail . back ( ) ;
m_trail . push_back ( Node {
m_that - > deref ( rlp [ back . child ] ) ,
hexPrefixEncode ( keyOf ( back . key ) , NibbleSlice ( bytesConstRef ( & back . child , 1 ) , 1 ) , false ) ,
255
} ) ;
break ;
}
}
}
}
template < class KeyType , class DB > typename TrieDB < KeyType , DB > : : iterator : : value_type TrieDB < KeyType , DB > : : iterator : : at ( ) const
{
auto p = Super : : at ( ) ;
value_type ret ;
assert ( p . first . size ( ) = = sizeof ( KeyType ) ) ;
memcpy ( & ret . first , p . first . data ( ) , sizeof ( KeyType ) ) ;
ret . second = p . second ;
return ret ;
}
template < class DB > void GenericTrieDB < DB > : : init ( )
{
m_root = insertNode ( & RLPNull ) ;
// std::cout << "Initialised root to " << m_root << std::endl;
assert ( node ( m_root ) . size ( ) ) ;
}
template < class DB > void GenericTrieDB < DB > : : insert ( bytesConstRef _key , bytesConstRef _value )
{
# if ETH_PARANOIA
tdebug < < " Insert " < < toHex ( _key . cropped ( 0 , 4 ) ) < < " => " < < toHex ( _value ) ;
# endif
std : : string rv = node ( m_root ) ;
assert ( rv . size ( ) ) ;
bytes b = mergeAt ( RLP ( rv ) , NibbleSlice ( _key ) , _value ) ;
// mergeAt won't attempt to delete the node if it's less than 32 bytes
// However, we know it's the root node and thus always hashed.
// So, if it's less than 32 (and thus should have been deleted but wasn't) then we delete it here.
if ( rv . size ( ) < 32 )
killNode ( m_root ) ;
m_root = insertNode ( & b ) ;
}
template < class DB > std : : string GenericTrieDB < DB > : : at ( bytesConstRef _key ) const
{
return atAux ( RLP ( node ( m_root ) ) , _key ) ;
}
template < class DB > std : : string GenericTrieDB < DB > : : atAux ( RLP const & _here , NibbleSlice _key ) const
{
if ( _here . isEmpty ( ) | | _here . isNull ( ) )
// not found.
return std : : string ( ) ;
assert ( _here . isList ( ) & & ( _here . itemCount ( ) = = 2 | | _here . itemCount ( ) = = 17 ) ) ;
if ( _here . itemCount ( ) = = 2 )
{
auto k = keyOf ( _here ) ;
if ( _key = = k & & isLeaf ( _here ) )
// reached leaf and it's us
return _here [ 1 ] . toString ( ) ;
else if ( _key . contains ( k ) & & ! isLeaf ( _here ) )
// not yet at leaf and it might yet be us. onwards...
return atAux ( _here [ 1 ] . isList ( ) ? _here [ 1 ] : RLP ( node ( _here [ 1 ] . toHash < h256 > ( ) ) ) , _key . mid ( k . size ( ) ) ) ;
else
// not us.
return std : : string ( ) ;
}
else
{
if ( _key . size ( ) = = 0 )
return _here [ 16 ] . toString ( ) ;
auto n = _here [ _key [ 0 ] ] ;
if ( n . isEmpty ( ) )
return std : : string ( ) ;
else
return atAux ( n . isList ( ) ? n : RLP ( node ( n . toHash < h256 > ( ) ) ) , _key . mid ( 1 ) ) ;
}
}
template < class DB > bytes GenericTrieDB < DB > : : mergeAt ( RLP const & _orig , NibbleSlice _k , bytesConstRef _v , bool _inLine )
{
# if ETH_PARANOIA
tdebug < < " mergeAt " < < _orig < < _k < < sha3 ( _orig . data ( ) ) . abridged ( ) ;
# endif
// The caller will make sure that the bytes are inserted properly.
// - This might mean inserting an entry into m_over
// We will take care to ensure that (our reference to) _orig is killed.
// Empty - just insert here
if ( _orig . isEmpty ( ) )
return place ( _orig , _k , _v ) ;
assert ( _orig . isList ( ) & & ( _orig . itemCount ( ) = = 2 | | _orig . itemCount ( ) = = 17 ) ) ;
if ( _orig . itemCount ( ) = = 2 )
{
// pair...
NibbleSlice k = keyOf ( _orig ) ;
// exactly our node - place value in directly.
if ( k = = _k & & isLeaf ( _orig ) )
return place ( _orig , _k , _v ) ;
// partial key is our key - move down.
if ( _k . contains ( k ) & & ! isLeaf ( _orig ) )
{
if ( ! _inLine )
killNode ( _orig ) ;
RLPStream s ( 2 ) ;
s . append ( _orig [ 0 ] ) ;
mergeAtAux ( s , _orig [ 1 ] , _k . mid ( k . size ( ) ) , _v ) ;
return s . out ( ) ;
}
auto sh = _k . shared ( k ) ;
// std::cout << _k << " sh " << k << " = " << sh << std::endl;
if ( sh )
{
// shared stuff - cleve at disagreement.
auto cleved = cleve ( _orig , sh ) ;
return mergeAt ( RLP ( cleved ) , _k , _v , true ) ;
}
else
{
// nothing shared - branch
auto branched = branch ( _orig ) ;
return mergeAt ( RLP ( branched ) , _k , _v , true ) ;
}
}
else
{
// branch...
// exactly our node - place value.
if ( _k . size ( ) = = 0 )
return place ( _orig , _k , _v ) ;
// Kill the node.
if ( ! _inLine )
killNode ( _orig ) ;
// not exactly our node - delve to next level at the correct index.
byte n = _k [ 0 ] ;
RLPStream r ( 17 ) ;
for ( byte i = 0 ; i < 17 ; + + i )
if ( i = = n )
mergeAtAux ( r , _orig [ i ] , _k . mid ( 1 ) , _v ) ;
else
r . append ( _orig [ i ] ) ;
return r . out ( ) ;
}
}
template < class DB > void GenericTrieDB < DB > : : mergeAtAux ( RLPStream & _out , RLP const & _orig , NibbleSlice _k , bytesConstRef _v )
{
# if ETH_PARANOIA
tdebug < < " mergeAtAux " < < _orig < < _k < < sha3 ( _orig . data ( ) ) . abridged ( ) < < ( ( _orig . isData ( ) & & _orig . size ( ) < = 32 ) ? _orig . toHash < h256 > ( ) . abridged ( ) : std : : string ( ) ) ;
# endif
RLP r = _orig ;
std : : string s ;
// _orig is always a segment of a node's RLP - removing it alone is pointless. However, if may be a hash, in which case we deref and we know it is removable.
bool isRemovable = false ;
if ( ! r . isList ( ) & & ! r . isEmpty ( ) )
{
s = node ( _orig . toHash < h256 > ( ) ) ;
r = RLP ( s ) ;
assert ( ! r . isNull ( ) ) ;
isRemovable = true ;
}
bytes b = mergeAt ( r , _k , _v , ! isRemovable ) ;
streamNode ( _out , b ) ;
}
template < class DB > void GenericTrieDB < DB > : : remove ( bytesConstRef _key )
{
# if ETH_PARANOIA
tdebug < < " Remove " < < toHex ( _key . cropped ( 0 , 4 ) . toBytes ( ) ) ;
# endif
std : : string rv = node ( m_root ) ;
bytes b = deleteAt ( RLP ( rv ) , NibbleSlice ( _key ) ) ;
if ( b . size ( ) )
{
if ( rv . size ( ) < 32 )
killNode ( m_root ) ;
m_root = insertNode ( & b ) ;
}
}
template < class DB > bool GenericTrieDB < DB > : : isTwoItemNode ( RLP const & _n ) const
{
return ( _n . isData ( ) & & RLP ( node ( _n . toHash < h256 > ( ) ) ) . itemCount ( ) = = 2 )
| | ( _n . isList ( ) & & _n . itemCount ( ) = = 2 ) ;
}
template < class DB > std : : string GenericTrieDB < DB > : : deref ( RLP const & _n ) const
{
return _n . isList ( ) ? _n . data ( ) . toString ( ) : node ( _n . toHash < h256 > ( ) ) ;
}
template < class DB > bytes GenericTrieDB < DB > : : deleteAt ( RLP const & _orig , NibbleSlice _k )
{
# if ETH_PARANOIA
tdebug < < " deleteAt " < < _orig < < _k < < sha3 ( _orig . data ( ) ) . abridged ( ) ;
# endif
// The caller will make sure that the bytes are inserted properly.
// - This might mean inserting an entry into m_over
// We will take care to ensure that (our reference to) _orig is killed.
// Empty - not found - no change.
if ( _orig . isEmpty ( ) )
return bytes ( ) ;
assert ( _orig . isList ( ) & & ( _orig . itemCount ( ) = = 2 | | _orig . itemCount ( ) = = 17 ) ) ;
if ( _orig . itemCount ( ) = = 2 )
{
// pair...
NibbleSlice k = keyOf ( _orig ) ;
// exactly our node - return null.
if ( k = = _k & & isLeaf ( _orig ) )
return RLPNull ;
// partial key is our key - move down.
if ( _k . contains ( k ) )
{
RLPStream s ;
s . appendList ( 2 ) < < _orig [ 0 ] ;
if ( ! deleteAtAux ( s , _orig [ 1 ] , _k . mid ( k . size ( ) ) ) )
return bytes ( ) ;
killNode ( _orig ) ;
RLP r ( s . out ( ) ) ;
if ( isTwoItemNode ( r [ 1 ] ) )
return graft ( r ) ;
return s . out ( ) ;
}
else
// not found - no change.
return bytes ( ) ;
}
else
{
// branch...
// exactly our node - remove and rejig.
if ( _k . size ( ) = = 0 & & ! _orig [ 16 ] . isEmpty ( ) )
{
// Kill the node.
killNode ( _orig ) ;
byte used = uniqueInUse ( _orig , 16 ) ;
if ( used ! = 255 )
if ( isTwoItemNode ( _orig [ used ] ) )
{
auto merged = merge ( _orig , used ) ;
return graft ( RLP ( merged ) ) ;
}
else
return merge ( _orig , used ) ;
else
{
RLPStream r ( 17 ) ;
for ( byte i = 0 ; i < 16 ; + + i )
r < < _orig [ i ] ;
r < < " " ;
return r . out ( ) ;
}
}
else
{
// not exactly our node - delve to next level at the correct index.
RLPStream r ( 17 ) ;
byte n = _k [ 0 ] ;
for ( byte i = 0 ; i < 17 ; + + i )
if ( i = = n )
if ( ! deleteAtAux ( r , _orig [ i ] , _k . mid ( 1 ) ) ) // bomb out if the key didn't turn up.
return bytes ( ) ;
else { }
else
r < < _orig [ i ] ;
// Kill the node.
killNode ( _orig ) ;
// check if we ended up leaving the node invalid.
RLP rlp ( r . out ( ) ) ;
byte used = uniqueInUse ( rlp , 255 ) ;
if ( used = = 255 ) // no - all ok.
return r . out ( ) ;
// yes; merge
if ( isTwoItemNode ( rlp [ used ] ) )
{
auto merged = merge ( rlp , used ) ;
return graft ( RLP ( merged ) ) ;
}
else
return merge ( rlp , used ) ;
}
}
}
template < class DB > bool GenericTrieDB < DB > : : deleteAtAux ( RLPStream & _out , RLP const & _orig , NibbleSlice _k )
{
# if ETH_PARANOIA
tdebug < < " deleteAtAux " < < _orig < < _k < < sha3 ( _orig . data ( ) ) . abridged ( ) < < ( ( _orig . isData ( ) & & _orig . size ( ) < = 32 ) ? _orig . toHash < h256 > ( ) . abridged ( ) : std : : string ( ) ) ;
# endif
bytes b = _orig . isEmpty ( ) ? bytes ( ) : deleteAt ( _orig . isList ( ) ? _orig : RLP ( node ( _orig . toHash < h256 > ( ) ) ) , _k ) ;
if ( ! b . size ( ) ) // not found - no change.
return false ;
/* if (_orig.isList())
killNode ( _orig ) ;
else
killNode ( _orig . toHash < h256 > ( ) ) ; */
streamNode ( _out , b ) ;
return true ;
}
template < class DB > bytes GenericTrieDB < DB > : : place ( RLP const & _orig , NibbleSlice _k , bytesConstRef _s )
{
# if ETH_PARANOIA
tdebug < < " place " < < _orig < < _k ;
# endif
killNode ( _orig ) ;
if ( _orig . isEmpty ( ) )
return ( RLPStream ( 2 ) < < hexPrefixEncode ( _k , true ) < < _s ) . out ( ) ;
assert ( _orig . isList ( ) & & ( _orig . itemCount ( ) = = 2 | | _orig . itemCount ( ) = = 17 ) ) ;
if ( _orig . itemCount ( ) = = 2 )
return ( RLPStream ( 2 ) < < _orig [ 0 ] < < _s ) . out ( ) ;
auto s = RLPStream ( 17 ) ;
for ( unsigned i = 0 ; i < 16 ; + + i )
s < < _orig [ i ] ;
s < < _s ;
return s . out ( ) ;
}
// in1: [K, S] (DEL)
// out1: null
// in2: [V0, ..., V15, S] (DEL)
// out2: [V0, ..., V15, null] iff exists i: !!Vi -- OR -- null otherwise
template < class DB > bytes GenericTrieDB < DB > : : remove ( RLP const & _orig )
{
# if ETH_PARANOIA
tdebug < < " kill " < < _orig ;
# endif
killNode ( _orig ) ;
assert ( _orig . isList ( ) & & ( _orig . itemCount ( ) = = 2 | | _orig . itemCount ( ) = = 17 ) ) ;
if ( _orig . itemCount ( ) = = 2 )
return RLPNull ;
RLPStream r ( 17 ) ;
for ( unsigned i = 0 ; i < 16 ; + + i )
r < < _orig [ i ] ;
r < < " " ;
return r . out ( ) ;
}
template < class DB > RLPStream & GenericTrieDB < DB > : : streamNode ( RLPStream & _s , bytes const & _b )
{
if ( _b . size ( ) < 32 )
_s . appendRaw ( _b ) ;
else
_s . append ( insertNode ( & _b ) ) ;
return _s ;
}
template < class DB > bytes GenericTrieDB < DB > : : cleve ( RLP const & _orig , unsigned _s )
{
# if ETH_PARANOIA
tdebug < < " cleve " < < _orig < < _s ;
# endif
killNode ( _orig ) ;
assert ( _orig . isList ( ) & & _orig . itemCount ( ) = = 2 ) ;
auto k = keyOf ( _orig ) ;
assert ( _s & & _s < = k . size ( ) ) ;
RLPStream bottom ( 2 ) ;
bottom < < hexPrefixEncode ( k , isLeaf ( _orig ) , /*ugh*/ ( int ) _s ) < < _orig [ 1 ] ;
RLPStream top ( 2 ) ;
top < < hexPrefixEncode ( k , false , 0 , /*ugh*/ ( int ) _s ) ;
streamNode ( top , bottom . out ( ) ) ;
return top . out ( ) ;
}
template < class DB > bytes GenericTrieDB < DB > : : graft ( RLP const & _orig )
{
# if ETH_PARANOIA
tdebug < < " graft " < < _orig ;
# endif
assert ( _orig . isList ( ) & & _orig . itemCount ( ) = = 2 ) ;
std : : string s ;
RLP n ;
if ( _orig [ 1 ] . isList ( ) )
n = _orig [ 1 ] ;
else
{
// remove second item from the trie after derefrencing it into s & n.
auto lh = _orig [ 1 ] . toHash < h256 > ( ) ;
s = node ( lh ) ;
killNode ( lh ) ;
n = RLP ( s ) ;
}
assert ( n . itemCount ( ) = = 2 ) ;
return ( RLPStream ( 2 ) < < hexPrefixEncode ( keyOf ( _orig ) , keyOf ( n ) , isLeaf ( n ) ) < < n [ 1 ] ) . out ( ) ;
// auto ret =
// std::cout << keyOf(_orig) << " ++ " << keyOf(n) << " == " << keyOf(RLP(ret)) << std::endl;
// return ret;
}
template < class DB > bytes GenericTrieDB < DB > : : merge ( RLP const & _orig , byte _i )
{
# if ETH_PARANOIA
tdebug < < " merge " < < _orig < < ( int ) _i ;
# endif
assert ( _orig . isList ( ) & & _orig . itemCount ( ) = = 17 ) ;
RLPStream s ( 2 ) ;
if ( _i ! = 16 )
{
assert ( ! _orig [ _i ] . isEmpty ( ) ) ;
s < < hexPrefixEncode ( bytesConstRef ( & _i , 1 ) , false , 1 , 2 , 0 ) ;
}
else
s < < hexPrefixEncode ( bytes ( ) , true ) ;
s < < _orig [ _i ] ;
return s . out ( ) ;
}
template < class DB > bytes GenericTrieDB < DB > : : branch ( RLP const & _orig )
{
# if ETH_PARANOIA
tdebug < < " branch " < < _orig ;
# endif
assert ( _orig . isList ( ) & & _orig . itemCount ( ) = = 2 ) ;
killNode ( _orig ) ;
auto k = keyOf ( _orig ) ;
RLPStream r ( 17 ) ;
if ( k . size ( ) = = 0 )
{
assert ( isLeaf ( _orig ) ) ;
for ( unsigned i = 0 ; i < 16 ; + + i )
r < < " " ;
r < < _orig [ 1 ] ;
}
else
{
byte b = k [ 0 ] ;
for ( unsigned i = 0 ; i < 16 ; + + i )
if ( i = = b )
if ( isLeaf ( _orig ) | | k . size ( ) > 1 )
{
RLPStream bottom ( 2 ) ;
bottom < < hexPrefixEncode ( k . mid ( 1 ) , isLeaf ( _orig ) ) < < _orig [ 1 ] ;
streamNode ( r , bottom . out ( ) ) ;
}
else
r < < _orig [ 1 ] ;
else
r < < " " ;
r < < " " ;
}
return r . out ( ) ;
}
}