github.com/bcnmy/go-ethereum@v1.10.27/trie/committer.go (about)

     1  // Copyright 2020 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package trie
    18  
    19  import (
    20  	"fmt"
    21  
    22  	"github.com/ethereum/go-ethereum/common"
    23  )
    24  
    25  // leaf represents a trie leaf node
    26  type leaf struct {
    27  	blob   []byte      // raw blob of leaf
    28  	parent common.Hash // the hash of parent node
    29  }
    30  
    31  // committer is the tool used for the trie Commit operation. The committer will
    32  // capture all dirty nodes during the commit process and keep them cached in
    33  // insertion order.
    34  type committer struct {
    35  	nodes       *NodeSet
    36  	collectLeaf bool
    37  }
    38  
    39  // newCommitter creates a new committer or picks one from the pool.
    40  func newCommitter(owner common.Hash, collectLeaf bool) *committer {
    41  	return &committer{
    42  		nodes:       NewNodeSet(owner),
    43  		collectLeaf: collectLeaf,
    44  	}
    45  }
    46  
    47  // Commit collapses a node down into a hash node and inserts it into the database
    48  func (c *committer) Commit(n node) (hashNode, *NodeSet, error) {
    49  	h, err := c.commit(nil, n)
    50  	if err != nil {
    51  		return nil, nil, err
    52  	}
    53  	return h.(hashNode), c.nodes, nil
    54  }
    55  
    56  // commit collapses a node down into a hash node and inserts it into the database
    57  func (c *committer) commit(path []byte, n node) (node, error) {
    58  	// if this path is clean, use available cached data
    59  	hash, dirty := n.cache()
    60  	if hash != nil && !dirty {
    61  		return hash, nil
    62  	}
    63  	// Commit children, then parent, and remove the dirty flag.
    64  	switch cn := n.(type) {
    65  	case *shortNode:
    66  		// Commit child
    67  		collapsed := cn.copy()
    68  
    69  		// If the child is fullNode, recursively commit,
    70  		// otherwise it can only be hashNode or valueNode.
    71  		if _, ok := cn.Val.(*fullNode); ok {
    72  			childV, err := c.commit(append(path, cn.Key...), cn.Val)
    73  			if err != nil {
    74  				return nil, err
    75  			}
    76  			collapsed.Val = childV
    77  		}
    78  		// The key needs to be copied, since we're delivering it to database
    79  		collapsed.Key = hexToCompact(cn.Key)
    80  		hashedNode := c.store(path, collapsed)
    81  		if hn, ok := hashedNode.(hashNode); ok {
    82  			return hn, nil
    83  		}
    84  		return collapsed, nil
    85  	case *fullNode:
    86  		hashedKids, err := c.commitChildren(path, cn)
    87  		if err != nil {
    88  			return nil, err
    89  		}
    90  		collapsed := cn.copy()
    91  		collapsed.Children = hashedKids
    92  
    93  		hashedNode := c.store(path, collapsed)
    94  		if hn, ok := hashedNode.(hashNode); ok {
    95  			return hn, nil
    96  		}
    97  		return collapsed, nil
    98  	case hashNode:
    99  		return cn, nil
   100  	default:
   101  		// nil, valuenode shouldn't be committed
   102  		panic(fmt.Sprintf("%T: invalid node: %v", n, n))
   103  	}
   104  }
   105  
   106  // commitChildren commits the children of the given fullnode
   107  func (c *committer) commitChildren(path []byte, n *fullNode) ([17]node, error) {
   108  	var children [17]node
   109  	for i := 0; i < 16; i++ {
   110  		child := n.Children[i]
   111  		if child == nil {
   112  			continue
   113  		}
   114  		// If it's the hashed child, save the hash value directly.
   115  		// Note: it's impossible that the child in range [0, 15]
   116  		// is a valueNode.
   117  		if hn, ok := child.(hashNode); ok {
   118  			children[i] = hn
   119  			continue
   120  		}
   121  		// Commit the child recursively and store the "hashed" value.
   122  		// Note the returned node can be some embedded nodes, so it's
   123  		// possible the type is not hashNode.
   124  		hashed, err := c.commit(append(path, byte(i)), child)
   125  		if err != nil {
   126  			return children, err
   127  		}
   128  		children[i] = hashed
   129  	}
   130  	// For the 17th child, it's possible the type is valuenode.
   131  	if n.Children[16] != nil {
   132  		children[16] = n.Children[16]
   133  	}
   134  	return children, nil
   135  }
   136  
   137  // store hashes the node n and if we have a storage layer specified, it writes
   138  // the key/value pair to it and tracks any node->child references as well as any
   139  // node->external trie references.
   140  func (c *committer) store(path []byte, n node) node {
   141  	// Larger nodes are replaced by their hash and stored in the database.
   142  	var hash, _ = n.cache()
   143  
   144  	// This was not generated - must be a small node stored in the parent.
   145  	// In theory, we should check if the node is leaf here (embedded node
   146  	// usually is leaf node). But small value(less than 32bytes) is not
   147  	// our target(leaves in account trie only).
   148  	if hash == nil {
   149  		return n
   150  	}
   151  	// We have the hash already, estimate the RLP encoding-size of the node.
   152  	// The size is used for mem tracking, does not need to be exact
   153  	var (
   154  		size  = estimateSize(n)
   155  		nhash = common.BytesToHash(hash)
   156  		mnode = &memoryNode{
   157  			hash: nhash,
   158  			node: simplifyNode(n),
   159  			size: uint16(size),
   160  		}
   161  	)
   162  	// Collect the dirty node to nodeset for return.
   163  	c.nodes.add(string(path), mnode)
   164  
   165  	// Collect the corresponding leaf node if it's required. We don't check
   166  	// full node since it's impossible to store value in fullNode. The key
   167  	// length of leaves should be exactly same.
   168  	if c.collectLeaf {
   169  		if sn, ok := n.(*shortNode); ok {
   170  			if val, ok := sn.Val.(valueNode); ok {
   171  				c.nodes.addLeaf(&leaf{blob: val, parent: nhash})
   172  			}
   173  		}
   174  	}
   175  	return hash
   176  }
   177  
   178  // estimateSize estimates the size of an rlp-encoded node, without actually
   179  // rlp-encoding it (zero allocs). This method has been experimentally tried, and with a trie
   180  // with 1000 leaves, the only errors above 1% are on small shortnodes, where this
   181  // method overestimates by 2 or 3 bytes (e.g. 37 instead of 35)
   182  func estimateSize(n node) int {
   183  	switch n := n.(type) {
   184  	case *shortNode:
   185  		// A short node contains a compacted key, and a value.
   186  		return 3 + len(n.Key) + estimateSize(n.Val)
   187  	case *fullNode:
   188  		// A full node contains up to 16 hashes (some nils), and a key
   189  		s := 3
   190  		for i := 0; i < 16; i++ {
   191  			if child := n.Children[i]; child != nil {
   192  				s += estimateSize(child)
   193  			} else {
   194  				s++
   195  			}
   196  		}
   197  		return s
   198  	case valueNode:
   199  		return 1 + len(n)
   200  	case hashNode:
   201  		return 1 + len(n)
   202  	default:
   203  		panic(fmt.Sprintf("node type %T", n))
   204  	}
   205  }