github.com/snowblossomcoin/go-ethereum@v1.9.25/core/state/snapshot/journal.go (about)

     1  // Copyright 2019 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 snapshot
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/binary"
    22  	"errors"
    23  	"fmt"
    24  	"io"
    25  	"time"
    26  
    27  	"github.com/VictoriaMetrics/fastcache"
    28  	"github.com/ethereum/go-ethereum/common"
    29  	"github.com/ethereum/go-ethereum/core/rawdb"
    30  	"github.com/ethereum/go-ethereum/ethdb"
    31  	"github.com/ethereum/go-ethereum/log"
    32  	"github.com/ethereum/go-ethereum/rlp"
    33  	"github.com/ethereum/go-ethereum/trie"
    34  )
    35  
    36  const journalVersion uint64 = 0
    37  
    38  // journalGenerator is a disk layer entry containing the generator progress marker.
    39  type journalGenerator struct {
    40  	Wiping   bool // Whether the database was in progress of being wiped
    41  	Done     bool // Whether the generator finished creating the snapshot
    42  	Marker   []byte
    43  	Accounts uint64
    44  	Slots    uint64
    45  	Storage  uint64
    46  }
    47  
    48  // journalDestruct is an account deletion entry in a diffLayer's disk journal.
    49  type journalDestruct struct {
    50  	Hash common.Hash
    51  }
    52  
    53  // journalAccount is an account entry in a diffLayer's disk journal.
    54  type journalAccount struct {
    55  	Hash common.Hash
    56  	Blob []byte
    57  }
    58  
    59  // journalStorage is an account's storage map in a diffLayer's disk journal.
    60  type journalStorage struct {
    61  	Hash common.Hash
    62  	Keys []common.Hash
    63  	Vals [][]byte
    64  }
    65  
    66  // loadAndParseLegacyJournal tries to parse the snapshot journal in legacy format.
    67  func loadAndParseLegacyJournal(db ethdb.KeyValueStore, base *diskLayer) (snapshot, journalGenerator, error) {
    68  	// Retrieve the journal, for legacy journal it must exist since even for
    69  	// 0 layer it stores whether we've already generated the snapshot or are
    70  	// in progress only.
    71  	journal := rawdb.ReadSnapshotJournal(db)
    72  	if len(journal) == 0 {
    73  		return nil, journalGenerator{}, errors.New("missing or corrupted snapshot journal")
    74  	}
    75  	r := rlp.NewStream(bytes.NewReader(journal), 0)
    76  
    77  	// Read the snapshot generation progress for the disk layer
    78  	var generator journalGenerator
    79  	if err := r.Decode(&generator); err != nil {
    80  		return nil, journalGenerator{}, fmt.Errorf("failed to load snapshot progress marker: %v", err)
    81  	}
    82  	// Load all the snapshot diffs from the journal
    83  	snapshot, err := loadDiffLayer(base, r)
    84  	if err != nil {
    85  		return nil, generator, err
    86  	}
    87  	return snapshot, generator, nil
    88  }
    89  
    90  // loadAndParseJournal tries to parse the snapshot journal in latest format.
    91  func loadAndParseJournal(db ethdb.KeyValueStore, base *diskLayer) (snapshot, journalGenerator, error) {
    92  	// Retrieve the disk layer generator. It must exist, no matter the
    93  	// snapshot is fully generated or not. Otherwise the entire disk
    94  	// layer is invalid.
    95  	generatorBlob := rawdb.ReadSnapshotGenerator(db)
    96  	if len(generatorBlob) == 0 {
    97  		return nil, journalGenerator{}, errors.New("missing snapshot generator")
    98  	}
    99  	var generator journalGenerator
   100  	if err := rlp.DecodeBytes(generatorBlob, &generator); err != nil {
   101  		return nil, journalGenerator{}, fmt.Errorf("failed to decode snapshot generator: %v", err)
   102  	}
   103  	// Retrieve the diff layer journal. It's possible that the journal is
   104  	// not existent, e.g. the disk layer is generating while that the Geth
   105  	// crashes without persisting the diff journal.
   106  	// So if there is no journal, or the journal is not matched with disk
   107  	// layer, we just discard all diffs and try to recover them later.
   108  	journal := rawdb.ReadSnapshotJournal(db)
   109  	if len(journal) == 0 {
   110  		log.Warn("Loaded snapshot journal", "diskroot", base.root, "diffs", "missing")
   111  		return base, generator, nil
   112  	}
   113  	r := rlp.NewStream(bytes.NewReader(journal), 0)
   114  
   115  	// Firstly, resolve the first element as the journal version
   116  	version, err := r.Uint()
   117  	if err != nil {
   118  		return nil, journalGenerator{}, err
   119  	}
   120  	if version != journalVersion {
   121  		return nil, journalGenerator{}, fmt.Errorf("journal version mismatch, want %d got %v", journalVersion, version)
   122  	}
   123  	// Secondly, resolve the disk layer root, ensure it's continuous
   124  	// with disk layer.
   125  	var root common.Hash
   126  	if err := r.Decode(&root); err != nil {
   127  		return nil, journalGenerator{}, errors.New("missing disk layer root")
   128  	}
   129  	// The diff journal is not matched with disk, discard them.
   130  	// It can happen that Geth crashes without persisting the latest
   131  	// diff journal.
   132  	if !bytes.Equal(root.Bytes(), base.root.Bytes()) {
   133  		log.Warn("Loaded snapshot journal", "diskroot", base.root, "diffs", "unmatched")
   134  		return base, generator, nil
   135  	}
   136  	// Load all the snapshot diffs from the journal
   137  	snapshot, err := loadDiffLayer(base, r)
   138  	if err != nil {
   139  		return nil, journalGenerator{}, err
   140  	}
   141  	log.Debug("Loaded snapshot journal", "diskroot", base.root, "diffhead", snapshot.Root())
   142  	return snapshot, generator, nil
   143  }
   144  
   145  // loadSnapshot loads a pre-existing state snapshot backed by a key-value store.
   146  func loadSnapshot(diskdb ethdb.KeyValueStore, triedb *trie.Database, cache int, root common.Hash, recovery bool) (snapshot, error) {
   147  	// Retrieve the block number and hash of the snapshot, failing if no snapshot
   148  	// is present in the database (or crashed mid-update).
   149  	baseRoot := rawdb.ReadSnapshotRoot(diskdb)
   150  	if baseRoot == (common.Hash{}) {
   151  		return nil, errors.New("missing or corrupted snapshot")
   152  	}
   153  	base := &diskLayer{
   154  		diskdb: diskdb,
   155  		triedb: triedb,
   156  		cache:  fastcache.New(cache * 1024 * 1024),
   157  		root:   baseRoot,
   158  	}
   159  	var legacy bool
   160  	snapshot, generator, err := loadAndParseJournal(diskdb, base)
   161  	if err != nil {
   162  		log.Debug("Failed to load new-format journal", "error", err)
   163  		snapshot, generator, err = loadAndParseLegacyJournal(diskdb, base)
   164  		legacy = true
   165  	}
   166  	if err != nil {
   167  		return nil, err
   168  	}
   169  	// Entire snapshot journal loaded, sanity check the head. If the loaded
   170  	// snapshot is not matched with current state root, print a warning log
   171  	// or discard the entire snapshot it's legacy snapshot.
   172  	//
   173  	// Possible scenario: Geth was crashed without persisting journal and then
   174  	// restart, the head is rewound to the point with available state(trie)
   175  	// which is below the snapshot. In this case the snapshot can be recovered
   176  	// by re-executing blocks but right now it's unavailable.
   177  	if head := snapshot.Root(); head != root {
   178  		// If it's legacy snapshot, or it's new-format snapshot but
   179  		// it's not in recovery mode, returns the error here for
   180  		// rebuilding the entire snapshot forcibly.
   181  		if legacy || !recovery {
   182  			return nil, fmt.Errorf("head doesn't match snapshot: have %#x, want %#x", head, root)
   183  		}
   184  		// It's in snapshot recovery, the assumption is held that
   185  		// the disk layer is always higher than chain head. It can
   186  		// be eventually recovered when the chain head beyonds the
   187  		// disk layer.
   188  		log.Warn("Snapshot is not continuous with chain", "snaproot", head, "chainroot", root)
   189  	}
   190  	// Everything loaded correctly, resume any suspended operations
   191  	if !generator.Done {
   192  		// If the generator was still wiping, restart one from scratch (fine for
   193  		// now as it's rare and the wiper deletes the stuff it touches anyway, so
   194  		// restarting won't incur a lot of extra database hops.
   195  		var wiper chan struct{}
   196  		if generator.Wiping {
   197  			log.Info("Resuming previous snapshot wipe")
   198  			wiper = wipeSnapshot(diskdb, false)
   199  		}
   200  		// Whether or not wiping was in progress, load any generator progress too
   201  		base.genMarker = generator.Marker
   202  		if base.genMarker == nil {
   203  			base.genMarker = []byte{}
   204  		}
   205  		base.genPending = make(chan struct{})
   206  		base.genAbort = make(chan chan *generatorStats)
   207  
   208  		var origin uint64
   209  		if len(generator.Marker) >= 8 {
   210  			origin = binary.BigEndian.Uint64(generator.Marker)
   211  		}
   212  		go base.generate(&generatorStats{
   213  			wiping:   wiper,
   214  			origin:   origin,
   215  			start:    time.Now(),
   216  			accounts: generator.Accounts,
   217  			slots:    generator.Slots,
   218  			storage:  common.StorageSize(generator.Storage),
   219  		})
   220  	}
   221  	return snapshot, nil
   222  }
   223  
   224  // loadDiffLayer reads the next sections of a snapshot journal, reconstructing a new
   225  // diff and verifying that it can be linked to the requested parent.
   226  func loadDiffLayer(parent snapshot, r *rlp.Stream) (snapshot, error) {
   227  	// Read the next diff journal entry
   228  	var root common.Hash
   229  	if err := r.Decode(&root); err != nil {
   230  		// The first read may fail with EOF, marking the end of the journal
   231  		if err == io.EOF {
   232  			return parent, nil
   233  		}
   234  		return nil, fmt.Errorf("load diff root: %v", err)
   235  	}
   236  	var destructs []journalDestruct
   237  	if err := r.Decode(&destructs); err != nil {
   238  		return nil, fmt.Errorf("load diff destructs: %v", err)
   239  	}
   240  	destructSet := make(map[common.Hash]struct{})
   241  	for _, entry := range destructs {
   242  		destructSet[entry.Hash] = struct{}{}
   243  	}
   244  	var accounts []journalAccount
   245  	if err := r.Decode(&accounts); err != nil {
   246  		return nil, fmt.Errorf("load diff accounts: %v", err)
   247  	}
   248  	accountData := make(map[common.Hash][]byte)
   249  	for _, entry := range accounts {
   250  		if len(entry.Blob) > 0 { // RLP loses nil-ness, but `[]byte{}` is not a valid item, so reinterpret that
   251  			accountData[entry.Hash] = entry.Blob
   252  		} else {
   253  			accountData[entry.Hash] = nil
   254  		}
   255  	}
   256  	var storage []journalStorage
   257  	if err := r.Decode(&storage); err != nil {
   258  		return nil, fmt.Errorf("load diff storage: %v", err)
   259  	}
   260  	storageData := make(map[common.Hash]map[common.Hash][]byte)
   261  	for _, entry := range storage {
   262  		slots := make(map[common.Hash][]byte)
   263  		for i, key := range entry.Keys {
   264  			if len(entry.Vals[i]) > 0 { // RLP loses nil-ness, but `[]byte{}` is not a valid item, so reinterpret that
   265  				slots[key] = entry.Vals[i]
   266  			} else {
   267  				slots[key] = nil
   268  			}
   269  		}
   270  		storageData[entry.Hash] = slots
   271  	}
   272  	return loadDiffLayer(newDiffLayer(parent, root, destructSet, accountData, storageData), r)
   273  }
   274  
   275  // Journal writes the persistent layer generator stats into a buffer to be stored
   276  // in the database as the snapshot journal.
   277  func (dl *diskLayer) Journal(buffer *bytes.Buffer) (common.Hash, error) {
   278  	// If the snapshot is currently being generated, abort it
   279  	var stats *generatorStats
   280  	if dl.genAbort != nil {
   281  		abort := make(chan *generatorStats)
   282  		dl.genAbort <- abort
   283  
   284  		if stats = <-abort; stats != nil {
   285  			stats.Log("Journalling in-progress snapshot", dl.root, dl.genMarker)
   286  		}
   287  	}
   288  	// Ensure the layer didn't get stale
   289  	dl.lock.RLock()
   290  	defer dl.lock.RUnlock()
   291  
   292  	if dl.stale {
   293  		return common.Hash{}, ErrSnapshotStale
   294  	}
   295  	// Write out the generator marker. Note it's a standalone disk layer generator
   296  	// which is not mixed with journal. It's ok if the generator is persisted while
   297  	// journal is not.
   298  	entry := journalGenerator{
   299  		Done:   dl.genMarker == nil,
   300  		Marker: dl.genMarker,
   301  	}
   302  	if stats != nil {
   303  		entry.Wiping = (stats.wiping != nil)
   304  		entry.Accounts = stats.accounts
   305  		entry.Slots = stats.slots
   306  		entry.Storage = uint64(stats.storage)
   307  	}
   308  	blob, err := rlp.EncodeToBytes(entry)
   309  	if err != nil {
   310  		return common.Hash{}, err
   311  	}
   312  	log.Debug("Journalled disk layer", "root", dl.root, "complete", dl.genMarker == nil)
   313  	rawdb.WriteSnapshotGenerator(dl.diskdb, blob)
   314  	return dl.root, nil
   315  }
   316  
   317  // Journal writes the memory layer contents into a buffer to be stored in the
   318  // database as the snapshot journal.
   319  func (dl *diffLayer) Journal(buffer *bytes.Buffer) (common.Hash, error) {
   320  	// Journal the parent first
   321  	base, err := dl.parent.Journal(buffer)
   322  	if err != nil {
   323  		return common.Hash{}, err
   324  	}
   325  	// Ensure the layer didn't get stale
   326  	dl.lock.RLock()
   327  	defer dl.lock.RUnlock()
   328  
   329  	if dl.Stale() {
   330  		return common.Hash{}, ErrSnapshotStale
   331  	}
   332  	// Everything below was journalled, persist this layer too
   333  	if err := rlp.Encode(buffer, dl.root); err != nil {
   334  		return common.Hash{}, err
   335  	}
   336  	destructs := make([]journalDestruct, 0, len(dl.destructSet))
   337  	for hash := range dl.destructSet {
   338  		destructs = append(destructs, journalDestruct{Hash: hash})
   339  	}
   340  	if err := rlp.Encode(buffer, destructs); err != nil {
   341  		return common.Hash{}, err
   342  	}
   343  	accounts := make([]journalAccount, 0, len(dl.accountData))
   344  	for hash, blob := range dl.accountData {
   345  		accounts = append(accounts, journalAccount{Hash: hash, Blob: blob})
   346  	}
   347  	if err := rlp.Encode(buffer, accounts); err != nil {
   348  		return common.Hash{}, err
   349  	}
   350  	storage := make([]journalStorage, 0, len(dl.storageData))
   351  	for hash, slots := range dl.storageData {
   352  		keys := make([]common.Hash, 0, len(slots))
   353  		vals := make([][]byte, 0, len(slots))
   354  		for key, val := range slots {
   355  			keys = append(keys, key)
   356  			vals = append(vals, val)
   357  		}
   358  		storage = append(storage, journalStorage{Hash: hash, Keys: keys, Vals: vals})
   359  	}
   360  	if err := rlp.Encode(buffer, storage); err != nil {
   361  		return common.Hash{}, err
   362  	}
   363  	log.Debug("Journalled diff layer", "root", dl.root, "parent", dl.parent.Root())
   364  	return base, nil
   365  }
   366  
   367  // LegacyJournal writes the persistent layer generator stats into a buffer
   368  // to be stored in the database as the snapshot journal.
   369  //
   370  // Note it's the legacy version which is only used in testing right now.
   371  func (dl *diskLayer) LegacyJournal(buffer *bytes.Buffer) (common.Hash, error) {
   372  	// If the snapshot is currently being generated, abort it
   373  	var stats *generatorStats
   374  	if dl.genAbort != nil {
   375  		abort := make(chan *generatorStats)
   376  		dl.genAbort <- abort
   377  
   378  		if stats = <-abort; stats != nil {
   379  			stats.Log("Journalling in-progress snapshot", dl.root, dl.genMarker)
   380  		}
   381  	}
   382  	// Ensure the layer didn't get stale
   383  	dl.lock.RLock()
   384  	defer dl.lock.RUnlock()
   385  
   386  	if dl.stale {
   387  		return common.Hash{}, ErrSnapshotStale
   388  	}
   389  	// Write out the generator marker
   390  	entry := journalGenerator{
   391  		Done:   dl.genMarker == nil,
   392  		Marker: dl.genMarker,
   393  	}
   394  	if stats != nil {
   395  		entry.Wiping = (stats.wiping != nil)
   396  		entry.Accounts = stats.accounts
   397  		entry.Slots = stats.slots
   398  		entry.Storage = uint64(stats.storage)
   399  	}
   400  	if err := rlp.Encode(buffer, entry); err != nil {
   401  		return common.Hash{}, err
   402  	}
   403  	return dl.root, nil
   404  }
   405  
   406  // Journal writes the memory layer contents into a buffer to be stored in the
   407  // database as the snapshot journal.
   408  //
   409  // Note it's the legacy version which is only used in testing right now.
   410  func (dl *diffLayer) LegacyJournal(buffer *bytes.Buffer) (common.Hash, error) {
   411  	// Journal the parent first
   412  	base, err := dl.parent.LegacyJournal(buffer)
   413  	if err != nil {
   414  		return common.Hash{}, err
   415  	}
   416  	// Ensure the layer didn't get stale
   417  	dl.lock.RLock()
   418  	defer dl.lock.RUnlock()
   419  
   420  	if dl.Stale() {
   421  		return common.Hash{}, ErrSnapshotStale
   422  	}
   423  	// Everything below was journalled, persist this layer too
   424  	if err := rlp.Encode(buffer, dl.root); err != nil {
   425  		return common.Hash{}, err
   426  	}
   427  	destructs := make([]journalDestruct, 0, len(dl.destructSet))
   428  	for hash := range dl.destructSet {
   429  		destructs = append(destructs, journalDestruct{Hash: hash})
   430  	}
   431  	if err := rlp.Encode(buffer, destructs); err != nil {
   432  		return common.Hash{}, err
   433  	}
   434  	accounts := make([]journalAccount, 0, len(dl.accountData))
   435  	for hash, blob := range dl.accountData {
   436  		accounts = append(accounts, journalAccount{Hash: hash, Blob: blob})
   437  	}
   438  	if err := rlp.Encode(buffer, accounts); err != nil {
   439  		return common.Hash{}, err
   440  	}
   441  	storage := make([]journalStorage, 0, len(dl.storageData))
   442  	for hash, slots := range dl.storageData {
   443  		keys := make([]common.Hash, 0, len(slots))
   444  		vals := make([][]byte, 0, len(slots))
   445  		for key, val := range slots {
   446  			keys = append(keys, key)
   447  			vals = append(vals, val)
   448  		}
   449  		storage = append(storage, journalStorage{Hash: hash, Keys: keys, Vals: vals})
   450  	}
   451  	if err := rlp.Encode(buffer, storage); err != nil {
   452  		return common.Hash{}, err
   453  	}
   454  	log.Debug("Journalled diff layer", "root", dl.root, "parent", dl.parent.Root())
   455  	return base, nil
   456  }