github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/triedb/pathdb/difflayer.go (about)

     1  // Copyright 2022 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 pathdb
    18  
    19  import (
    20  	"fmt"
    21  	"sync"
    22  
    23  	"github.com/ethereum/go-ethereum/common"
    24  	"github.com/ethereum/go-ethereum/log"
    25  	"github.com/ethereum/go-ethereum/trie/trienode"
    26  	"github.com/ethereum/go-ethereum/trie/triestate"
    27  )
    28  
    29  // diffLayer represents a collection of modifications made to the in-memory tries
    30  // along with associated state changes after running a block on top.
    31  //
    32  // The goal of a diff layer is to act as a journal, tracking recent modifications
    33  // made to the state, that have not yet graduated into a semi-immutable state.
    34  type diffLayer struct {
    35  	// Immutables
    36  	root   common.Hash                               // Root hash to which this layer diff belongs to
    37  	id     uint64                                    // Corresponding state id
    38  	block  uint64                                    // Associated block number
    39  	nodes  map[common.Hash]map[string]*trienode.Node // Cached trie nodes indexed by owner and path
    40  	states *triestate.Set                            // Associated state change set for building history
    41  	memory uint64                                    // Approximate guess as to how much memory we use
    42  
    43  	parent layer        // Parent layer modified by this one, never nil, **can be changed**
    44  	lock   sync.RWMutex // Lock used to protect parent
    45  }
    46  
    47  // newDiffLayer creates a new diff layer on top of an existing layer.
    48  func newDiffLayer(parent layer, root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string]*trienode.Node, states *triestate.Set) *diffLayer {
    49  	var (
    50  		size  int64
    51  		count int
    52  	)
    53  	dl := &diffLayer{
    54  		root:   root,
    55  		id:     id,
    56  		block:  block,
    57  		nodes:  nodes,
    58  		states: states,
    59  		parent: parent,
    60  	}
    61  	for _, subset := range nodes {
    62  		for path, n := range subset {
    63  			dl.memory += uint64(n.Size() + len(path))
    64  			size += int64(len(n.Blob) + len(path))
    65  		}
    66  		count += len(subset)
    67  	}
    68  	if states != nil {
    69  		dl.memory += uint64(states.Size())
    70  	}
    71  	dirtyWriteMeter.Mark(size)
    72  	diffLayerNodesMeter.Mark(int64(count))
    73  	diffLayerBytesMeter.Mark(int64(dl.memory))
    74  	log.Debug("Created new diff layer", "id", id, "block", block, "nodes", count, "size", common.StorageSize(dl.memory))
    75  	return dl
    76  }
    77  
    78  // rootHash implements the layer interface, returning the root hash of
    79  // corresponding state.
    80  func (dl *diffLayer) rootHash() common.Hash {
    81  	return dl.root
    82  }
    83  
    84  // stateID implements the layer interface, returning the state id of the layer.
    85  func (dl *diffLayer) stateID() uint64 {
    86  	return dl.id
    87  }
    88  
    89  // parentLayer implements the layer interface, returning the subsequent
    90  // layer of the diff layer.
    91  func (dl *diffLayer) parentLayer() layer {
    92  	dl.lock.RLock()
    93  	defer dl.lock.RUnlock()
    94  
    95  	return dl.parent
    96  }
    97  
    98  // node implements the layer interface, retrieving the trie node blob with the
    99  // provided node information. No error will be returned if the node is not found.
   100  func (dl *diffLayer) node(owner common.Hash, path []byte, depth int) ([]byte, common.Hash, *nodeLoc, error) {
   101  	// Hold the lock, ensure the parent won't be changed during the
   102  	// state accessing.
   103  	dl.lock.RLock()
   104  	defer dl.lock.RUnlock()
   105  
   106  	// If the trie node is known locally, return it
   107  	subset, ok := dl.nodes[owner]
   108  	if ok {
   109  		n, ok := subset[string(path)]
   110  		if ok {
   111  			dirtyHitMeter.Mark(1)
   112  			dirtyNodeHitDepthHist.Update(int64(depth))
   113  			dirtyReadMeter.Mark(int64(len(n.Blob)))
   114  			return n.Blob, n.Hash, &nodeLoc{loc: locDiffLayer, depth: depth}, nil
   115  		}
   116  	}
   117  	// Trie node unknown to this layer, resolve from parent
   118  	return dl.parent.node(owner, path, depth+1)
   119  }
   120  
   121  // update implements the layer interface, creating a new layer on top of the
   122  // existing layer tree with the specified data items.
   123  func (dl *diffLayer) update(root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string]*trienode.Node, states *triestate.Set) *diffLayer {
   124  	return newDiffLayer(dl, root, id, block, nodes, states)
   125  }
   126  
   127  // persist flushes the diff layer and all its parent layers to disk layer.
   128  func (dl *diffLayer) persist(force bool) (layer, error) {
   129  	if parent, ok := dl.parentLayer().(*diffLayer); ok {
   130  		// Hold the lock to prevent any read operation until the new
   131  		// parent is linked correctly.
   132  		dl.lock.Lock()
   133  
   134  		// The merging of diff layers starts at the bottom-most layer,
   135  		// therefore we recurse down here, flattening on the way up
   136  		// (diffToDisk).
   137  		result, err := parent.persist(force)
   138  		if err != nil {
   139  			dl.lock.Unlock()
   140  			return nil, err
   141  		}
   142  		dl.parent = result
   143  		dl.lock.Unlock()
   144  	}
   145  	return diffToDisk(dl, force)
   146  }
   147  
   148  // diffToDisk merges a bottom-most diff into the persistent disk layer underneath
   149  // it. The method will panic if called onto a non-bottom-most diff layer.
   150  func diffToDisk(layer *diffLayer, force bool) (layer, error) {
   151  	disk, ok := layer.parentLayer().(*diskLayer)
   152  	if !ok {
   153  		panic(fmt.Sprintf("unknown layer type: %T", layer.parentLayer()))
   154  	}
   155  	return disk.commit(layer, force)
   156  }