2023-08-01 20:17:32 +08:00
|
|
|
// Copyright 2022 The go-ethereum Authors
|
|
|
|
// This file is part of the go-ethereum library.
|
|
|
|
//
|
|
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU Lesser General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
package pathdb
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
|
|
|
"github.com/ethereum/go-ethereum/core/types"
|
2024-04-30 22:25:35 +08:00
|
|
|
"github.com/ethereum/go-ethereum/crypto"
|
2023-08-01 20:17:32 +08:00
|
|
|
"github.com/ethereum/go-ethereum/ethdb"
|
|
|
|
"github.com/ethereum/go-ethereum/log"
|
|
|
|
"github.com/ethereum/go-ethereum/params"
|
|
|
|
"github.com/ethereum/go-ethereum/trie/trienode"
|
|
|
|
"github.com/ethereum/go-ethereum/trie/triestate"
|
|
|
|
)
|
|
|
|
|
all: activate pbss as experimental feature (#26274)
* all: activate pbss
* core/rawdb: fix compilation error
* cma, core, eth, les, trie: address comments
* cmd, core, eth, trie: polish code
* core, cmd, eth: address comments
* cmd, core, eth, les, light, tests: address comment
* cmd/utils: shorten log message
* trie/triedb/pathdb: limit node buffer size to 1gb
* cmd/utils: fix opening non-existing db
* cmd/utils: rename flag name
* cmd, core: group chain history flags and fix tests
* core, eth, trie: fix memory leak in snapshot generation
* cmd, eth, internal: deprecate flags
* all: enable state tests for pathdb, fixes
* cmd, core: polish code
* trie/triedb/pathdb: limit the node buffer size to 256mb
---------
Co-authored-by: Martin Holst Swende <martin@swende.se>
Co-authored-by: Péter Szilágyi <peterke@gmail.com>
2023-08-11 03:21:36 +08:00
|
|
|
const (
|
|
|
|
// defaultCleanSize is the default memory allowance of clean cache.
|
|
|
|
defaultCleanSize = 16 * 1024 * 1024
|
|
|
|
|
|
|
|
// maxBufferSize is the maximum memory allowance of node buffer.
|
|
|
|
// Too large nodebuffer will cause the system to pause for a long
|
|
|
|
// time when write happens. Also, the largest batch that pebble can
|
|
|
|
// support is 4GB, node will panic if batch size exceeds this limit.
|
|
|
|
maxBufferSize = 256 * 1024 * 1024
|
|
|
|
|
|
|
|
// DefaultBufferSize is the default memory allowance of node buffer
|
|
|
|
// that aggregates the writes from above until it's flushed into the
|
|
|
|
// disk. It's meant to be used once the initial sync is finished.
|
|
|
|
// Do not increase the buffer size arbitrarily, otherwise the system
|
|
|
|
// pause time will increase when the database writes happen.
|
|
|
|
DefaultBufferSize = 64 * 1024 * 1024
|
|
|
|
)
|
2023-08-01 20:17:32 +08:00
|
|
|
|
2024-03-19 10:50:08 +08:00
|
|
|
var (
|
|
|
|
// maxDiffLayers is the maximum diff layers allowed in the layer tree.
|
|
|
|
maxDiffLayers = 128
|
|
|
|
)
|
|
|
|
|
2023-08-01 20:17:32 +08:00
|
|
|
// layer is the interface implemented by all state layers which includes some
|
|
|
|
// public methods and some additional methods for internal usage.
|
|
|
|
type layer interface {
|
2024-03-26 21:25:41 +01:00
|
|
|
// node retrieves the trie node with the node info. An error will be returned
|
|
|
|
// if the read operation exits abnormally. Specifically, if the layer is
|
|
|
|
// already stale.
|
|
|
|
//
|
|
|
|
// Note, no error will be returned if the requested node is not found in database.
|
|
|
|
node(owner common.Hash, path []byte, depth int) ([]byte, common.Hash, *nodeLoc, error)
|
2023-08-01 20:17:32 +08:00
|
|
|
|
|
|
|
// rootHash returns the root hash for which this layer was made.
|
|
|
|
rootHash() common.Hash
|
|
|
|
|
|
|
|
// stateID returns the associated state id of layer.
|
|
|
|
stateID() uint64
|
|
|
|
|
|
|
|
// parentLayer returns the subsequent layer of it, or nil if the disk was reached.
|
|
|
|
parentLayer() layer
|
|
|
|
|
|
|
|
// update creates a new layer on top of the existing layer diff tree with
|
|
|
|
// the provided dirty trie nodes along with the state change set.
|
|
|
|
//
|
|
|
|
// Note, the maps are retained by the method to avoid copying everything.
|
|
|
|
update(root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string]*trienode.Node, states *triestate.Set) *diffLayer
|
|
|
|
|
|
|
|
// journal commits an entire diff hierarchy to disk into a single journal entry.
|
|
|
|
// This is meant to be used during shutdown to persist the layer without
|
|
|
|
// flattening everything down (bad for reorgs).
|
|
|
|
journal(w io.Writer) error
|
|
|
|
}
|
|
|
|
|
|
|
|
// Config contains the settings for database.
|
|
|
|
type Config struct {
|
all: activate pbss as experimental feature (#26274)
* all: activate pbss
* core/rawdb: fix compilation error
* cma, core, eth, les, trie: address comments
* cmd, core, eth, trie: polish code
* core, cmd, eth: address comments
* cmd, core, eth, les, light, tests: address comment
* cmd/utils: shorten log message
* trie/triedb/pathdb: limit node buffer size to 1gb
* cmd/utils: fix opening non-existing db
* cmd/utils: rename flag name
* cmd, core: group chain history flags and fix tests
* core, eth, trie: fix memory leak in snapshot generation
* cmd, eth, internal: deprecate flags
* all: enable state tests for pathdb, fixes
* cmd, core: polish code
* trie/triedb/pathdb: limit the node buffer size to 256mb
---------
Co-authored-by: Martin Holst Swende <martin@swende.se>
Co-authored-by: Péter Szilágyi <peterke@gmail.com>
2023-08-11 03:21:36 +08:00
|
|
|
StateHistory uint64 // Number of recent blocks to maintain state history for
|
|
|
|
CleanCacheSize int // Maximum memory allowance (in bytes) for caching clean nodes
|
|
|
|
DirtyCacheSize int // Maximum memory allowance (in bytes) for caching dirty nodes
|
|
|
|
ReadOnly bool // Flag whether the database is opened in read only mode.
|
2023-08-01 20:17:32 +08:00
|
|
|
}
|
|
|
|
|
all: activate pbss as experimental feature (#26274)
* all: activate pbss
* core/rawdb: fix compilation error
* cma, core, eth, les, trie: address comments
* cmd, core, eth, trie: polish code
* core, cmd, eth: address comments
* cmd, core, eth, les, light, tests: address comment
* cmd/utils: shorten log message
* trie/triedb/pathdb: limit node buffer size to 1gb
* cmd/utils: fix opening non-existing db
* cmd/utils: rename flag name
* cmd, core: group chain history flags and fix tests
* core, eth, trie: fix memory leak in snapshot generation
* cmd, eth, internal: deprecate flags
* all: enable state tests for pathdb, fixes
* cmd, core: polish code
* trie/triedb/pathdb: limit the node buffer size to 256mb
---------
Co-authored-by: Martin Holst Swende <martin@swende.se>
Co-authored-by: Péter Szilágyi <peterke@gmail.com>
2023-08-11 03:21:36 +08:00
|
|
|
// sanitize checks the provided user configurations and changes anything that's
|
|
|
|
// unreasonable or unworkable.
|
|
|
|
func (c *Config) sanitize() *Config {
|
|
|
|
conf := *c
|
|
|
|
if conf.DirtyCacheSize > maxBufferSize {
|
|
|
|
log.Warn("Sanitizing invalid node buffer size", "provided", common.StorageSize(conf.DirtyCacheSize), "updated", common.StorageSize(maxBufferSize))
|
|
|
|
conf.DirtyCacheSize = maxBufferSize
|
|
|
|
}
|
|
|
|
return &conf
|
|
|
|
}
|
2023-08-01 20:17:32 +08:00
|
|
|
|
|
|
|
// Defaults contains default settings for Ethereum mainnet.
|
|
|
|
var Defaults = &Config{
|
all: activate pbss as experimental feature (#26274)
* all: activate pbss
* core/rawdb: fix compilation error
* cma, core, eth, les, trie: address comments
* cmd, core, eth, trie: polish code
* core, cmd, eth: address comments
* cmd, core, eth, les, light, tests: address comment
* cmd/utils: shorten log message
* trie/triedb/pathdb: limit node buffer size to 1gb
* cmd/utils: fix opening non-existing db
* cmd/utils: rename flag name
* cmd, core: group chain history flags and fix tests
* core, eth, trie: fix memory leak in snapshot generation
* cmd, eth, internal: deprecate flags
* all: enable state tests for pathdb, fixes
* cmd, core: polish code
* trie/triedb/pathdb: limit the node buffer size to 256mb
---------
Co-authored-by: Martin Holst Swende <martin@swende.se>
Co-authored-by: Péter Szilágyi <peterke@gmail.com>
2023-08-11 03:21:36 +08:00
|
|
|
StateHistory: params.FullImmutabilityThreshold,
|
|
|
|
CleanCacheSize: defaultCleanSize,
|
|
|
|
DirtyCacheSize: DefaultBufferSize,
|
2023-08-01 20:17:32 +08:00
|
|
|
}
|
|
|
|
|
all: activate pbss as experimental feature (#26274)
* all: activate pbss
* core/rawdb: fix compilation error
* cma, core, eth, les, trie: address comments
* cmd, core, eth, trie: polish code
* core, cmd, eth: address comments
* cmd, core, eth, les, light, tests: address comment
* cmd/utils: shorten log message
* trie/triedb/pathdb: limit node buffer size to 1gb
* cmd/utils: fix opening non-existing db
* cmd/utils: rename flag name
* cmd, core: group chain history flags and fix tests
* core, eth, trie: fix memory leak in snapshot generation
* cmd, eth, internal: deprecate flags
* all: enable state tests for pathdb, fixes
* cmd, core: polish code
* trie/triedb/pathdb: limit the node buffer size to 256mb
---------
Co-authored-by: Martin Holst Swende <martin@swende.se>
Co-authored-by: Péter Szilágyi <peterke@gmail.com>
2023-08-11 03:21:36 +08:00
|
|
|
// ReadOnly is the config in order to open database in read only mode.
|
|
|
|
var ReadOnly = &Config{ReadOnly: true}
|
|
|
|
|
2023-08-01 20:17:32 +08:00
|
|
|
// Database is a multiple-layered structure for maintaining in-memory trie nodes.
|
|
|
|
// It consists of one persistent base layer backed by a key-value store, on top
|
|
|
|
// of which arbitrarily many in-memory diff layers are stacked. The memory diffs
|
|
|
|
// can form a tree with branching, but the disk layer is singleton and common to
|
|
|
|
// all. If a reorg goes deeper than the disk layer, a batch of reverse diffs can
|
|
|
|
// be applied to rollback. The deepest reorg that can be handled depends on the
|
|
|
|
// amount of state histories tracked in the disk.
|
|
|
|
//
|
|
|
|
// At most one readable and writable database can be opened at the same time in
|
|
|
|
// the whole system which ensures that only one database writer can operate disk
|
|
|
|
// state. Unexpected open operations can cause the system to panic.
|
|
|
|
type Database struct {
|
|
|
|
// readOnly is the flag whether the mutation is allowed to be applied.
|
|
|
|
// It will be set automatically when the database is journaled during
|
|
|
|
// the shutdown to reject all following unexpected mutations.
|
2024-04-30 17:33:22 +08:00
|
|
|
readOnly bool // Flag if database is opened in read only mode
|
|
|
|
waitSync bool // Flag if database is deactivated due to initial state sync
|
|
|
|
isVerkle bool // Flag if database is used for verkle tree
|
|
|
|
bufferSize int // Memory allowance (in bytes) for caching dirty nodes
|
|
|
|
config *Config // Configuration for database
|
|
|
|
diskdb ethdb.Database // Persistent storage for matured trie nodes
|
|
|
|
tree *layerTree // The group for all known layers
|
|
|
|
freezer ethdb.ResettableAncientStore // Freezer for storing trie histories, nil possible in tests
|
|
|
|
lock sync.RWMutex // Lock to prevent mutations from happening at the same time
|
2023-08-01 20:17:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// New attempts to load an already existing layer from a persistent key-value
|
|
|
|
// store (with a number of memory layers from a journal). If the journal is not
|
|
|
|
// matched with the base persistent layer, all the recorded diff layers are discarded.
|
2024-03-26 21:25:41 +01:00
|
|
|
func New(diskdb ethdb.Database, config *Config, isVerkle bool) *Database {
|
2023-08-01 20:17:32 +08:00
|
|
|
if config == nil {
|
|
|
|
config = Defaults
|
|
|
|
}
|
all: activate pbss as experimental feature (#26274)
* all: activate pbss
* core/rawdb: fix compilation error
* cma, core, eth, les, trie: address comments
* cmd, core, eth, trie: polish code
* core, cmd, eth: address comments
* cmd, core, eth, les, light, tests: address comment
* cmd/utils: shorten log message
* trie/triedb/pathdb: limit node buffer size to 1gb
* cmd/utils: fix opening non-existing db
* cmd/utils: rename flag name
* cmd, core: group chain history flags and fix tests
* core, eth, trie: fix memory leak in snapshot generation
* cmd, eth, internal: deprecate flags
* all: enable state tests for pathdb, fixes
* cmd, core: polish code
* trie/triedb/pathdb: limit the node buffer size to 256mb
---------
Co-authored-by: Martin Holst Swende <martin@swende.se>
Co-authored-by: Péter Szilágyi <peterke@gmail.com>
2023-08-11 03:21:36 +08:00
|
|
|
config = config.sanitize()
|
|
|
|
|
2023-08-01 20:17:32 +08:00
|
|
|
db := &Database{
|
|
|
|
readOnly: config.ReadOnly,
|
2024-03-26 21:25:41 +01:00
|
|
|
isVerkle: isVerkle,
|
all: activate pbss as experimental feature (#26274)
* all: activate pbss
* core/rawdb: fix compilation error
* cma, core, eth, les, trie: address comments
* cmd, core, eth, trie: polish code
* core, cmd, eth: address comments
* cmd, core, eth, les, light, tests: address comment
* cmd/utils: shorten log message
* trie/triedb/pathdb: limit node buffer size to 1gb
* cmd/utils: fix opening non-existing db
* cmd/utils: rename flag name
* cmd, core: group chain history flags and fix tests
* core, eth, trie: fix memory leak in snapshot generation
* cmd, eth, internal: deprecate flags
* all: enable state tests for pathdb, fixes
* cmd, core: polish code
* trie/triedb/pathdb: limit the node buffer size to 256mb
---------
Co-authored-by: Martin Holst Swende <martin@swende.se>
Co-authored-by: Péter Szilágyi <peterke@gmail.com>
2023-08-11 03:21:36 +08:00
|
|
|
bufferSize: config.DirtyCacheSize,
|
2023-08-01 20:17:32 +08:00
|
|
|
config: config,
|
|
|
|
diskdb: diskdb,
|
|
|
|
}
|
|
|
|
// Construct the layer tree by resolving the in-disk singleton state
|
|
|
|
// and in-memory layer journal.
|
|
|
|
db.tree = newLayerTree(db.loadLayers())
|
|
|
|
|
2024-04-30 17:33:22 +08:00
|
|
|
// Repair the state history, which might not be aligned with the state
|
|
|
|
// in the key-value store due to an unclean shutdown.
|
|
|
|
if err := db.repairHistory(); err != nil {
|
|
|
|
log.Crit("Failed to repair pathdb", "err", err)
|
2023-08-01 20:17:32 +08:00
|
|
|
}
|
core, accounts, eth, trie: handle genesis state missing (#28171)
* core, accounts, eth, trie: handle genesis state missing
* core, eth, trie: polish
* core: manage txpool subscription in mainpool
* eth/backend: fix test
* cmd, eth: fix test
* core/rawdb, trie/triedb/pathdb: address comments
* eth, trie: address comments
* eth: inline the function
* eth: use synced flag
* core/txpool: revert changes in txpool
* core, eth, trie: rename functions
2023-09-28 15:00:53 +08:00
|
|
|
// Disable database in case node is still in the initial state sync stage.
|
|
|
|
if rawdb.ReadSnapSyncStatusFlag(diskdb) == rawdb.StateSyncRunning && !db.readOnly {
|
|
|
|
if err := db.Disable(); err != nil {
|
|
|
|
log.Crit("Failed to disable database", "err", err) // impossible to happen
|
|
|
|
}
|
|
|
|
}
|
2023-08-01 20:17:32 +08:00
|
|
|
return db
|
|
|
|
}
|
|
|
|
|
2024-04-30 17:33:22 +08:00
|
|
|
// repairHistory truncates leftover state history objects, which may occur due
|
|
|
|
// to an unclean shutdown or other unexpected reasons.
|
|
|
|
func (db *Database) repairHistory() error {
|
|
|
|
// Open the freezer for state history. This mechanism ensures that
|
|
|
|
// only one database instance can be opened at a time to prevent
|
|
|
|
// accidental mutation.
|
|
|
|
ancient, err := db.diskdb.AncientDatadir()
|
|
|
|
if err != nil {
|
|
|
|
// TODO error out if ancient store is disabled. A tons of unit tests
|
|
|
|
// disable the ancient store thus the error here will immediately fail
|
|
|
|
// all of them. Fix the tests first.
|
|
|
|
return nil
|
|
|
|
}
|
2024-05-28 20:41:11 +08:00
|
|
|
freezer, err := rawdb.NewStateFreezer(ancient, db.readOnly)
|
2024-04-30 17:33:22 +08:00
|
|
|
if err != nil {
|
|
|
|
log.Crit("Failed to open state history freezer", "err", err)
|
|
|
|
}
|
|
|
|
db.freezer = freezer
|
|
|
|
|
|
|
|
// Reset the entire state histories if the trie database is not initialized
|
|
|
|
// yet. This action is necessary because these state histories are not
|
|
|
|
// expected to exist without an initialized trie database.
|
|
|
|
id := db.tree.bottom().stateID()
|
|
|
|
if id == 0 {
|
|
|
|
frozen, err := db.freezer.Ancients()
|
|
|
|
if err != nil {
|
|
|
|
log.Crit("Failed to retrieve head of state history", "err", err)
|
|
|
|
}
|
|
|
|
if frozen != 0 {
|
|
|
|
err := db.freezer.Reset()
|
|
|
|
if err != nil {
|
|
|
|
log.Crit("Failed to reset state histories", "err", err)
|
|
|
|
}
|
|
|
|
log.Info("Truncated extraneous state history")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
// Truncate the extra state histories above in freezer in case it's not
|
|
|
|
// aligned with the disk layer. It might happen after a unclean shutdown.
|
|
|
|
pruned, err := truncateFromHead(db.diskdb, db.freezer, id)
|
|
|
|
if err != nil {
|
|
|
|
log.Crit("Failed to truncate extra state histories", "err", err)
|
|
|
|
}
|
|
|
|
if pruned != 0 {
|
|
|
|
log.Warn("Truncated extra state histories", "number", pruned)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-08-01 20:17:32 +08:00
|
|
|
// Update adds a new layer into the tree, if that can be linked to an existing
|
|
|
|
// old parent. It is disallowed to insert a disk layer (the origin of all). Apart
|
|
|
|
// from that this function will flatten the extra diff layers at bottom into disk
|
|
|
|
// to only keep 128 diff layers in memory by default.
|
|
|
|
//
|
|
|
|
// The passed in maps(nodes, states) will be retained to avoid copying everything.
|
|
|
|
// Therefore, these maps must not be changed afterwards.
|
|
|
|
func (db *Database) Update(root common.Hash, parentRoot common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *triestate.Set) error {
|
|
|
|
// Hold the lock to prevent concurrent mutations.
|
|
|
|
db.lock.Lock()
|
|
|
|
defer db.lock.Unlock()
|
|
|
|
|
core, accounts, eth, trie: handle genesis state missing (#28171)
* core, accounts, eth, trie: handle genesis state missing
* core, eth, trie: polish
* core: manage txpool subscription in mainpool
* eth/backend: fix test
* cmd, eth: fix test
* core/rawdb, trie/triedb/pathdb: address comments
* eth, trie: address comments
* eth: inline the function
* eth: use synced flag
* core/txpool: revert changes in txpool
* core, eth, trie: rename functions
2023-09-28 15:00:53 +08:00
|
|
|
// Short circuit if the mutation is not allowed.
|
|
|
|
if err := db.modifyAllowed(); err != nil {
|
|
|
|
return err
|
2023-08-01 20:17:32 +08:00
|
|
|
}
|
|
|
|
if err := db.tree.add(root, parentRoot, block, nodes, states); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Keep 128 diff layers in the memory, persistent layer is 129th.
|
|
|
|
// - head layer is paired with HEAD state
|
|
|
|
// - head-1 layer is paired with HEAD-1 state
|
|
|
|
// - head-127 layer(bottom-most diff layer) is paired with HEAD-127 state
|
|
|
|
// - head-128 layer(disk layer) is paired with HEAD-128 state
|
|
|
|
return db.tree.cap(root, maxDiffLayers)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Commit traverses downwards the layer tree from a specified layer with the
|
|
|
|
// provided state root and all the layers below are flattened downwards. It
|
|
|
|
// can be used alone and mostly for test purposes.
|
|
|
|
func (db *Database) Commit(root common.Hash, report bool) error {
|
|
|
|
// Hold the lock to prevent concurrent mutations.
|
|
|
|
db.lock.Lock()
|
|
|
|
defer db.lock.Unlock()
|
|
|
|
|
core, accounts, eth, trie: handle genesis state missing (#28171)
* core, accounts, eth, trie: handle genesis state missing
* core, eth, trie: polish
* core: manage txpool subscription in mainpool
* eth/backend: fix test
* cmd, eth: fix test
* core/rawdb, trie/triedb/pathdb: address comments
* eth, trie: address comments
* eth: inline the function
* eth: use synced flag
* core/txpool: revert changes in txpool
* core, eth, trie: rename functions
2023-09-28 15:00:53 +08:00
|
|
|
// Short circuit if the mutation is not allowed.
|
|
|
|
if err := db.modifyAllowed(); err != nil {
|
|
|
|
return err
|
2023-08-01 20:17:32 +08:00
|
|
|
}
|
|
|
|
return db.tree.cap(root, 0)
|
|
|
|
}
|
|
|
|
|
core, accounts, eth, trie: handle genesis state missing (#28171)
* core, accounts, eth, trie: handle genesis state missing
* core, eth, trie: polish
* core: manage txpool subscription in mainpool
* eth/backend: fix test
* cmd, eth: fix test
* core/rawdb, trie/triedb/pathdb: address comments
* eth, trie: address comments
* eth: inline the function
* eth: use synced flag
* core/txpool: revert changes in txpool
* core, eth, trie: rename functions
2023-09-28 15:00:53 +08:00
|
|
|
// Disable deactivates the database and invalidates all available state layers
|
|
|
|
// as stale to prevent access to the persistent state, which is in the syncing
|
|
|
|
// stage.
|
|
|
|
func (db *Database) Disable() error {
|
2023-08-01 20:17:32 +08:00
|
|
|
db.lock.Lock()
|
|
|
|
defer db.lock.Unlock()
|
|
|
|
|
|
|
|
// Short circuit if the database is in read only mode.
|
|
|
|
if db.readOnly {
|
core, accounts, eth, trie: handle genesis state missing (#28171)
* core, accounts, eth, trie: handle genesis state missing
* core, eth, trie: polish
* core: manage txpool subscription in mainpool
* eth/backend: fix test
* cmd, eth: fix test
* core/rawdb, trie/triedb/pathdb: address comments
* eth, trie: address comments
* eth: inline the function
* eth: use synced flag
* core/txpool: revert changes in txpool
* core, eth, trie: rename functions
2023-09-28 15:00:53 +08:00
|
|
|
return errDatabaseReadOnly
|
2023-08-01 20:17:32 +08:00
|
|
|
}
|
core, accounts, eth, trie: handle genesis state missing (#28171)
* core, accounts, eth, trie: handle genesis state missing
* core, eth, trie: polish
* core: manage txpool subscription in mainpool
* eth/backend: fix test
* cmd, eth: fix test
* core/rawdb, trie/triedb/pathdb: address comments
* eth, trie: address comments
* eth: inline the function
* eth: use synced flag
* core/txpool: revert changes in txpool
* core, eth, trie: rename functions
2023-09-28 15:00:53 +08:00
|
|
|
// Prevent duplicated disable operation.
|
|
|
|
if db.waitSync {
|
|
|
|
log.Error("Reject duplicated disable operation")
|
|
|
|
return nil
|
2023-08-01 20:17:32 +08:00
|
|
|
}
|
core, accounts, eth, trie: handle genesis state missing (#28171)
* core, accounts, eth, trie: handle genesis state missing
* core, eth, trie: polish
* core: manage txpool subscription in mainpool
* eth/backend: fix test
* cmd, eth: fix test
* core/rawdb, trie/triedb/pathdb: address comments
* eth, trie: address comments
* eth: inline the function
* eth: use synced flag
* core/txpool: revert changes in txpool
* core, eth, trie: rename functions
2023-09-28 15:00:53 +08:00
|
|
|
db.waitSync = true
|
|
|
|
|
|
|
|
// Mark the disk layer as stale to prevent access to persistent state.
|
2023-08-01 20:17:32 +08:00
|
|
|
db.tree.bottom().markStale()
|
|
|
|
|
core, accounts, eth, trie: handle genesis state missing (#28171)
* core, accounts, eth, trie: handle genesis state missing
* core, eth, trie: polish
* core: manage txpool subscription in mainpool
* eth/backend: fix test
* cmd, eth: fix test
* core/rawdb, trie/triedb/pathdb: address comments
* eth, trie: address comments
* eth: inline the function
* eth: use synced flag
* core/txpool: revert changes in txpool
* core, eth, trie: rename functions
2023-09-28 15:00:53 +08:00
|
|
|
// Write the initial sync flag to persist it across restarts.
|
|
|
|
rawdb.WriteSnapSyncStatusFlag(db.diskdb, rawdb.StateSyncRunning)
|
|
|
|
log.Info("Disabled trie database due to state sync")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enable activates database and resets the state tree with the provided persistent
|
|
|
|
// state root once the state sync is finished.
|
|
|
|
func (db *Database) Enable(root common.Hash) error {
|
|
|
|
db.lock.Lock()
|
|
|
|
defer db.lock.Unlock()
|
|
|
|
|
|
|
|
// Short circuit if the database is in read only mode.
|
|
|
|
if db.readOnly {
|
|
|
|
return errDatabaseReadOnly
|
|
|
|
}
|
|
|
|
// Ensure the provided state root matches the stored one.
|
|
|
|
root = types.TrieRootHash(root)
|
2024-04-30 22:25:35 +08:00
|
|
|
stored := types.EmptyRootHash
|
|
|
|
if blob := rawdb.ReadAccountTrieNode(db.diskdb, nil); len(blob) > 0 {
|
|
|
|
stored = crypto.Keccak256Hash(blob)
|
|
|
|
}
|
core, accounts, eth, trie: handle genesis state missing (#28171)
* core, accounts, eth, trie: handle genesis state missing
* core, eth, trie: polish
* core: manage txpool subscription in mainpool
* eth/backend: fix test
* cmd, eth: fix test
* core/rawdb, trie/triedb/pathdb: address comments
* eth, trie: address comments
* eth: inline the function
* eth: use synced flag
* core/txpool: revert changes in txpool
* core, eth, trie: rename functions
2023-09-28 15:00:53 +08:00
|
|
|
if stored != root {
|
|
|
|
return fmt.Errorf("state root mismatch: stored %x, synced %x", stored, root)
|
|
|
|
}
|
2023-08-01 20:17:32 +08:00
|
|
|
// Drop the stale state journal in persistent database and
|
|
|
|
// reset the persistent state id back to zero.
|
core, accounts, eth, trie: handle genesis state missing (#28171)
* core, accounts, eth, trie: handle genesis state missing
* core, eth, trie: polish
* core: manage txpool subscription in mainpool
* eth/backend: fix test
* cmd, eth: fix test
* core/rawdb, trie/triedb/pathdb: address comments
* eth, trie: address comments
* eth: inline the function
* eth: use synced flag
* core/txpool: revert changes in txpool
* core, eth, trie: rename functions
2023-09-28 15:00:53 +08:00
|
|
|
batch := db.diskdb.NewBatch()
|
2023-08-01 20:17:32 +08:00
|
|
|
rawdb.DeleteTrieJournal(batch)
|
|
|
|
rawdb.WritePersistentStateID(batch, 0)
|
|
|
|
if err := batch.Write(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Clean up all state histories in freezer. Theoretically
|
|
|
|
// all root->id mappings should be removed as well. Since
|
|
|
|
// mappings can be huge and might take a while to clear
|
|
|
|
// them, just leave them in disk and wait for overwriting.
|
|
|
|
if db.freezer != nil {
|
|
|
|
if err := db.freezer.Reset(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Re-construct a new disk layer backed by persistent state
|
|
|
|
// with **empty clean cache and node buffer**.
|
core, accounts, eth, trie: handle genesis state missing (#28171)
* core, accounts, eth, trie: handle genesis state missing
* core, eth, trie: polish
* core: manage txpool subscription in mainpool
* eth/backend: fix test
* cmd, eth: fix test
* core/rawdb, trie/triedb/pathdb: address comments
* eth, trie: address comments
* eth: inline the function
* eth: use synced flag
* core/txpool: revert changes in txpool
* core, eth, trie: rename functions
2023-09-28 15:00:53 +08:00
|
|
|
db.tree.reset(newDiskLayer(root, 0, db, nil, newNodeBuffer(db.bufferSize, nil, 0)))
|
|
|
|
|
|
|
|
// Re-enable the database as the final step.
|
|
|
|
db.waitSync = false
|
|
|
|
rawdb.WriteSnapSyncStatusFlag(db.diskdb, rawdb.StateSyncFinished)
|
2023-08-01 20:17:32 +08:00
|
|
|
log.Info("Rebuilt trie database", "root", root)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Recover rollbacks the database to a specified historical point.
|
|
|
|
// The state is supported as the rollback destination only if it's
|
|
|
|
// canonical state and the corresponding trie histories are existent.
|
|
|
|
func (db *Database) Recover(root common.Hash, loader triestate.TrieLoader) error {
|
|
|
|
db.lock.Lock()
|
|
|
|
defer db.lock.Unlock()
|
|
|
|
|
|
|
|
// Short circuit if rollback operation is not supported.
|
core, accounts, eth, trie: handle genesis state missing (#28171)
* core, accounts, eth, trie: handle genesis state missing
* core, eth, trie: polish
* core: manage txpool subscription in mainpool
* eth/backend: fix test
* cmd, eth: fix test
* core/rawdb, trie/triedb/pathdb: address comments
* eth, trie: address comments
* eth: inline the function
* eth: use synced flag
* core/txpool: revert changes in txpool
* core, eth, trie: rename functions
2023-09-28 15:00:53 +08:00
|
|
|
if err := db.modifyAllowed(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if db.freezer == nil {
|
2023-08-01 20:17:32 +08:00
|
|
|
return errors.New("state rollback is non-supported")
|
|
|
|
}
|
|
|
|
// Short circuit if the target state is not recoverable.
|
|
|
|
root = types.TrieRootHash(root)
|
|
|
|
if !db.Recoverable(root) {
|
|
|
|
return errStateUnrecoverable
|
|
|
|
}
|
|
|
|
// Apply the state histories upon the disk layer in order.
|
|
|
|
var (
|
|
|
|
start = time.Now()
|
|
|
|
dl = db.tree.bottom()
|
|
|
|
)
|
|
|
|
for dl.rootHash() != root {
|
|
|
|
h, err := readHistory(db.freezer, dl.stateID())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
dl, err = dl.revert(h, loader)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// reset layer with newly created disk layer. It must be
|
|
|
|
// done after each revert operation, otherwise the new
|
|
|
|
// disk layer won't be accessible from outside.
|
|
|
|
db.tree.reset(dl)
|
|
|
|
}
|
|
|
|
rawdb.DeleteTrieJournal(db.diskdb)
|
|
|
|
_, err := truncateFromHead(db.diskdb, db.freezer, dl.stateID())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
log.Debug("Recovered state", "root", root, "elapsed", common.PrettyDuration(time.Since(start)))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Recoverable returns the indicator if the specified state is recoverable.
|
|
|
|
func (db *Database) Recoverable(root common.Hash) bool {
|
|
|
|
// Ensure the requested state is a known state.
|
|
|
|
root = types.TrieRootHash(root)
|
|
|
|
id := rawdb.ReadStateID(db.diskdb, root)
|
|
|
|
if id == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
// Recoverable state must below the disk layer. The recoverable
|
|
|
|
// state only refers the state that is currently not available,
|
|
|
|
// but can be restored by applying state history.
|
|
|
|
dl := db.tree.bottom()
|
|
|
|
if *id >= dl.stateID() {
|
|
|
|
return false
|
|
|
|
}
|
2024-02-28 20:40:28 +08:00
|
|
|
// This is a temporary workaround for the unavailability of the freezer in
|
|
|
|
// dev mode. As a consequence, the Pathdb loses the ability for deep reorg
|
|
|
|
// in certain cases.
|
|
|
|
// TODO(rjl493456442): Implement the in-memory ancient store.
|
|
|
|
if db.freezer == nil {
|
|
|
|
return false
|
|
|
|
}
|
2023-08-01 20:17:32 +08:00
|
|
|
// Ensure the requested state is a canonical state and all state
|
|
|
|
// histories in range [id+1, disklayer.ID] are present and complete.
|
|
|
|
return checkHistories(db.freezer, *id+1, dl.stateID()-*id, func(m *meta) error {
|
2024-02-28 20:40:28 +08:00
|
|
|
if m.parent != root {
|
2023-08-01 20:17:32 +08:00
|
|
|
return errors.New("unexpected state history")
|
|
|
|
}
|
2024-02-28 20:40:28 +08:00
|
|
|
root = m.root
|
2023-08-01 20:17:32 +08:00
|
|
|
return nil
|
|
|
|
}) == nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close closes the trie database and the held freezer.
|
|
|
|
func (db *Database) Close() error {
|
|
|
|
db.lock.Lock()
|
|
|
|
defer db.lock.Unlock()
|
|
|
|
|
all: activate pbss as experimental feature (#26274)
* all: activate pbss
* core/rawdb: fix compilation error
* cma, core, eth, les, trie: address comments
* cmd, core, eth, trie: polish code
* core, cmd, eth: address comments
* cmd, core, eth, les, light, tests: address comment
* cmd/utils: shorten log message
* trie/triedb/pathdb: limit node buffer size to 1gb
* cmd/utils: fix opening non-existing db
* cmd/utils: rename flag name
* cmd, core: group chain history flags and fix tests
* core, eth, trie: fix memory leak in snapshot generation
* cmd, eth, internal: deprecate flags
* all: enable state tests for pathdb, fixes
* cmd, core: polish code
* trie/triedb/pathdb: limit the node buffer size to 256mb
---------
Co-authored-by: Martin Holst Swende <martin@swende.se>
Co-authored-by: Péter Szilágyi <peterke@gmail.com>
2023-08-11 03:21:36 +08:00
|
|
|
// Set the database to read-only mode to prevent all
|
|
|
|
// following mutations.
|
2023-08-01 20:17:32 +08:00
|
|
|
db.readOnly = true
|
all: activate pbss as experimental feature (#26274)
* all: activate pbss
* core/rawdb: fix compilation error
* cma, core, eth, les, trie: address comments
* cmd, core, eth, trie: polish code
* core, cmd, eth: address comments
* cmd, core, eth, les, light, tests: address comment
* cmd/utils: shorten log message
* trie/triedb/pathdb: limit node buffer size to 1gb
* cmd/utils: fix opening non-existing db
* cmd/utils: rename flag name
* cmd, core: group chain history flags and fix tests
* core, eth, trie: fix memory leak in snapshot generation
* cmd, eth, internal: deprecate flags
* all: enable state tests for pathdb, fixes
* cmd, core: polish code
* trie/triedb/pathdb: limit the node buffer size to 256mb
---------
Co-authored-by: Martin Holst Swende <martin@swende.se>
Co-authored-by: Péter Szilágyi <peterke@gmail.com>
2023-08-11 03:21:36 +08:00
|
|
|
|
|
|
|
// Release the memory held by clean cache.
|
|
|
|
db.tree.bottom().resetCache()
|
|
|
|
|
|
|
|
// Close the attached state history freezer.
|
2023-08-01 20:17:32 +08:00
|
|
|
if db.freezer == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return db.freezer.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Size returns the current storage size of the memory cache in front of the
|
|
|
|
// persistent database layer.
|
2023-08-23 14:08:39 +03:00
|
|
|
func (db *Database) Size() (diffs common.StorageSize, nodes common.StorageSize) {
|
2023-08-01 20:17:32 +08:00
|
|
|
db.tree.forEach(func(layer layer) {
|
|
|
|
if diff, ok := layer.(*diffLayer); ok {
|
2023-08-23 14:08:39 +03:00
|
|
|
diffs += common.StorageSize(diff.memory)
|
2023-08-01 20:17:32 +08:00
|
|
|
}
|
|
|
|
if disk, ok := layer.(*diskLayer); ok {
|
2023-08-23 14:08:39 +03:00
|
|
|
nodes += disk.size()
|
2023-08-01 20:17:32 +08:00
|
|
|
}
|
|
|
|
})
|
2023-08-23 14:08:39 +03:00
|
|
|
return diffs, nodes
|
2023-08-01 20:17:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Initialized returns an indicator if the state data is already
|
|
|
|
// initialized in path-based scheme.
|
|
|
|
func (db *Database) Initialized(genesisRoot common.Hash) bool {
|
|
|
|
var inited bool
|
|
|
|
db.tree.forEach(func(layer layer) {
|
|
|
|
if layer.rootHash() != types.EmptyRootHash {
|
|
|
|
inited = true
|
|
|
|
}
|
|
|
|
})
|
core, cmd, trie: fix the condition of pathdb initialization (#28718)
Original problem was caused by #28595, where we made it so that as soon as we start to sync, the root of the disk layer is deleted. That is not wrong per se, but another part of the code uses the "presence of the root" as an init-check for the pathdb. And, since the init-check now failed, the code tried to re-initialize it which failed since a sync was already ongoing.
The total impact being: after a state-sync has begun, if the node for some reason is is shut down, it will refuse to start up again, with the error message: `Fatal: Failed to register the Ethereum service: waiting for sync.`.
This change also modifies how `geth removedb` works, so that the user is prompted for two things: `state data` and `ancient chain`. The former includes both the chaindb aswell as any state history stored in ancients.
---------
Co-authored-by: Martin HS <martin@swende.se>
2023-12-22 03:28:32 +08:00
|
|
|
if !inited {
|
|
|
|
inited = rawdb.ReadSnapSyncStatusFlag(db.diskdb) != rawdb.StateSyncUnknown
|
|
|
|
}
|
2023-08-01 20:17:32 +08:00
|
|
|
return inited
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetBufferSize sets the node buffer size to the provided value(in bytes).
|
|
|
|
func (db *Database) SetBufferSize(size int) error {
|
|
|
|
db.lock.Lock()
|
|
|
|
defer db.lock.Unlock()
|
|
|
|
|
all: activate pbss as experimental feature (#26274)
* all: activate pbss
* core/rawdb: fix compilation error
* cma, core, eth, les, trie: address comments
* cmd, core, eth, trie: polish code
* core, cmd, eth: address comments
* cmd, core, eth, les, light, tests: address comment
* cmd/utils: shorten log message
* trie/triedb/pathdb: limit node buffer size to 1gb
* cmd/utils: fix opening non-existing db
* cmd/utils: rename flag name
* cmd, core: group chain history flags and fix tests
* core, eth, trie: fix memory leak in snapshot generation
* cmd, eth, internal: deprecate flags
* all: enable state tests for pathdb, fixes
* cmd, core: polish code
* trie/triedb/pathdb: limit the node buffer size to 256mb
---------
Co-authored-by: Martin Holst Swende <martin@swende.se>
Co-authored-by: Péter Szilágyi <peterke@gmail.com>
2023-08-11 03:21:36 +08:00
|
|
|
if size > maxBufferSize {
|
|
|
|
log.Info("Capped node buffer size", "provided", common.StorageSize(size), "adjusted", common.StorageSize(maxBufferSize))
|
|
|
|
size = maxBufferSize
|
|
|
|
}
|
2023-08-01 20:17:32 +08:00
|
|
|
db.bufferSize = size
|
|
|
|
return db.tree.bottom().setBufferSize(db.bufferSize)
|
|
|
|
}
|
|
|
|
|
core, accounts, eth, trie: handle genesis state missing (#28171)
* core, accounts, eth, trie: handle genesis state missing
* core, eth, trie: polish
* core: manage txpool subscription in mainpool
* eth/backend: fix test
* cmd, eth: fix test
* core/rawdb, trie/triedb/pathdb: address comments
* eth, trie: address comments
* eth: inline the function
* eth: use synced flag
* core/txpool: revert changes in txpool
* core, eth, trie: rename functions
2023-09-28 15:00:53 +08:00
|
|
|
// modifyAllowed returns the indicator if mutation is allowed. This function
|
|
|
|
// assumes the db.lock is already held.
|
|
|
|
func (db *Database) modifyAllowed() error {
|
|
|
|
if db.readOnly {
|
|
|
|
return errDatabaseReadOnly
|
|
|
|
}
|
|
|
|
if db.waitSync {
|
|
|
|
return errDatabaseWaitSync
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2024-03-22 20:12:10 +08:00
|
|
|
|
|
|
|
// AccountHistory inspects the account history within the specified range.
|
|
|
|
//
|
|
|
|
// Start: State ID of the first history object for the query. 0 implies the first
|
|
|
|
// available object is selected as the starting point.
|
|
|
|
//
|
|
|
|
// End: State ID of the last history for the query. 0 implies the last available
|
|
|
|
// object is selected as the ending point. Note end is included in the query.
|
|
|
|
func (db *Database) AccountHistory(address common.Address, start, end uint64) (*HistoryStats, error) {
|
|
|
|
return accountHistory(db.freezer, address, start, end)
|
|
|
|
}
|
|
|
|
|
|
|
|
// StorageHistory inspects the storage history within the specified range.
|
|
|
|
//
|
|
|
|
// Start: State ID of the first history object for the query. 0 implies the first
|
|
|
|
// available object is selected as the starting point.
|
|
|
|
//
|
|
|
|
// End: State ID of the last history for the query. 0 implies the last available
|
|
|
|
// object is selected as the ending point. Note end is included in the query.
|
|
|
|
//
|
|
|
|
// Note, slot refers to the hash of the raw slot key.
|
|
|
|
func (db *Database) StorageHistory(address common.Address, slot common.Hash, start uint64, end uint64) (*HistoryStats, error) {
|
|
|
|
return storageHistory(db.freezer, address, slot, start, end)
|
|
|
|
}
|
|
|
|
|
|
|
|
// HistoryRange returns the block numbers associated with earliest and latest
|
|
|
|
// state history in the local store.
|
|
|
|
func (db *Database) HistoryRange() (uint64, uint64, error) {
|
|
|
|
return historyRange(db.freezer)
|
|
|
|
}
|