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