github.com/MetalBlockchain/subnet-evm@v0.4.9/trie/committer.go (about)

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