@ -23,7 +23,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
"golang.org/x/crypto/sha3"
"golang.org/x/crypto/sha3"
)
)
@ -33,10 +32,9 @@ const leafChanSize = 200
// leaf represents a trie leaf value
// leaf represents a trie leaf value
type leaf struct {
type leaf struct {
size int // size of the rlp data (estimate)
size int // size of the rlp data (estimate)
hash common . Hash // hash of rlp data
hash common . Hash // hash of rlp data
node node // the node to commit
node node // the node to commit
vnodes bool // set to true if the node (possibly) contains a valueNode
}
}
// committer is a type used for the trie Commit operation. A committer has some
// committer is a type used for the trie Commit operation. A committer has some
@ -74,18 +72,12 @@ func returnCommitterToPool(h *committer) {
committerPool . Put ( h )
committerPool . Put ( h )
}
}
// commitNeeded returns 'false' if the given node is already in sync with db
func ( c * committer ) commitNeeded ( n node ) bool {
hash , dirty := n . cache ( )
return hash == nil || dirty
}
// commit collapses a node down into a hash node and inserts it into the database
// commit collapses a node down into a hash node and inserts it into the database
func ( c * committer ) Commit ( n node , db * Database ) ( hashNode , error ) {
func ( c * committer ) Commit ( n node , db * Database ) ( hashNode , error ) {
if db == nil {
if db == nil {
return nil , errors . New ( "no db provided" )
return nil , errors . New ( "no db provided" )
}
}
h , err := c . commit ( n , db , true )
h , err := c . commit ( n , db )
if err != nil {
if err != nil {
return nil , err
return nil , err
}
}
@ -93,7 +85,7 @@ func (c *committer) Commit(n node, db *Database) (hashNode, error) {
}
}
// commit collapses a node down into a hash node and inserts it into the database
// commit collapses a node down into a hash node and inserts it into the database
func ( c * committer ) commit ( n node , db * Database , force bool ) ( node , error ) {
func ( c * committer ) commit ( n node , db * Database ) ( node , error ) {
// if this path is clean, use available cached data
// if this path is clean, use available cached data
hash , dirty := n . cache ( )
hash , dirty := n . cache ( )
if hash != nil && ! dirty {
if hash != nil && ! dirty {
@ -104,8 +96,11 @@ func (c *committer) commit(n node, db *Database, force bool) (node, error) {
case * shortNode :
case * shortNode :
// Commit child
// Commit child
collapsed := cn . copy ( )
collapsed := cn . copy ( )
if _ , ok := cn . Val . ( valueNode ) ; ! ok {
childV , err := c . commit ( cn . Val , db , false )
// If the child is fullnode, recursively commit.
// Otherwise it can only be hashNode or valueNode.
if _ , ok := cn . Val . ( * fullNode ) ; ok {
childV , err := c . commit ( cn . Val , db )
if err != nil {
if err != nil {
return nil , err
return nil , err
}
}
@ -113,78 +108,78 @@ func (c *committer) commit(n node, db *Database, force bool) (node, error) {
}
}
// The key needs to be copied, since we're delivering it to database
// The key needs to be copied, since we're delivering it to database
collapsed . Key = hexToCompact ( cn . Key )
collapsed . Key = hexToCompact ( cn . Key )
hashedNode := c . store ( collapsed , db , force , true )
hashedNode := c . store ( collapsed , db )
if hn , ok := hashedNode . ( hashNode ) ; ok {
if hn , ok := hashedNode . ( hashNode ) ; ok {
return hn , nil
return hn , nil
}
}
return collapsed , nil
return collapsed , nil
case * fullNode :
case * fullNode :
hashedKids , hasVnodes , err := c . commitChildren ( cn , db , force )
hashedKids , err := c . commitChildren ( cn , db )
if err != nil {
if err != nil {
return nil , err
return nil , err
}
}
collapsed := cn . copy ( )
collapsed := cn . copy ( )
collapsed . Children = hashedKids
collapsed . Children = hashedKids
hashedNode := c . store ( collapsed , db , force , hasVnodes )
hashedNode := c . store ( collapsed , db )
if hn , ok := hashedNode . ( hashNode ) ; ok {
if hn , ok := hashedNode . ( hashNode ) ; ok {
return hn , nil
return hn , nil
}
}
return collapsed , nil
return collapsed , nil
case valueNode :
return c . store ( cn , db , force , false ) , nil
// hashnodes aren't stored
case hashNode :
case hashNode :
return cn , nil
return cn , nil
default :
// nil, valuenode shouldn't be committed
panic ( fmt . Sprintf ( "%T: invalid node: %v" , n , n ) )
}
}
return hash , nil
}
}
// commitChildren commits the children of the given fullnode
// commitChildren commits the children of the given fullnode
func ( c * committer ) commitChildren ( n * fullNode , db * Database , force bool ) ( [ 17 ] node , bool , error ) {
func ( c * committer ) commitChildren ( n * fullNode , db * Database ) ( [ 17 ] node , error ) {
var children [ 17 ] node
var children [ 17 ] node
var hasValueNodeChildren = false
for i := 0 ; i < 16 ; i ++ {
for i , child := range n . Children {
child := n . Children [ i ]
if child == nil {
if child == nil {
continue
continue
}
}
hnode , err := c . commit ( child , db , false )
// If it's the hashed child, save the hash value directly.
if err != nil {
// Note: it's impossible that the child in range [0, 15]
return children , false , err
// is a valuenode.
if hn , ok := child . ( hashNode ) ; ok {
children [ i ] = hn
continue
}
}
children [ i ] = hnode
// Commit the child recursively and store the "hashed" value.
if _ , ok := hnode . ( valueNode ) ; ok {
// Note the returned node can be some embedded nodes, so it's
hasValueNodeChildren = true
// possible the type is not hashnode.
hashed , err := c . commit ( child , db )
if err != nil {
return children , err
}
}
children [ i ] = hashed
}
}
return children , hasValueNodeChildren , nil
// For the 17th child, it's possible the type is valuenode.
if n . Children [ 16 ] != nil {
children [ 16 ] = n . Children [ 16 ]
}
return children , nil
}
}
// store hashes the node n and if we have a storage layer specified, it writes
// store hashes the node n and if we have a storage layer specified, it writes
// the key/value pair to it and tracks any node->child references as well as any
// the key/value pair to it and tracks any node->child references as well as any
// node->external trie references.
// node->external trie references.
func ( c * committer ) store ( n node , db * Database , force bool , hasVnodeChildren bool ) node {
func ( c * committer ) store ( n node , db * Database ) node {
// Larger nodes are replaced by their hash and stored in the database.
// Larger nodes are replaced by their hash and stored in the database.
var (
var (
hash , _ = n . cache ( )
hash , _ = n . cache ( )
size int
size int
)
)
if hash == nil {
if hash == nil {
if vn , ok := n . ( valueNode ) ; ok {
// This was not generated - must be a small node stored in the parent.
c . tmp . Reset ( )
// In theory we should apply the leafCall here if it's not nil(embedded
if err := rlp . Encode ( & c . tmp , vn ) ; err != nil {
// node usually contains value). But small value(less than 32bytes) is
panic ( "encode error: " + err . Error ( ) )
// not our target.
}
return n
size = len ( c . tmp )
if size < 32 && ! force {
return n // Nodes smaller than 32 bytes are stored inside their parent
}
hash = c . makeHashNode ( c . tmp )
} else {
// This was not generated - must be a small node stored in the parent
// No need to do anything here
return n
}
} else {
} else {
// We have the hash already, estimate the RLP encoding-size of the node.
// We have the hash already, estimate the RLP encoding-size of the node.
// The size is used for mem tracking, does not need to be exact
// The size is used for mem tracking, does not need to be exact
@ -194,10 +189,9 @@ func (c *committer) store(n node, db *Database, force bool, hasVnodeChildren boo
// The leaf channel will be active only when there an active leaf-callback
// The leaf channel will be active only when there an active leaf-callback
if c . leafCh != nil {
if c . leafCh != nil {
c . leafCh <- & leaf {
c . leafCh <- & leaf {
size : size ,
size : size ,
hash : common . BytesToHash ( hash ) ,
hash : common . BytesToHash ( hash ) ,
node : n ,
node : n ,
vnodes : hasVnodeChildren ,
}
}
} else if db != nil {
} else if db != nil {
// No leaf-callback used, but there's still a database. Do serial
// No leaf-callback used, but there's still a database. Do serial
@ -209,30 +203,30 @@ func (c *committer) store(n node, db *Database, force bool, hasVnodeChildren boo
return hash
return hash
}
}
// commitLoop does the actual insert + leaf callback for nodes
// commitLoop does the actual insert + leaf callback for nodes.
func ( c * committer ) commitLoop ( db * Database ) {
func ( c * committer ) commitLoop ( db * Database ) {
for item := range c . leafCh {
for item := range c . leafCh {
var (
var (
hash = item . hash
hash = item . hash
size = item . size
size = item . size
n = item . node
n = item . node
hasVnodes = item . vnodes
)
)
// We are pooling the trie nodes into an intermediate memory cache
// We are pooling the trie nodes into an intermediate memory cache
db . lock . Lock ( )
db . lock . Lock ( )
db . insert ( hash , size , n )
db . insert ( hash , size , n )
db . lock . Unlock ( )
db . lock . Unlock ( )
if c . onleaf != nil && hasVnodes {
if c . onleaf != nil {
switch n := n . ( type ) {
switch n := n . ( type ) {
case * shortNode :
case * shortNode :
if child , ok := n . Val . ( valueNode ) ; ok {
if child , ok := n . Val . ( valueNode ) ; ok {
c . onleaf ( nil , child , hash )
c . onleaf ( nil , child , hash )
}
}
case * fullNode :
case * fullNode :
for i := 0 ; i < 16 ; i ++ {
// For children in range [0, 15], it's impossible
if child , ok := n . Children [ i ] . ( valueNode ) ; ok {
// to contain valuenode. Only check the 17th child.
c . onleaf ( nil , child , hash )
if n . Children [ 16 ] != nil {
}
c . onleaf ( nil , n . Children [ 16 ] . ( valueNode ) , hash )
}
}
}
}
}
}