github.com/ethereum/go-ethereum@v1.16.1/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  )
    26  
    27  // diffLayer represents a collection of modifications made to the in-memory tries
    28  // along with associated state changes after running a block on top.
    29  //
    30  // The purpose of a diff layer is to serve as a journal, recording recent state modifications
    31  // that have not yet been committed to a more stable or semi-permanent state.
    32  type diffLayer struct {
    33  	// Immutables
    34  	root   common.Hash         // Root hash to which this layer diff belongs to
    35  	id     uint64              // Corresponding state id
    36  	block  uint64              // Associated block number
    37  	nodes  *nodeSet            // Cached trie nodes indexed by owner and path
    38  	states *StateSetWithOrigin // Associated state changes along with origin value
    39  
    40  	parent layer        // Parent layer modified by this one, never nil, **can be changed**
    41  	lock   sync.RWMutex // Lock used to protect parent
    42  }
    43  
    44  // newDiffLayer creates a new diff layer on top of an existing layer.
    45  func newDiffLayer(parent layer, root common.Hash, id uint64, block uint64, nodes *nodeSet, states *StateSetWithOrigin) *diffLayer {
    46  	dl := &diffLayer{
    47  		root:   root,
    48  		id:     id,
    49  		block:  block,
    50  		parent: parent,
    51  		nodes:  nodes,
    52  		states: states,
    53  	}
    54  	dirtyNodeWriteMeter.Mark(int64(nodes.size))
    55  	dirtyStateWriteMeter.Mark(int64(states.size))
    56  	log.Debug("Created new diff layer", "id", id, "block", block, "nodesize", common.StorageSize(nodes.size), "statesize", common.StorageSize(states.size))
    57  	return dl
    58  }
    59  
    60  // rootHash implements the layer interface, returning the root hash of
    61  // corresponding state.
    62  func (dl *diffLayer) rootHash() common.Hash {
    63  	return dl.root
    64  }
    65  
    66  // stateID implements the layer interface, returning the state id of the layer.
    67  func (dl *diffLayer) stateID() uint64 {
    68  	return dl.id
    69  }
    70  
    71  // parentLayer implements the layer interface, returning the subsequent
    72  // layer of the diff layer.
    73  func (dl *diffLayer) parentLayer() layer {
    74  	dl.lock.RLock()
    75  	defer dl.lock.RUnlock()
    76  
    77  	return dl.parent
    78  }
    79  
    80  // node implements the layer interface, retrieving the trie node blob with the
    81  // provided node information. No error will be returned if the node is not found.
    82  func (dl *diffLayer) node(owner common.Hash, path []byte, depth int) ([]byte, common.Hash, *nodeLoc, error) {
    83  	// Hold the lock, ensure the parent won't be changed during the
    84  	// state accessing.
    85  	dl.lock.RLock()
    86  	defer dl.lock.RUnlock()
    87  
    88  	// If the trie node is known locally, return it
    89  	n, ok := dl.nodes.node(owner, path)
    90  	if ok {
    91  		dirtyNodeHitMeter.Mark(1)
    92  		dirtyNodeHitDepthHist.Update(int64(depth))
    93  		dirtyNodeReadMeter.Mark(int64(len(n.Blob)))
    94  		return n.Blob, n.Hash, &nodeLoc{loc: locDiffLayer, depth: depth}, nil
    95  	}
    96  	// Trie node unknown to this layer, resolve from parent
    97  	return dl.parent.node(owner, path, depth+1)
    98  }
    99  
   100  // account directly retrieves the account RLP associated with a particular
   101  // hash in the slim data format.
   102  //
   103  // Note the returned account is not a copy, please don't modify it.
   104  func (dl *diffLayer) account(hash common.Hash, depth int) ([]byte, error) {
   105  	// Hold the lock, ensure the parent won't be changed during the
   106  	// state accessing.
   107  	dl.lock.RLock()
   108  	defer dl.lock.RUnlock()
   109  
   110  	if blob, found := dl.states.account(hash); found {
   111  		dirtyStateHitMeter.Mark(1)
   112  		dirtyStateHitDepthHist.Update(int64(depth))
   113  		dirtyStateReadMeter.Mark(int64(len(blob)))
   114  
   115  		if len(blob) == 0 {
   116  			stateAccountInexMeter.Mark(1)
   117  		} else {
   118  			stateAccountExistMeter.Mark(1)
   119  		}
   120  		return blob, nil
   121  	}
   122  	// Account is unknown to this layer, resolve from parent
   123  	return dl.parent.account(hash, depth+1)
   124  }
   125  
   126  // storage directly retrieves the storage data associated with a particular hash,
   127  // within a particular account.
   128  //
   129  // Note the returned storage slot is not a copy, please don't modify it.
   130  func (dl *diffLayer) storage(accountHash, storageHash common.Hash, depth int) ([]byte, error) {
   131  	// Hold the lock, ensure the parent won't be changed during the
   132  	// state accessing.
   133  	dl.lock.RLock()
   134  	defer dl.lock.RUnlock()
   135  
   136  	if blob, found := dl.states.storage(accountHash, storageHash); found {
   137  		dirtyStateHitMeter.Mark(1)
   138  		dirtyStateHitDepthHist.Update(int64(depth))
   139  		dirtyStateReadMeter.Mark(int64(len(blob)))
   140  
   141  		if len(blob) == 0 {
   142  			stateStorageInexMeter.Mark(1)
   143  		} else {
   144  			stateStorageExistMeter.Mark(1)
   145  		}
   146  		return blob, nil
   147  	}
   148  	// storage slot is unknown to this layer, resolve from parent
   149  	return dl.parent.storage(accountHash, storageHash, depth+1)
   150  }
   151  
   152  // update implements the layer interface, creating a new layer on top of the
   153  // existing layer tree with the specified data items.
   154  func (dl *diffLayer) update(root common.Hash, id uint64, block uint64, nodes *nodeSet, states *StateSetWithOrigin) *diffLayer {
   155  	return newDiffLayer(dl, root, id, block, nodes, states)
   156  }
   157  
   158  // persist flushes the diff layer and all its parent layers to disk layer.
   159  func (dl *diffLayer) persist(force bool) (*diskLayer, error) {
   160  	if parent, ok := dl.parentLayer().(*diffLayer); ok {
   161  		// Hold the lock to prevent any read operation until the new
   162  		// parent is linked correctly.
   163  		dl.lock.Lock()
   164  
   165  		// The merging of diff layers starts at the bottom-most layer,
   166  		// therefore we recurse down here, flattening on the way up
   167  		// (diffToDisk).
   168  		result, err := parent.persist(force)
   169  		if err != nil {
   170  			dl.lock.Unlock()
   171  			return nil, err
   172  		}
   173  		dl.parent = result
   174  		dl.lock.Unlock()
   175  	}
   176  	return diffToDisk(dl, force)
   177  }
   178  
   179  // size returns the approximate memory size occupied by this diff layer.
   180  func (dl *diffLayer) size() uint64 {
   181  	return dl.nodes.size + dl.states.size
   182  }
   183  
   184  // diffToDisk merges a bottom-most diff into the persistent disk layer underneath
   185  // it. The method will panic if called onto a non-bottom-most diff layer.
   186  func diffToDisk(layer *diffLayer, force bool) (*diskLayer, error) {
   187  	disk, ok := layer.parentLayer().(*diskLayer)
   188  	if !ok {
   189  		panic(fmt.Sprintf("unknown layer type: %T", layer.parentLayer()))
   190  	}
   191  	return disk.commit(layer, force)
   192  }