github.com/jimmyx0x/go-ethereum@v1.10.28/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  	tracer      *tracer
    37  	collectLeaf bool
    38  }
    39  
    40  // newCommitter creates a new committer or picks one from the pool.
    41  func newCommitter(owner common.Hash, tracer *tracer, collectLeaf bool) *committer {
    42  	return &committer{
    43  		nodes:       NewNodeSet(owner),
    44  		tracer:      tracer,
    45  		collectLeaf: collectLeaf,
    46  	}
    47  }
    48  
    49  // Commit collapses a node down into a hash node and returns it along with
    50  // the modified nodeset.
    51  func (c *committer) Commit(n node) (hashNode, *NodeSet, error) {
    52  	h, err := c.commit(nil, n)
    53  	if err != nil {
    54  		return nil, nil, err
    55  	}
    56  	// Some nodes can be deleted from trie which can't be captured
    57  	// by committer itself. Iterate all deleted nodes tracked by
    58  	// tracer and marked them as deleted only if they are present
    59  	// in database previously.
    60  	c.tracer.markDeletions(c.nodes)
    61  	return h.(hashNode), c.nodes, nil
    62  }
    63  
    64  // commit collapses a node down into a hash node and returns it.
    65  func (c *committer) commit(path []byte, n node) (node, error) {
    66  	// if this path is clean, use available cached data
    67  	hash, dirty := n.cache()
    68  	if hash != nil && !dirty {
    69  		return hash, nil
    70  	}
    71  	// Commit children, then parent, and remove the dirty flag.
    72  	switch cn := n.(type) {
    73  	case *shortNode:
    74  		// Commit child
    75  		collapsed := cn.copy()
    76  
    77  		// If the child is fullNode, recursively commit,
    78  		// otherwise it can only be hashNode or valueNode.
    79  		if _, ok := cn.Val.(*fullNode); ok {
    80  			childV, err := c.commit(append(path, cn.Key...), cn.Val)
    81  			if err != nil {
    82  				return nil, err
    83  			}
    84  			collapsed.Val = childV
    85  		}
    86  		// The key needs to be copied, since we're adding it to the
    87  		// modified nodeset.
    88  		collapsed.Key = hexToCompact(cn.Key)
    89  		hashedNode := c.store(path, collapsed)
    90  		if hn, ok := hashedNode.(hashNode); ok {
    91  			return hn, nil
    92  		}
    93  		// The short node now is embedded in its parent. Mark the node as
    94  		// deleted if it's present in database previously. It's equivalent
    95  		// as deletion from database's perspective.
    96  		if prev := c.tracer.getPrev(path); len(prev) != 0 {
    97  			c.nodes.markDeleted(path, prev)
    98  		}
    99  		return collapsed, nil
   100  	case *fullNode:
   101  		hashedKids, err := c.commitChildren(path, cn)
   102  		if err != nil {
   103  			return nil, err
   104  		}
   105  		collapsed := cn.copy()
   106  		collapsed.Children = hashedKids
   107  
   108  		hashedNode := c.store(path, collapsed)
   109  		if hn, ok := hashedNode.(hashNode); ok {
   110  			return hn, nil
   111  		}
   112  		// The full node now is embedded in its parent. Mark the node as
   113  		// deleted if it's present in database previously. It's equivalent
   114  		// as deletion from database's perspective.
   115  		if prev := c.tracer.getPrev(path); len(prev) != 0 {
   116  			c.nodes.markDeleted(path, prev)
   117  		}
   118  		return collapsed, nil
   119  	case hashNode:
   120  		return cn, nil
   121  	default:
   122  		// nil, valuenode shouldn't be committed
   123  		panic(fmt.Sprintf("%T: invalid node: %v", n, n))
   124  	}
   125  }
   126  
   127  // commitChildren commits the children of the given fullnode
   128  func (c *committer) commitChildren(path []byte, n *fullNode) ([17]node, error) {
   129  	var children [17]node
   130  	for i := 0; i < 16; i++ {
   131  		child := n.Children[i]
   132  		if child == nil {
   133  			continue
   134  		}
   135  		// If it's the hashed child, save the hash value directly.
   136  		// Note: it's impossible that the child in range [0, 15]
   137  		// is a valueNode.
   138  		if hn, ok := child.(hashNode); ok {
   139  			children[i] = hn
   140  			continue
   141  		}
   142  		// Commit the child recursively and store the "hashed" value.
   143  		// Note the returned node can be some embedded nodes, so it's
   144  		// possible the type is not hashNode.
   145  		hashed, err := c.commit(append(path, byte(i)), child)
   146  		if err != nil {
   147  			return children, err
   148  		}
   149  		children[i] = hashed
   150  	}
   151  	// For the 17th child, it's possible the type is valuenode.
   152  	if n.Children[16] != nil {
   153  		children[16] = n.Children[16]
   154  	}
   155  	return children, nil
   156  }
   157  
   158  // store hashes the node n and adds it to the modified nodeset. If leaf collection
   159  // is enabled, leaf nodes will be tracked in the modified nodeset as well.
   160  func (c *committer) store(path []byte, n node) node {
   161  	// Larger nodes are replaced by their hash and stored in the database.
   162  	var hash, _ = n.cache()
   163  
   164  	// This was not generated - must be a small node stored in the parent.
   165  	// In theory, we should check if the node is leaf here (embedded node
   166  	// usually is leaf node). But small value (less than 32bytes) is not
   167  	// our target (leaves in account trie only).
   168  	if hash == nil {
   169  		return n
   170  	}
   171  	// We have the hash already, estimate the RLP encoding-size of the node.
   172  	// The size is used for mem tracking, does not need to be exact
   173  	var (
   174  		size  = estimateSize(n)
   175  		nhash = common.BytesToHash(hash)
   176  		mnode = &memoryNode{
   177  			hash: nhash,
   178  			node: simplifyNode(n),
   179  			size: uint16(size),
   180  		}
   181  	)
   182  	// Collect the dirty node to nodeset for return.
   183  	c.nodes.markUpdated(path, mnode, c.tracer.getPrev(path))
   184  
   185  	// Collect the corresponding leaf node if it's required. We don't check
   186  	// full node since it's impossible to store value in fullNode. The key
   187  	// length of leaves should be exactly same.
   188  	if c.collectLeaf {
   189  		if sn, ok := n.(*shortNode); ok {
   190  			if val, ok := sn.Val.(valueNode); ok {
   191  				c.nodes.addLeaf(&leaf{blob: val, parent: nhash})
   192  			}
   193  		}
   194  	}
   195  	return hash
   196  }
   197  
   198  // estimateSize estimates the size of an rlp-encoded node, without actually
   199  // rlp-encoding it (zero allocs). This method has been experimentally tried, and with a trie
   200  // with 1000 leaves, the only errors above 1% are on small shortnodes, where this
   201  // method overestimates by 2 or 3 bytes (e.g. 37 instead of 35)
   202  func estimateSize(n node) int {
   203  	switch n := n.(type) {
   204  	case *shortNode:
   205  		// A short node contains a compacted key, and a value.
   206  		return 3 + len(n.Key) + estimateSize(n.Val)
   207  	case *fullNode:
   208  		// A full node contains up to 16 hashes (some nils), and a key
   209  		s := 3
   210  		for i := 0; i < 16; i++ {
   211  			if child := n.Children[i]; child != nil {
   212  				s += estimateSize(child)
   213  			} else {
   214  				s++
   215  			}
   216  		}
   217  		return s
   218  	case valueNode:
   219  		return 1 + len(n)
   220  	case hashNode:
   221  		return 1 + len(n)
   222  	default:
   223  		panic(fmt.Sprintf("node type %T", n))
   224  	}
   225  }