github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/triedb/pathdb/disklayer.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/VictoriaMetrics/fastcache"
    24  	"github.com/ethereum/go-ethereum/common"
    25  	"github.com/ethereum/go-ethereum/core/rawdb"
    26  	"github.com/ethereum/go-ethereum/crypto"
    27  	"github.com/ethereum/go-ethereum/log"
    28  	"github.com/ethereum/go-ethereum/trie/trienode"
    29  	"github.com/ethereum/go-ethereum/trie/triestate"
    30  )
    31  
    32  // diskLayer is a low level persistent layer built on top of a key-value store.
    33  type diskLayer struct {
    34  	root   common.Hash      // Immutable, root hash to which this layer was made for
    35  	id     uint64           // Immutable, corresponding state id
    36  	db     *Database        // Path-based trie database
    37  	cleans *fastcache.Cache // GC friendly memory cache of clean node RLPs
    38  	buffer *nodebuffer      // Node buffer to aggregate writes
    39  	stale  bool             // Signals that the layer became stale (state progressed)
    40  	lock   sync.RWMutex     // Lock used to protect stale flag
    41  }
    42  
    43  // newDiskLayer creates a new disk layer based on the passing arguments.
    44  func newDiskLayer(root common.Hash, id uint64, db *Database, cleans *fastcache.Cache, buffer *nodebuffer) *diskLayer {
    45  	// Initialize a clean cache if the memory allowance is not zero
    46  	// or reuse the provided cache if it is not nil (inherited from
    47  	// the original disk layer).
    48  	if cleans == nil && db.config.CleanCacheSize != 0 {
    49  		cleans = fastcache.New(db.config.CleanCacheSize)
    50  	}
    51  	return &diskLayer{
    52  		root:   root,
    53  		id:     id,
    54  		db:     db,
    55  		cleans: cleans,
    56  		buffer: buffer,
    57  	}
    58  }
    59  
    60  // rootHash implements the layer interface, returning root hash of corresponding state.
    61  func (dl *diskLayer) rootHash() common.Hash {
    62  	return dl.root
    63  }
    64  
    65  // stateID implements the layer interface, returning the state id of disk layer.
    66  func (dl *diskLayer) stateID() uint64 {
    67  	return dl.id
    68  }
    69  
    70  // parentLayer implements the layer interface, returning nil as there's no layer
    71  // below the disk.
    72  func (dl *diskLayer) parentLayer() layer {
    73  	return nil
    74  }
    75  
    76  // isStale return whether this layer has become stale (was flattened across) or if
    77  // it's still live.
    78  func (dl *diskLayer) isStale() bool {
    79  	dl.lock.RLock()
    80  	defer dl.lock.RUnlock()
    81  
    82  	return dl.stale
    83  }
    84  
    85  // markStale sets the stale flag as true.
    86  func (dl *diskLayer) markStale() {
    87  	dl.lock.Lock()
    88  	defer dl.lock.Unlock()
    89  
    90  	if dl.stale {
    91  		panic("triedb disk layer is stale") // we've committed into the same base from two children, boom
    92  	}
    93  	dl.stale = true
    94  }
    95  
    96  // node implements the layer interface, retrieving the trie node with the
    97  // provided node info. No error will be returned if the node is not found.
    98  func (dl *diskLayer) node(owner common.Hash, path []byte, depth int) ([]byte, common.Hash, *nodeLoc, error) {
    99  	dl.lock.RLock()
   100  	defer dl.lock.RUnlock()
   101  
   102  	if dl.stale {
   103  		return nil, common.Hash{}, nil, errSnapshotStale
   104  	}
   105  	// Try to retrieve the trie node from the not-yet-written
   106  	// node buffer first. Note the buffer is lock free since
   107  	// it's impossible to mutate the buffer before tagging the
   108  	// layer as stale.
   109  	n, found := dl.buffer.node(owner, path)
   110  	if found {
   111  		dirtyHitMeter.Mark(1)
   112  		dirtyReadMeter.Mark(int64(len(n.Blob)))
   113  		dirtyNodeHitDepthHist.Update(int64(depth))
   114  		return n.Blob, n.Hash, &nodeLoc{loc: locDirtyCache, depth: depth}, nil
   115  	}
   116  	dirtyMissMeter.Mark(1)
   117  
   118  	// Try to retrieve the trie node from the clean memory cache
   119  	h := newHasher()
   120  	defer h.release()
   121  
   122  	key := cacheKey(owner, path)
   123  	if dl.cleans != nil {
   124  		if blob := dl.cleans.Get(nil, key); len(blob) > 0 {
   125  			cleanHitMeter.Mark(1)
   126  			cleanReadMeter.Mark(int64(len(blob)))
   127  			return blob, h.hash(blob), &nodeLoc{loc: locCleanCache, depth: depth}, nil
   128  		}
   129  		cleanMissMeter.Mark(1)
   130  	}
   131  	// Try to retrieve the trie node from the disk.
   132  	var blob []byte
   133  	if owner == (common.Hash{}) {
   134  		blob = rawdb.ReadAccountTrieNode(dl.db.diskdb, path)
   135  	} else {
   136  		blob = rawdb.ReadStorageTrieNode(dl.db.diskdb, owner, path)
   137  	}
   138  	if dl.cleans != nil && len(blob) > 0 {
   139  		dl.cleans.Set(key, blob)
   140  		cleanWriteMeter.Mark(int64(len(blob)))
   141  	}
   142  
   143  	return blob, h.hash(blob), &nodeLoc{loc: locDiskLayer, depth: depth}, nil
   144  }
   145  
   146  // update implements the layer interface, returning a new diff layer on top
   147  // with the given state set.
   148  func (dl *diskLayer) update(root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string]*trienode.Node, states *triestate.Set) *diffLayer {
   149  	return newDiffLayer(dl, root, id, block, nodes, states)
   150  }
   151  
   152  // commit merges the given bottom-most diff layer into the node buffer
   153  // and returns a newly constructed disk layer. Note the current disk
   154  // layer must be tagged as stale first to prevent re-access.
   155  func (dl *diskLayer) commit(bottom *diffLayer, force bool) (*diskLayer, error) {
   156  	dl.lock.Lock()
   157  	defer dl.lock.Unlock()
   158  
   159  	// Construct and store the state history first. If crash happens after storing
   160  	// the state history but without flushing the corresponding states(journal),
   161  	// the stored state history will be truncated from head in the next restart.
   162  	var (
   163  		overflow bool
   164  		oldest   uint64
   165  	)
   166  	if dl.db.freezer != nil {
   167  		err := writeHistory(dl.db.freezer, bottom)
   168  		if err != nil {
   169  			return nil, err
   170  		}
   171  		// Determine if the persisted history object has exceeded the configured
   172  		// limitation, set the overflow as true if so.
   173  		tail, err := dl.db.freezer.Tail()
   174  		if err != nil {
   175  			return nil, err
   176  		}
   177  		limit := dl.db.config.StateHistory
   178  		if limit != 0 && bottom.stateID()-tail > limit {
   179  			overflow = true
   180  			oldest = bottom.stateID() - limit + 1 // track the id of history **after truncation**
   181  		}
   182  	}
   183  	// Mark the diskLayer as stale before applying any mutations on top.
   184  	dl.stale = true
   185  
   186  	// Store the root->id lookup afterwards. All stored lookups are identified
   187  	// by the **unique** state root. It's impossible that in the same chain
   188  	// blocks are not adjacent but have the same root.
   189  	if dl.id == 0 {
   190  		rawdb.WriteStateID(dl.db.diskdb, dl.root, 0)
   191  	}
   192  	rawdb.WriteStateID(dl.db.diskdb, bottom.rootHash(), bottom.stateID())
   193  
   194  	// Construct a new disk layer by merging the nodes from the provided diff
   195  	// layer, and flush the content in disk layer if there are too many nodes
   196  	// cached. The clean cache is inherited from the original disk layer.
   197  	ndl := newDiskLayer(bottom.root, bottom.stateID(), dl.db, dl.cleans, dl.buffer.commit(bottom.nodes))
   198  
   199  	// In a unique scenario where the ID of the oldest history object (after tail
   200  	// truncation) surpasses the persisted state ID, we take the necessary action
   201  	// of forcibly committing the cached dirty nodes to ensure that the persisted
   202  	// state ID remains higher.
   203  	if !force && rawdb.ReadPersistentStateID(dl.db.diskdb) < oldest {
   204  		force = true
   205  	}
   206  	if err := ndl.buffer.flush(ndl.db.diskdb, ndl.cleans, ndl.id, force); err != nil {
   207  		return nil, err
   208  	}
   209  	// To remove outdated history objects from the end, we set the 'tail' parameter
   210  	// to 'oldest-1' due to the offset between the freezer index and the history ID.
   211  	if overflow {
   212  		pruned, err := truncateFromTail(ndl.db.diskdb, ndl.db.freezer, oldest-1)
   213  		if err != nil {
   214  			return nil, err
   215  		}
   216  		log.Debug("Pruned state history", "items", pruned, "tailid", oldest)
   217  	}
   218  	return ndl, nil
   219  }
   220  
   221  // revert applies the given state history and return a reverted disk layer.
   222  func (dl *diskLayer) revert(h *history, loader triestate.TrieLoader) (*diskLayer, error) {
   223  	if h.meta.root != dl.rootHash() {
   224  		return nil, errUnexpectedHistory
   225  	}
   226  	if dl.id == 0 {
   227  		return nil, fmt.Errorf("%w: zero state id", errStateUnrecoverable)
   228  	}
   229  	// Apply the reverse state changes upon the current state. This must
   230  	// be done before holding the lock in order to access state in "this"
   231  	// layer.
   232  	nodes, err := triestate.Apply(h.meta.parent, h.meta.root, h.accounts, h.storages, loader)
   233  	if err != nil {
   234  		return nil, err
   235  	}
   236  	// Mark the diskLayer as stale before applying any mutations on top.
   237  	dl.lock.Lock()
   238  	defer dl.lock.Unlock()
   239  
   240  	dl.stale = true
   241  
   242  	// State change may be applied to node buffer, or the persistent
   243  	// state, depends on if node buffer is empty or not. If the node
   244  	// buffer is not empty, it means that the state transition that
   245  	// needs to be reverted is not yet flushed and cached in node
   246  	// buffer, otherwise, manipulate persistent state directly.
   247  	if !dl.buffer.empty() {
   248  		err := dl.buffer.revert(dl.db.diskdb, nodes)
   249  		if err != nil {
   250  			return nil, err
   251  		}
   252  	} else {
   253  		batch := dl.db.diskdb.NewBatch()
   254  		writeNodes(batch, nodes, dl.cleans)
   255  		rawdb.WritePersistentStateID(batch, dl.id-1)
   256  		if err := batch.Write(); err != nil {
   257  			log.Crit("Failed to write states", "err", err)
   258  		}
   259  	}
   260  	return newDiskLayer(h.meta.parent, dl.id-1, dl.db, dl.cleans, dl.buffer), nil
   261  }
   262  
   263  // setBufferSize sets the node buffer size to the provided value.
   264  func (dl *diskLayer) setBufferSize(size int) error {
   265  	dl.lock.RLock()
   266  	defer dl.lock.RUnlock()
   267  
   268  	if dl.stale {
   269  		return errSnapshotStale
   270  	}
   271  	return dl.buffer.setSize(size, dl.db.diskdb, dl.cleans, dl.id)
   272  }
   273  
   274  // size returns the approximate size of cached nodes in the disk layer.
   275  func (dl *diskLayer) size() common.StorageSize {
   276  	dl.lock.RLock()
   277  	defer dl.lock.RUnlock()
   278  
   279  	if dl.stale {
   280  		return 0
   281  	}
   282  	return common.StorageSize(dl.buffer.size)
   283  }
   284  
   285  // resetCache releases the memory held by clean cache to prevent memory leak.
   286  func (dl *diskLayer) resetCache() {
   287  	dl.lock.RLock()
   288  	defer dl.lock.RUnlock()
   289  
   290  	// Stale disk layer loses the ownership of clean cache.
   291  	if dl.stale {
   292  		return
   293  	}
   294  	if dl.cleans != nil {
   295  		dl.cleans.Reset()
   296  	}
   297  }
   298  
   299  // hasher is used to compute the sha256 hash of the provided data.
   300  type hasher struct{ sha crypto.KeccakState }
   301  
   302  var hasherPool = sync.Pool{
   303  	New: func() interface{} { return &hasher{sha: crypto.NewKeccakState()} },
   304  }
   305  
   306  func newHasher() *hasher {
   307  	return hasherPool.Get().(*hasher)
   308  }
   309  
   310  func (h *hasher) hash(data []byte) common.Hash {
   311  	return crypto.HashData(h.sha, data)
   312  }
   313  
   314  func (h *hasher) release() {
   315  	hasherPool.Put(h)
   316  }