github.com/ethereum/go-ethereum@v1.16.1/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  	"sync"
    22  
    23  	"github.com/ethereum/go-ethereum/common"
    24  	"github.com/ethereum/go-ethereum/trie/trienode"
    25  )
    26  
    27  // committer is the tool used for the trie Commit operation. The committer will
    28  // capture all dirty nodes during the commit process and keep them cached in
    29  // insertion order.
    30  type committer struct {
    31  	nodes       *trienode.NodeSet
    32  	tracer      *tracer
    33  	collectLeaf bool
    34  }
    35  
    36  // newCommitter creates a new committer or picks one from the pool.
    37  func newCommitter(nodeset *trienode.NodeSet, tracer *tracer, collectLeaf bool) *committer {
    38  	return &committer{
    39  		nodes:       nodeset,
    40  		tracer:      tracer,
    41  		collectLeaf: collectLeaf,
    42  	}
    43  }
    44  
    45  // Commit collapses a node down into a hash node.
    46  func (c *committer) Commit(n node, parallel bool) hashNode {
    47  	return c.commit(nil, n, parallel).(hashNode)
    48  }
    49  
    50  // commit collapses a node down into a hash node and returns it.
    51  func (c *committer) commit(path []byte, n node, parallel bool) node {
    52  	// if this path is clean, use available cached data
    53  	hash, dirty := n.cache()
    54  	if hash != nil && !dirty {
    55  		return hash
    56  	}
    57  	// Commit children, then parent, and remove the dirty flag.
    58  	switch cn := n.(type) {
    59  	case *shortNode:
    60  		// If the child is fullNode, recursively commit,
    61  		// otherwise it can only be hashNode or valueNode.
    62  		if _, ok := cn.Val.(*fullNode); ok {
    63  			cn.Val = c.commit(append(path, cn.Key...), cn.Val, false)
    64  		}
    65  		// The key needs to be copied, since we're adding it to the
    66  		// modified nodeset.
    67  		cn.Key = hexToCompact(cn.Key)
    68  		hashedNode := c.store(path, cn)
    69  		if hn, ok := hashedNode.(hashNode); ok {
    70  			return hn
    71  		}
    72  		return cn
    73  	case *fullNode:
    74  		c.commitChildren(path, cn, parallel)
    75  		hashedNode := c.store(path, cn)
    76  		if hn, ok := hashedNode.(hashNode); ok {
    77  			return hn
    78  		}
    79  		return cn
    80  	case hashNode:
    81  		return cn
    82  	default:
    83  		// nil, valuenode shouldn't be committed
    84  		panic(fmt.Sprintf("%T: invalid node: %v", n, n))
    85  	}
    86  }
    87  
    88  // commitChildren commits the children of the given fullnode
    89  func (c *committer) commitChildren(path []byte, n *fullNode, parallel bool) {
    90  	var (
    91  		wg      sync.WaitGroup
    92  		nodesMu sync.Mutex
    93  	)
    94  	for i := 0; i < 16; i++ {
    95  		child := n.Children[i]
    96  		if child == nil {
    97  			continue
    98  		}
    99  		// If it's the hashed child, save the hash value directly.
   100  		// Note: it's impossible that the child in range [0, 15]
   101  		// is a valueNode.
   102  		if _, ok := child.(hashNode); ok {
   103  			continue
   104  		}
   105  		// Commit the child recursively and store the "hashed" value.
   106  		// Note the returned node can be some embedded nodes, so it's
   107  		// possible the type is not hashNode.
   108  		if !parallel {
   109  			n.Children[i] = c.commit(append(path, byte(i)), child, false)
   110  		} else {
   111  			wg.Add(1)
   112  			go func(index int) {
   113  				p := append(path, byte(index))
   114  				childSet := trienode.NewNodeSet(c.nodes.Owner)
   115  				childCommitter := newCommitter(childSet, c.tracer, c.collectLeaf)
   116  				n.Children[index] = childCommitter.commit(p, child, false)
   117  				nodesMu.Lock()
   118  				c.nodes.MergeSet(childSet)
   119  				nodesMu.Unlock()
   120  				wg.Done()
   121  			}(i)
   122  		}
   123  	}
   124  	if parallel {
   125  		wg.Wait()
   126  	}
   127  }
   128  
   129  // store hashes the node n and adds it to the modified nodeset. If leaf collection
   130  // is enabled, leaf nodes will be tracked in the modified nodeset as well.
   131  func (c *committer) store(path []byte, n node) node {
   132  	// Larger nodes are replaced by their hash and stored in the database.
   133  	var hash, _ = n.cache()
   134  
   135  	// This was not generated - must be a small node stored in the parent.
   136  	// In theory, we should check if the node is leaf here (embedded node
   137  	// usually is leaf node). But small value (less than 32bytes) is not
   138  	// our target (leaves in account trie only).
   139  	if hash == nil {
   140  		// The node is embedded in its parent, in other words, this node
   141  		// will not be stored in the database independently, mark it as
   142  		// deleted only if the node was existent in database before.
   143  		_, ok := c.tracer.accessList[string(path)]
   144  		if ok {
   145  			c.nodes.AddNode(path, trienode.NewDeleted())
   146  		}
   147  		return n
   148  	}
   149  	// Collect the dirty node to nodeset for return.
   150  	nhash := common.BytesToHash(hash)
   151  	c.nodes.AddNode(path, trienode.New(nhash, nodeToBytes(n)))
   152  
   153  	// Collect the corresponding leaf node if it's required. We don't check
   154  	// full node since it's impossible to store value in fullNode. The key
   155  	// length of leaves should be exactly same.
   156  	if c.collectLeaf {
   157  		if sn, ok := n.(*shortNode); ok {
   158  			if val, ok := sn.Val.(valueNode); ok {
   159  				c.nodes.AddLeaf(nhash, val)
   160  			}
   161  		}
   162  	}
   163  	return hash
   164  }
   165  
   166  // ForGatherChildren decodes the provided node and traverses the children inside.
   167  func ForGatherChildren(node []byte, onChild func(common.Hash)) {
   168  	forGatherChildren(mustDecodeNodeUnsafe(nil, node), onChild)
   169  }
   170  
   171  // forGatherChildren traverses the node hierarchy and invokes the callback
   172  // for all the hashnode children.
   173  func forGatherChildren(n node, onChild func(hash common.Hash)) {
   174  	switch n := n.(type) {
   175  	case *shortNode:
   176  		forGatherChildren(n.Val, onChild)
   177  	case *fullNode:
   178  		for i := 0; i < 16; i++ {
   179  			forGatherChildren(n.Children[i], onChild)
   180  		}
   181  	case hashNode:
   182  		onChild(common.BytesToHash(n))
   183  	case valueNode, nil:
   184  	default:
   185  		panic(fmt.Sprintf("unknown node type: %T", n))
   186  	}
   187  }