Skip to content

Commit

Permalink
snapshot block hash changes
Browse files Browse the repository at this point in the history
  • Loading branch information
darioush committed Nov 21, 2024
1 parent f9f6852 commit 4f1e58e
Show file tree
Hide file tree
Showing 14 changed files with 85 additions and 87 deletions.
38 changes: 24 additions & 14 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ import (
"github.com/ava-labs/coreth/consensus/misc/eip4844"
"github.com/ava-labs/coreth/core/rawdb"
"github.com/ava-labs/coreth/core/state"
"github.com/ava-labs/coreth/core/state/snapshot"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/internal/version"
"github.com/ava-labs/coreth/metrics"
Expand All @@ -52,6 +51,7 @@ import (
"github.com/ava-labs/coreth/triedb/pathdb"
"github.com/ava-labs/libevm/common"
"github.com/ava-labs/libevm/common/lru"
ethsnapshot "github.com/ava-labs/libevm/core/state/snapshot"
"github.com/ava-labs/libevm/core/vm"
"github.com/ava-labs/libevm/ethdb"
"github.com/ava-labs/libevm/event"
Expand Down Expand Up @@ -242,11 +242,11 @@ type BlockChain struct {
chainConfig *params.ChainConfig // Chain & network configuration
cacheConfig *CacheConfig // Cache configuration for pruning

db ethdb.Database // Low level persistent database to store final content in
snaps *snapshot.Tree // Snapshot tree for fast trie leaf access
triedb *triedb.Database // The database handler for maintaining trie nodes.
stateCache state.Database // State database to reuse between imports (contains state cache)
txIndexer *txIndexer // Transaction indexer, might be nil if not enabled
db ethdb.Database // Low level persistent database to store final content in
snaps *ethsnapshot.Tree // Snapshot tree for fast trie leaf access
triedb *triedb.Database // The database handler for maintaining trie nodes.
stateCache state.Database // State database to reuse between imports (contains state cache)
txIndexer *txIndexer // Transaction indexer, might be nil if not enabled
stateManager TrieWriter

hc *HeaderChain
Expand Down Expand Up @@ -940,6 +940,14 @@ func (bc *BlockChain) Stop() {

// Ensure that the entirety of the state snapshot is journaled to disk.
if bc.snaps != nil {
_, err := bc.db.Has(nil)
dbOpen := err == nil
if dbOpen {
//if _, err = bc.snaps.Journal(bc.CurrentBlock().Root); err != nil {
// log.Error("Failed to journal state snapshot", "err", err)
//}
}
bc.snaps.AbortGeneration()
bc.snaps.Release()
}
if bc.triedb.Scheme() == rawdb.PathScheme {
Expand Down Expand Up @@ -1695,18 +1703,21 @@ func (bc *BlockChain) commitWithSnap(
) (common.Hash, error) {
// If snapshots are enabled, WithBlockHashes must be called as snapshot layers
// are stored by block hash.
if bc.snaps != nil {
bc.snaps.WithBlockHashes(current.Hash(), current.ParentHash())
}
root, err := statedb.Commit(current.NumberU64(), bc.chainConfig.IsEIP158(current.Number()))
root, err := statedb.Commit(
current.NumberU64(), bc.chainConfig.IsEIP158(current.Number()),
ethsnapshot.WithBlockHashes(current.Hash(), current.ParentHash()),
)
if err != nil {
return common.Hash{}, err
}
// Upstream does not perform a snapshot update if the root is the same as the
// parent root, however here the snapshots are based on the block hash, so
// this update is necessary.
if bc.snaps != nil && root == parentRoot {
if err := bc.snaps.Update(root, parentRoot, nil, nil, nil); err != nil {
if err := bc.snaps.Update(
root, parentRoot, nil, nil, nil,
ethsnapshot.WithBlockHashes(current.Hash(), current.ParentHash()),
); err != nil {
return common.Hash{}, err
}
}
Expand All @@ -1728,14 +1739,13 @@ func (bc *BlockChain) initSnapshot(b *types.Header) {
asyncBuild := !bc.cacheConfig.SnapshotWait && b.Number.Uint64() > 0
noBuild := bc.cacheConfig.SnapshotNoBuild && b.Number.Uint64() > 0
log.Info("Initializing snapshots", "async", asyncBuild, "rebuild", !noBuild, "headHash", b.Hash(), "headRoot", b.Root)
snapconfig := snapshot.Config{
snapconfig := ethsnapshot.Config{
CacheSize: bc.cacheConfig.SnapshotLimit,
NoBuild: noBuild,
AsyncBuild: asyncBuild,
SkipVerify: !bc.cacheConfig.SnapshotVerify,
}
var err error
bc.snaps, err = snapshot.New(snapconfig, bc.db, bc.triedb, b.Hash(), b.Root)
bc.snaps, err = ethsnapshot.New(snapconfig, bc.db, bc.triedb, b.Root)
if err != nil {
log.Error("failed to initialize snapshots", "headHash", b.Hash(), "headRoot", b.Root, "err", err, "async", asyncBuild)
}
Expand Down
6 changes: 5 additions & 1 deletion core/blockchain_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,10 +264,14 @@ func (bc *BlockChain) Config() *params.ChainConfig { return bc.chainConfig }
func (bc *BlockChain) Engine() consensus.Engine { return bc.engine }

// Snapshots returns the blockchain snapshot tree.
func (bc *BlockChain) Snapshots() *snapshot.Tree {
func (bc *BlockChain) Snapshots() snapshot.DiskIterable {
return bc.snaps
}

func (bc *BlockChain) VerifySnapshot(root common.Hash) error {
return bc.snaps.Verify(root)
}

// Validator returns the current validator.
func (bc *BlockChain) Validator() Validator {
return bc.validator
Expand Down
2 changes: 1 addition & 1 deletion core/blockchain_snapshot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func (basic *snapshotTestBasic) verify(t *testing.T, chain *BlockChain, blocks [
t.Errorf("The corresponding block[%d] of snapshot disk layer is missing", basic.expSnapshotBottom)
} else if !bytes.Equal(chain.snaps.DiskRoot().Bytes(), block.Root().Bytes()) {
t.Errorf("The snapshot disk layer root is incorrect, want %x, get %x", block.Root(), chain.snaps.DiskRoot())
} else if len(chain.snaps.Snapshots(block.Hash(), -1, false)) != 1 {
} else if len(chain.snaps.Snapshots(block.Root(), -1, false)) != 1 {
t.Errorf("The corresponding block[%d] of snapshot disk layer is missing", basic.expSnapshotBottom)
}

Expand Down
11 changes: 5 additions & 6 deletions core/state/pruner/pruner.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ import (
"time"

"github.com/ava-labs/coreth/core/rawdb"
"github.com/ava-labs/coreth/core/state/snapshot"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/libevm/common"
ethsnapshot "github.com/ava-labs/libevm/core/state/snapshot"
"github.com/ava-labs/libevm/ethdb"
"github.com/ava-labs/libevm/log"
"github.com/ava-labs/libevm/rlp"
Expand Down Expand Up @@ -87,7 +87,7 @@ type Pruner struct {
chainHeader *types.Header
db ethdb.Database
stateBloom *stateBloom
snaptree *snapshot.Tree
snaptree *ethsnapshot.Tree
}

// NewPruner creates the pruner instance.
Expand All @@ -103,13 +103,12 @@ func NewPruner(db ethdb.Database, config Config) (*Pruner, error) {
// us from ever needing to enter RecoverPruning in an invalid pruning session (a session where we do not have
// the protected trie in the triedb and in the snapshot disk layer).

snapconfig := snapshot.Config{
snapconfig := ethsnapshot.Config{
CacheSize: 256,
AsyncBuild: false,
NoBuild: true,
SkipVerify: true,
}
snaptree, err := snapshot.New(snapconfig, db, triedb, headBlock.Hash(), headBlock.Root())
snaptree, err := ethsnapshot.New(snapconfig, db, triedb, headBlock.Root())
if err != nil {
return nil, fmt.Errorf("failed to create snapshot for pruning, must restart without offline pruning disabled to recover: %w", err) // The relevant snapshot(s) might not exist
}
Expand Down Expand Up @@ -285,7 +284,7 @@ func (p *Pruner) Prune(root common.Hash) error {
// Traverse the target state, re-construct the whole state trie and
// commit to the given bloom filter.
start := time.Now()
if err := snapshot.GenerateTrie(p.snaptree, root, p.db, p.stateBloom); err != nil {
if err := ethsnapshot.GenerateTrie(p.snaptree, root, p.db, p.stateBloom); err != nil {
return err
}
// Traverse the genesis, put all genesis state entries into the
Expand Down
17 changes: 3 additions & 14 deletions core/state/snapshot/iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (

"github.com/ava-labs/coreth/core/rawdb"
"github.com/ava-labs/libevm/common"
ethsnapshot "github.com/ava-labs/libevm/core/state/snapshot"
"github.com/ava-labs/libevm/ethdb"
)

Expand All @@ -59,23 +60,11 @@ type Iterator interface {

// AccountIterator is an iterator to step over all the accounts in a snapshot,
// which may or may not be composed of multiple layers.
type AccountIterator interface {
Iterator

// Account returns the RLP encoded slim account the iterator is currently at.
// An error will be returned if the iterator becomes invalid
Account() []byte
}
type AccountIterator = ethsnapshot.AccountIterator

// StorageIterator is an iterator to step over the specific storage in a snapshot,
// which may or may not be composed of multiple layers.
type StorageIterator interface {
Iterator

// Slot returns the storage slot the iterator is currently at. An error will
// be returned if the iterator becomes invalid
Slot() []byte
}
type StorageIterator = ethsnapshot.StorageIterator

// diffAccountIterator is an account iterator that steps over the accounts (both
// live and deleted) contained within a single diff layer. Higher order iterators
Expand Down
32 changes: 9 additions & 23 deletions core/state/snapshot/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ import (
"time"

"github.com/ava-labs/coreth/core/rawdb"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/metrics"
"github.com/ava-labs/libevm/common"
ethsnapshot "github.com/ava-labs/libevm/core/state/snapshot"
"github.com/ava-labs/libevm/ethdb"
"github.com/ava-labs/libevm/log"
"github.com/ava-labs/libevm/triedb"
Expand Down Expand Up @@ -118,28 +118,7 @@ var (
)

// Snapshot represents the functionality supported by a snapshot storage layer.
type Snapshot interface {
// Root returns the root hash for which this snapshot was made.
Root() common.Hash

// Account directly retrieves the account associated with a particular hash in
// the snapshot slim data format.
Account(hash common.Hash) (*types.SlimAccount, error)

// AccountRLP directly retrieves the account RLP associated with a particular
// hash in the snapshot slim data format.
AccountRLP(hash common.Hash) ([]byte, error)

// Storage directly retrieves the storage data associated with a particular hash,
// within a particular account.
Storage(accountHash, storageHash common.Hash) ([]byte, error)

// AccountIterator creates an account iterator over the account trie given by the provided root hash.
AccountIterator(seek common.Hash) AccountIterator

// StorageIterator creates a storage iterator over the storage trie given by the provided root hash.
StorageIterator(account common.Hash, seek common.Hash) (StorageIterator, bool)
}
type Snapshot = ethsnapshot.Snapshot

// snapshot is the internal version of the snapshot data layer that supports some
// additional methods compared to the public API.
Expand All @@ -164,6 +143,12 @@ type snapshot interface {
// Stale return whether this layer has become stale (was flattened across) or
// if it's still live.
Stale() bool

// AccountIterator creates an account iterator over an arbitrary layer.
AccountIterator(seek common.Hash) AccountIterator

// StorageIterator creates a storage iterator over an arbitrary layer.
StorageIterator(account common.Hash, seek common.Hash) (StorageIterator, bool)
}

// Config includes the configurations for snapshots.
Expand Down Expand Up @@ -339,6 +324,7 @@ func (t *Tree) Update(
destructs map[common.Hash]struct{},
accounts map[common.Hash][]byte,
storage map[common.Hash]map[common.Hash][]byte,
opts ...ethsnapshot.LibEVMOption,
) error {
blockHash := *t.blockHash
parentBlockHash := *t.parentBlockHash
Expand Down
17 changes: 16 additions & 1 deletion core/state/snapshot/snapshot_ext.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,24 @@ func (t *Tree) DiskStorageIterator(account common.Hash, seek common.Hash) Storag
return it
}

type SnapshotIterable interface {
Snapshot

// AccountIterator creates an account iterator over an arbitrary layer.
AccountIterator(seek common.Hash) AccountIterator

// StorageIterator creates a storage iterator over an arbitrary layer.
StorageIterator(account common.Hash, seek common.Hash) (StorageIterator, bool)
}

type DiskIterable interface {
DiskAccountIterator(seek common.Hash) AccountIterator
DiskStorageIterator(account common.Hash, seek common.Hash) StorageIterator
}

// NewDiskLayer creates a diskLayer for direct access to the contents of the on-disk
// snapshot. Does not perform any validation.
func NewDiskLayer(diskdb ethdb.KeyValueStore) Snapshot {
func NewDiskLayer(diskdb ethdb.KeyValueStore) SnapshotIterable {
return &diskLayer{
diskdb: diskdb,
created: time.Now(),
Expand Down
18 changes: 10 additions & 8 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/metrics"
"github.com/ava-labs/libevm/common"
ethsnapshot "github.com/ava-labs/libevm/core/state/snapshot"
"github.com/ava-labs/libevm/crypto"
"github.com/ava-labs/libevm/log"
"github.com/ava-labs/libevm/params"
Expand All @@ -58,16 +59,17 @@ type revision struct {
}

type snapshotTree interface {
Snapshot(root common.Hash) snapshot.Snapshot
Snapshot(root common.Hash) ethsnapshot.Snapshot
Update(
blockRoot common.Hash,
parentRoot common.Hash,
destructs map[common.Hash]struct{},
accounts map[common.Hash][]byte,
storage map[common.Hash]map[common.Hash][]byte,
opts ...ethsnapshot.LibEVMOption,
) error
StorageIterator(root common.Hash, account common.Hash, seek common.Hash) (snapshot.StorageIterator, error)
Cap(root common.Hash, layers int) error
StorageIterator(root common.Hash, account common.Hash, seek common.Hash) (ethsnapshot.StorageIterator, error)
//Cap(root common.Hash, layers int, ...opts ethsnapshot.) error
}

// StateDB structs within the ethereum protocol are used to store anything
Expand Down Expand Up @@ -1227,7 +1229,7 @@ func (s *StateDB) handleDestruction(nodes *trienode.MergedNodeSet) (map[common.A
//
// The associated block number of the state transition is also provided
// for more chain context.
func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, error) {
func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool, opts ...ethsnapshot.LibEVMOption) (common.Hash, error) {
// Short circuit in case any database failure occurred earlier.
if s.dbErr != nil {
return common.Hash{}, fmt.Errorf("commit aborted due to earlier error: %v", s.dbErr)
Expand Down Expand Up @@ -1317,16 +1319,16 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er
start := time.Now()
// Only update if there's a state transition (skip empty Clique blocks)
if parent := s.snap.Root(); parent != root {
if err := s.snaps.Update(root, parent, s.convertAccountSet(s.stateObjectsDestruct), s.accounts, s.storages); err != nil {
if err := s.snaps.Update(root, parent, s.convertAccountSet(s.stateObjectsDestruct), s.accounts, s.storages, opts...); err != nil {
log.Warn("Failed to update snapshot tree", "from", parent, "to", root, "err", 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
if err := s.snaps.Cap(root, 128); err != nil {
log.Warn("Failed to cap snapshot tree", "root", root, "layers", 128, "err", err)
}
// if err := s.snaps.Cap(root, 128); err != nil {
// log.Warn("Failed to cap snapshot tree", "root", root, "layers", 128, "err", err)
// }
}
if metrics.EnabledExpensive {
s.SnapshotCommits += time.Since(start)
Expand Down
9 changes: 1 addition & 8 deletions core/test_blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ func TestInsertLongForkedChain(t *testing.T, create func(db ethdb.Database, gspe

if blockchain.snaps != nil {
// Snap layer count should be 1 fewer
if want, got := len(chain1)+len(chain2), blockchain.snaps.NumBlockLayers(); got != want {
if want, got := len(chain1), blockchain.snaps.NumBlockLayers(); got != want {
t.Fatalf("incorrect snapshot layer count; got %d, want %d", got, want)
}
}
Expand All @@ -360,13 +360,6 @@ func TestInsertLongForkedChain(t *testing.T, create func(db ethdb.Database, gspe
if err := blockchain.Reject(chain2[i]); err != nil {
t.Fatal(err)
}

if blockchain.snaps != nil {
// Snap layer count should decrease by 1 per Reject
if want, got := len(chain1)+len(chain2)-i-1, blockchain.snaps.NumBlockLayers(); got != want {
t.Fatalf("incorrect snapshot layer count; got %d, want %d", got, want)
}
}
}

if blockchain.snaps != nil {
Expand Down
2 changes: 1 addition & 1 deletion plugin/evm/syncervm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ func TestStateSyncToggleEnabledToDisabled(t *testing.T) {
}
// Verify the snapshot disk layer matches the last block root
lastRoot := syncDisabledVM.blockChain.CurrentBlock().Root
if err := syncDisabledVM.blockChain.Snapshots().Verify(lastRoot); err != nil {
if err := syncDisabledVM.blockChain.VerifySnapshot(lastRoot); err != nil {
t.Fatal(err)
}
syncDisabledVM.blockChain.DrainAcceptorQueue()
Expand Down
2 changes: 1 addition & 1 deletion sync/handlers/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type BlockProvider interface {
}

type SnapshotProvider interface {
Snapshots() *snapshot.Tree
Snapshots() snapshot.DiskIterable
}

type SyncDataProvider interface {
Expand Down
2 changes: 1 addition & 1 deletion sync/handlers/leafs_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ type responseBuilder struct {
request *message.LeafsRequest
response *message.LeafsResponse
t *trie.Trie
snap *snapshot.Tree
snap snapshot.DiskIterable
keyLength int
limit uint16

Expand Down
Loading

0 comments on commit 4f1e58e

Please sign in to comment.