github.com/ethereum/go-ethereum@v1.16.1/triedb/pathdb/journal.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  	"bytes"
    21  	"errors"
    22  	"fmt"
    23  	"io"
    24  	"time"
    25  
    26  	"github.com/ethereum/go-ethereum/common"
    27  	"github.com/ethereum/go-ethereum/core/rawdb"
    28  	"github.com/ethereum/go-ethereum/core/types"
    29  	"github.com/ethereum/go-ethereum/ethdb"
    30  	"github.com/ethereum/go-ethereum/log"
    31  	"github.com/ethereum/go-ethereum/rlp"
    32  )
    33  
    34  var (
    35  	errMissJournal       = errors.New("journal not found")
    36  	errMissVersion       = errors.New("version not found")
    37  	errUnexpectedVersion = errors.New("unexpected journal version")
    38  	errMissDiskRoot      = errors.New("disk layer root not found")
    39  	errUnmatchedJournal  = errors.New("unmatched journal")
    40  )
    41  
    42  // journalVersion ensures that an incompatible journal is detected and discarded.
    43  //
    44  // Changelog:
    45  //
    46  // - Version 0: initial version
    47  // - Version 1: storage.Incomplete field is removed
    48  // - Version 2: add post-modification state values
    49  // - Version 3: a flag has been added to indicate whether the storage slot key is the raw key or a hash
    50  const journalVersion uint64 = 3
    51  
    52  // loadJournal tries to parse the layer journal from the disk.
    53  func (db *Database) loadJournal(diskRoot common.Hash) (layer, error) {
    54  	journal := rawdb.ReadTrieJournal(db.diskdb)
    55  	if len(journal) == 0 {
    56  		return nil, errMissJournal
    57  	}
    58  	r := rlp.NewStream(bytes.NewReader(journal), 0)
    59  
    60  	// Firstly, resolve the first element as the journal version
    61  	version, err := r.Uint64()
    62  	if err != nil {
    63  		return nil, errMissVersion
    64  	}
    65  	if version != journalVersion {
    66  		return nil, fmt.Errorf("%w want %d got %d", errUnexpectedVersion, journalVersion, version)
    67  	}
    68  	// Secondly, resolve the disk layer root, ensure it's continuous
    69  	// with disk layer. Note now we can ensure it's the layer journal
    70  	// correct version, so we expect everything can be resolved properly.
    71  	var root common.Hash
    72  	if err := r.Decode(&root); err != nil {
    73  		return nil, errMissDiskRoot
    74  	}
    75  	// The journal is not matched with persistent state, discard them.
    76  	// It can happen that geth crashes without persisting the journal.
    77  	if !bytes.Equal(root.Bytes(), diskRoot.Bytes()) {
    78  		return nil, fmt.Errorf("%w want %x got %x", errUnmatchedJournal, root, diskRoot)
    79  	}
    80  	// Load the disk layer from the journal
    81  	base, err := db.loadDiskLayer(r)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  	// Load all the diff layers from the journal
    86  	head, err := db.loadDiffLayer(base, r)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	log.Debug("Loaded layer journal", "diskroot", diskRoot, "diffhead", head.rootHash())
    91  	return head, nil
    92  }
    93  
    94  // journalGenerator is a disk layer entry containing the generator progress marker.
    95  type journalGenerator struct {
    96  	// Indicator that whether the database was in progress of being wiped.
    97  	// It's deprecated but keep it here for backward compatibility.
    98  	Wiping bool
    99  
   100  	Done     bool // Whether the generator finished creating the snapshot
   101  	Marker   []byte
   102  	Accounts uint64
   103  	Slots    uint64
   104  	Storage  uint64
   105  }
   106  
   107  // loadGenerator loads the state generation progress marker from the database.
   108  func loadGenerator(db ethdb.KeyValueReader, hash nodeHasher) (*journalGenerator, common.Hash, error) {
   109  	trieRoot, err := hash(rawdb.ReadAccountTrieNode(db, nil))
   110  	if err != nil {
   111  		return nil, common.Hash{}, err
   112  	}
   113  	// State generation progress marker is lost, rebuild it
   114  	blob := rawdb.ReadSnapshotGenerator(db)
   115  	if len(blob) == 0 {
   116  		log.Info("State snapshot generator is not found")
   117  		return nil, trieRoot, nil
   118  	}
   119  	// State generation progress marker is not compatible, rebuild it
   120  	var generator journalGenerator
   121  	if err := rlp.DecodeBytes(blob, &generator); err != nil {
   122  		log.Info("State snapshot generator is not compatible")
   123  		return nil, trieRoot, nil
   124  	}
   125  	// The state snapshot is inconsistent with the trie data and must
   126  	// be rebuilt.
   127  	//
   128  	// Note: The SnapshotRoot and SnapshotGenerator are always consistent
   129  	// with each other, both in the legacy state snapshot and the path database.
   130  	// Therefore, if the SnapshotRoot does not match the trie root,
   131  	// the entire generator is considered stale and must be discarded.
   132  	stateRoot := rawdb.ReadSnapshotRoot(db)
   133  	if trieRoot != stateRoot {
   134  		log.Info("State snapshot is not consistent", "trie", trieRoot, "state", stateRoot)
   135  		return nil, trieRoot, nil
   136  	}
   137  	// Slice null-ness is lost after rlp decoding, reset it back to empty
   138  	if !generator.Done && generator.Marker == nil {
   139  		generator.Marker = []byte{}
   140  	}
   141  	return &generator, trieRoot, nil
   142  }
   143  
   144  // loadLayers loads a pre-existing state layer backed by a key-value store.
   145  func (db *Database) loadLayers() layer {
   146  	// Retrieve the root node of persistent state.
   147  	root, err := db.hasher(rawdb.ReadAccountTrieNode(db.diskdb, nil))
   148  	if err != nil {
   149  		log.Crit("Failed to compute node hash", "err", err)
   150  	}
   151  	// Load the layers by resolving the journal
   152  	head, err := db.loadJournal(root)
   153  	if err == nil {
   154  		return head
   155  	}
   156  	// journal is not matched(or missing) with the persistent state, discard
   157  	// it. Display log for discarding journal, but try to avoid showing
   158  	// useless information when the db is created from scratch.
   159  	if !(root == types.EmptyRootHash && errors.Is(err, errMissJournal)) {
   160  		log.Info("Failed to load journal, discard it", "err", err)
   161  	}
   162  	// Return single layer with persistent state.
   163  	return newDiskLayer(root, rawdb.ReadPersistentStateID(db.diskdb), db, nil, nil, newBuffer(db.config.WriteBufferSize, nil, nil, 0), nil)
   164  }
   165  
   166  // loadDiskLayer reads the binary blob from the layer journal, reconstructing
   167  // a new disk layer on it.
   168  func (db *Database) loadDiskLayer(r *rlp.Stream) (layer, error) {
   169  	// Resolve disk layer root
   170  	var root common.Hash
   171  	if err := r.Decode(&root); err != nil {
   172  		return nil, fmt.Errorf("load disk root: %v", err)
   173  	}
   174  	// Resolve the state id of disk layer, it can be different
   175  	// with the persistent id tracked in disk, the id distance
   176  	// is the number of transitions aggregated in disk layer.
   177  	var id uint64
   178  	if err := r.Decode(&id); err != nil {
   179  		return nil, fmt.Errorf("load state id: %v", err)
   180  	}
   181  	stored := rawdb.ReadPersistentStateID(db.diskdb)
   182  	if stored > id {
   183  		return nil, fmt.Errorf("invalid state id: stored %d resolved %d", stored, id)
   184  	}
   185  	// Resolve nodes cached in aggregated buffer
   186  	var nodes nodeSet
   187  	if err := nodes.decode(r); err != nil {
   188  		return nil, err
   189  	}
   190  	// Resolve flat state sets in aggregated buffer
   191  	var states stateSet
   192  	if err := states.decode(r); err != nil {
   193  		return nil, err
   194  	}
   195  	return newDiskLayer(root, id, db, nil, nil, newBuffer(db.config.WriteBufferSize, &nodes, &states, id-stored), nil), nil
   196  }
   197  
   198  // loadDiffLayer reads the next sections of a layer journal, reconstructing a new
   199  // diff and verifying that it can be linked to the requested parent.
   200  func (db *Database) loadDiffLayer(parent layer, r *rlp.Stream) (layer, error) {
   201  	// Read the next diff journal entry
   202  	var root common.Hash
   203  	if err := r.Decode(&root); err != nil {
   204  		// The first read may fail with EOF, marking the end of the journal
   205  		if err == io.EOF {
   206  			return parent, nil
   207  		}
   208  		return nil, fmt.Errorf("load diff root: %v", err)
   209  	}
   210  	var block uint64
   211  	if err := r.Decode(&block); err != nil {
   212  		return nil, fmt.Errorf("load block number: %v", err)
   213  	}
   214  	// Read in-memory trie nodes from journal
   215  	var nodes nodeSet
   216  	if err := nodes.decode(r); err != nil {
   217  		return nil, err
   218  	}
   219  	// Read flat states set (with original value attached) from journal
   220  	var stateSet StateSetWithOrigin
   221  	if err := stateSet.decode(r); err != nil {
   222  		return nil, err
   223  	}
   224  	return db.loadDiffLayer(newDiffLayer(parent, root, parent.stateID()+1, block, &nodes, &stateSet), r)
   225  }
   226  
   227  // journal implements the layer interface, marshaling the un-flushed trie nodes
   228  // along with layer meta data into provided byte buffer.
   229  func (dl *diskLayer) journal(w io.Writer) error {
   230  	dl.lock.RLock()
   231  	defer dl.lock.RUnlock()
   232  
   233  	// Ensure the layer didn't get stale
   234  	if dl.stale {
   235  		return errSnapshotStale
   236  	}
   237  	// Step one, write the disk root into the journal.
   238  	if err := rlp.Encode(w, dl.root); err != nil {
   239  		return err
   240  	}
   241  	// Step two, write the corresponding state id into the journal
   242  	if err := rlp.Encode(w, dl.id); err != nil {
   243  		return err
   244  	}
   245  	// Step three, write the accumulated trie nodes into the journal
   246  	if err := dl.buffer.nodes.encode(w); err != nil {
   247  		return err
   248  	}
   249  	// Step four, write the accumulated flat states into the journal
   250  	if err := dl.buffer.states.encode(w); err != nil {
   251  		return err
   252  	}
   253  	log.Debug("Journaled pathdb disk layer", "root", dl.root)
   254  	return nil
   255  }
   256  
   257  // journal implements the layer interface, writing the memory layer contents
   258  // into a buffer to be stored in the database as the layer journal.
   259  func (dl *diffLayer) journal(w io.Writer) error {
   260  	dl.lock.RLock()
   261  	defer dl.lock.RUnlock()
   262  
   263  	// journal the parent first
   264  	if err := dl.parent.journal(w); err != nil {
   265  		return err
   266  	}
   267  	// Everything below was journaled, persist this layer too
   268  	if err := rlp.Encode(w, dl.root); err != nil {
   269  		return err
   270  	}
   271  	if err := rlp.Encode(w, dl.block); err != nil {
   272  		return err
   273  	}
   274  	// Write the accumulated trie nodes into buffer
   275  	if err := dl.nodes.encode(w); err != nil {
   276  		return err
   277  	}
   278  	// Write the associated flat state set into buffer
   279  	if err := dl.states.encode(w); err != nil {
   280  		return err
   281  	}
   282  	log.Debug("Journaled pathdb diff layer", "root", dl.root, "parent", dl.parent.rootHash(), "id", dl.stateID(), "block", dl.block)
   283  	return nil
   284  }
   285  
   286  // Journal commits an entire diff hierarchy to disk into a single journal entry.
   287  // This is meant to be used during shutdown to persist the layer without
   288  // flattening everything down (bad for reorgs). And this function will mark the
   289  // database as read-only to prevent all following mutation to disk.
   290  //
   291  // The supplied root must be a valid trie hash value.
   292  func (db *Database) Journal(root common.Hash) error {
   293  	// Retrieve the head layer to journal from.
   294  	l := db.tree.get(root)
   295  	if l == nil {
   296  		return fmt.Errorf("triedb layer [%#x] missing", root)
   297  	}
   298  	disk := db.tree.bottom()
   299  	if l, ok := l.(*diffLayer); ok {
   300  		log.Info("Persisting dirty state to disk", "head", l.block, "root", root, "layers", l.id-disk.id+disk.buffer.layers)
   301  	} else { // disk layer only on noop runs (likely) or deep reorgs (unlikely)
   302  		log.Info("Persisting dirty state to disk", "root", root, "layers", disk.buffer.layers)
   303  	}
   304  	// Block until the background flushing is finished and terminate
   305  	// the potential active state generator.
   306  	if err := disk.terminate(); err != nil {
   307  		return err
   308  	}
   309  	start := time.Now()
   310  
   311  	// Run the journaling
   312  	db.lock.Lock()
   313  	defer db.lock.Unlock()
   314  
   315  	// Short circuit if the database is in read only mode.
   316  	if db.readOnly {
   317  		return errDatabaseReadOnly
   318  	}
   319  	// Firstly write out the metadata of journal
   320  	journal := new(bytes.Buffer)
   321  	if err := rlp.Encode(journal, journalVersion); err != nil {
   322  		return err
   323  	}
   324  	// Secondly write out the state root in disk, ensure all layers
   325  	// on top are continuous with disk.
   326  	diskRoot, err := db.hasher(rawdb.ReadAccountTrieNode(db.diskdb, nil))
   327  	if err != nil {
   328  		return err
   329  	}
   330  	if err := rlp.Encode(journal, diskRoot); err != nil {
   331  		return err
   332  	}
   333  	// Finally write out the journal of each layer in reverse order.
   334  	if err := l.journal(journal); err != nil {
   335  		return err
   336  	}
   337  	// Store the journal into the database and return
   338  	rawdb.WriteTrieJournal(db.diskdb, journal.Bytes())
   339  
   340  	// Set the db in read only mode to reject all following mutations
   341  	db.readOnly = true
   342  	log.Info("Persisted dirty state to disk", "size", common.StorageSize(journal.Len()), "elapsed", common.PrettyDuration(time.Since(start)))
   343  	return nil
   344  }