forked from cerc-io/plugeth
Merge tag 'v1.10.14' into develop
This commit is contained in:
@@ -48,13 +48,13 @@ var (
|
||||
// accountCheckRange is the upper limit of the number of accounts involved in
|
||||
// each range check. This is a value estimated based on experience. If this
|
||||
// value is too large, the failure rate of range prove will increase. Otherwise
|
||||
// the the value is too small, the efficiency of the state recovery will decrease.
|
||||
// the value is too small, the efficiency of the state recovery will decrease.
|
||||
accountCheckRange = 128
|
||||
|
||||
// storageCheckRange is the upper limit of the number of storage slots involved
|
||||
// in each range check. This is a value estimated based on experience. If this
|
||||
// value is too large, the failure rate of range prove will increase. Otherwise
|
||||
// the the value is too small, the efficiency of the state recovery will decrease.
|
||||
// the value is too small, the efficiency of the state recovery will decrease.
|
||||
storageCheckRange = 1024
|
||||
|
||||
// errMissingTrie is returned if the target trie is missing while the generation
|
||||
|
||||
@@ -21,67 +21,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
)
|
||||
|
||||
// wipeSnapshot starts a goroutine to iterate over the entire key-value database
|
||||
// and delete all the data associated with the snapshot (accounts, storage,
|
||||
// metadata). After all is done, the snapshot range of the database is compacted
|
||||
// to free up unused data blocks.
|
||||
func wipeSnapshot(db ethdb.KeyValueStore, full bool) chan struct{} {
|
||||
// Wipe the snapshot root marker synchronously
|
||||
if full {
|
||||
rawdb.DeleteSnapshotRoot(db)
|
||||
}
|
||||
// Wipe everything else asynchronously
|
||||
wiper := make(chan struct{}, 1)
|
||||
go func() {
|
||||
if err := wipeContent(db); err != nil {
|
||||
log.Error("Failed to wipe state snapshot", "err", err) // Database close will trigger this
|
||||
return
|
||||
}
|
||||
close(wiper)
|
||||
}()
|
||||
return wiper
|
||||
}
|
||||
|
||||
// wipeContent iterates over the entire key-value database and deletes all the
|
||||
// data associated with the snapshot (accounts, storage), but not the root hash
|
||||
// as the wiper is meant to run on a background thread but the root needs to be
|
||||
// removed in sync to avoid data races. After all is done, the snapshot range of
|
||||
// the database is compacted to free up unused data blocks.
|
||||
func wipeContent(db ethdb.KeyValueStore) error {
|
||||
if err := wipeKeyRange(db, "accounts", rawdb.SnapshotAccountPrefix, nil, nil, len(rawdb.SnapshotAccountPrefix)+common.HashLength, snapWipedAccountMeter, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := wipeKeyRange(db, "storage", rawdb.SnapshotStoragePrefix, nil, nil, len(rawdb.SnapshotStoragePrefix)+2*common.HashLength, snapWipedStorageMeter, true); err != nil {
|
||||
return err
|
||||
}
|
||||
// Compact the snapshot section of the database to get rid of unused space
|
||||
start := time.Now()
|
||||
|
||||
log.Info("Compacting snapshot account area ")
|
||||
end := common.CopyBytes(rawdb.SnapshotAccountPrefix)
|
||||
end[len(end)-1]++
|
||||
|
||||
if err := db.Compact(rawdb.SnapshotAccountPrefix, end); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("Compacting snapshot storage area ")
|
||||
end = common.CopyBytes(rawdb.SnapshotStoragePrefix)
|
||||
end[len(end)-1]++
|
||||
|
||||
if err := db.Compact(rawdb.SnapshotStoragePrefix, end); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("Compacted snapshot area in database", "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// wipeKeyRange deletes a range of keys from the database starting with prefix
|
||||
// and having a specific total key length. The start and limit is optional for
|
||||
// specifying a particular key range for deletion.
|
||||
|
||||
@@ -30,95 +30,50 @@ import (
|
||||
func TestWipe(t *testing.T) {
|
||||
// Create a database with some random snapshot data
|
||||
db := memorydb.New()
|
||||
|
||||
for i := 0; i < 128; i++ {
|
||||
account := randomHash()
|
||||
rawdb.WriteAccountSnapshot(db, account, randomHash().Bytes())
|
||||
for j := 0; j < 1024; j++ {
|
||||
rawdb.WriteStorageSnapshot(db, account, randomHash(), randomHash().Bytes())
|
||||
}
|
||||
rawdb.WriteAccountSnapshot(db, randomHash(), randomHash().Bytes())
|
||||
}
|
||||
rawdb.WriteSnapshotRoot(db, randomHash())
|
||||
|
||||
// Add some random non-snapshot data too to make wiping harder
|
||||
for i := 0; i < 65536; i++ {
|
||||
// Generate a key that's the wrong length for a state snapshot item
|
||||
var keysize int
|
||||
for keysize == 0 || keysize == 32 || keysize == 64 {
|
||||
keysize = 8 + rand.Intn(64) // +8 to ensure we will "never" randomize duplicates
|
||||
}
|
||||
// Randomize the suffix, dedup and inject it under the snapshot namespace
|
||||
keysuffix := make([]byte, keysize)
|
||||
for i := 0; i < 500; i++ {
|
||||
// Generate keys with wrong length for a state snapshot item
|
||||
keysuffix := make([]byte, 31)
|
||||
rand.Read(keysuffix)
|
||||
|
||||
if rand.Int31n(2) == 0 {
|
||||
db.Put(append(rawdb.SnapshotAccountPrefix, keysuffix...), randomHash().Bytes())
|
||||
} else {
|
||||
db.Put(append(rawdb.SnapshotStoragePrefix, keysuffix...), randomHash().Bytes())
|
||||
db.Put(append(rawdb.SnapshotAccountPrefix, keysuffix...), randomHash().Bytes())
|
||||
keysuffix = make([]byte, 33)
|
||||
rand.Read(keysuffix)
|
||||
db.Put(append(rawdb.SnapshotAccountPrefix, keysuffix...), randomHash().Bytes())
|
||||
}
|
||||
count := func() (items int) {
|
||||
it := db.NewIterator(rawdb.SnapshotAccountPrefix, nil)
|
||||
defer it.Release()
|
||||
for it.Next() {
|
||||
if len(it.Key()) == len(rawdb.SnapshotAccountPrefix)+common.HashLength {
|
||||
items++
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
// Sanity check that all the keys are present
|
||||
var items int
|
||||
|
||||
it := db.NewIterator(rawdb.SnapshotAccountPrefix, nil)
|
||||
defer it.Release()
|
||||
|
||||
for it.Next() {
|
||||
key := it.Key()
|
||||
if len(key) == len(rawdb.SnapshotAccountPrefix)+common.HashLength {
|
||||
items++
|
||||
}
|
||||
if items := count(); items != 128 {
|
||||
t.Fatalf("snapshot size mismatch: have %d, want %d", items, 128)
|
||||
}
|
||||
it = db.NewIterator(rawdb.SnapshotStoragePrefix, nil)
|
||||
defer it.Release()
|
||||
|
||||
for it.Next() {
|
||||
key := it.Key()
|
||||
if len(key) == len(rawdb.SnapshotStoragePrefix)+2*common.HashLength {
|
||||
items++
|
||||
}
|
||||
// Wipe the accounts
|
||||
if err := wipeKeyRange(db, "accounts", rawdb.SnapshotAccountPrefix, nil, nil,
|
||||
len(rawdb.SnapshotAccountPrefix)+common.HashLength, snapWipedAccountMeter, true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if items != 128+128*1024 {
|
||||
t.Fatalf("snapshot size mismatch: have %d, want %d", items, 128+128*1024)
|
||||
}
|
||||
if hash := rawdb.ReadSnapshotRoot(db); hash == (common.Hash{}) {
|
||||
t.Errorf("snapshot block marker mismatch: have %#x, want <not-nil>", hash)
|
||||
}
|
||||
// Wipe all snapshot entries from the database
|
||||
<-wipeSnapshot(db, true)
|
||||
|
||||
// Iterate over the database end ensure no snapshot information remains
|
||||
it = db.NewIterator(rawdb.SnapshotAccountPrefix, nil)
|
||||
defer it.Release()
|
||||
|
||||
for it.Next() {
|
||||
key := it.Key()
|
||||
if len(key) == len(rawdb.SnapshotAccountPrefix)+common.HashLength {
|
||||
t.Errorf("snapshot entry remained after wipe: %x", key)
|
||||
}
|
||||
}
|
||||
it = db.NewIterator(rawdb.SnapshotStoragePrefix, nil)
|
||||
defer it.Release()
|
||||
|
||||
for it.Next() {
|
||||
key := it.Key()
|
||||
if len(key) == len(rawdb.SnapshotStoragePrefix)+2*common.HashLength {
|
||||
t.Errorf("snapshot entry remained after wipe: %x", key)
|
||||
}
|
||||
}
|
||||
if hash := rawdb.ReadSnapshotRoot(db); hash != (common.Hash{}) {
|
||||
t.Errorf("snapshot block marker remained after wipe: %#x", hash)
|
||||
if items := count(); items != 0 {
|
||||
t.Fatalf("snapshot size mismatch: have %d, want %d", items, 0)
|
||||
}
|
||||
// Iterate over the database and ensure miscellaneous items are present
|
||||
items = 0
|
||||
|
||||
it = db.NewIterator(nil, nil)
|
||||
items := 0
|
||||
it := db.NewIterator(nil, nil)
|
||||
defer it.Release()
|
||||
|
||||
for it.Next() {
|
||||
items++
|
||||
}
|
||||
if items != 65536 {
|
||||
t.Fatalf("misc item count mismatch: have %d, want %d", items, 65536)
|
||||
if items != 1000 {
|
||||
t.Fatalf("misc item count mismatch: have %d, want %d", items, 1000)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -27,7 +27,7 @@ import (
|
||||
)
|
||||
|
||||
// NewStateSync create a new state trie download scheduler.
|
||||
func NewStateSync(root common.Hash, database ethdb.KeyValueReader, bloom *trie.SyncBloom, onLeaf func(paths [][]byte, leaf []byte) error) *trie.Sync {
|
||||
func NewStateSync(root common.Hash, database ethdb.KeyValueReader, onLeaf func(paths [][]byte, leaf []byte) error) *trie.Sync {
|
||||
// Register the storage slot callback if the external callback is specified.
|
||||
var onSlot func(paths [][]byte, hexpath []byte, leaf []byte, parent common.Hash) error
|
||||
if onLeaf != nil {
|
||||
@@ -52,6 +52,6 @@ func NewStateSync(root common.Hash, database ethdb.KeyValueReader, bloom *trie.S
|
||||
syncer.AddCodeEntry(common.BytesToHash(obj.CodeHash), hexpath, parent)
|
||||
return nil
|
||||
}
|
||||
syncer = trie.NewSync(root, database, onAccount, bloom)
|
||||
syncer = trie.NewSync(root, database, onAccount)
|
||||
return syncer
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethdb/memorydb"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
@@ -134,7 +133,7 @@ func checkStateConsistency(db ethdb.Database, root common.Hash) error {
|
||||
// Tests that an empty state is not scheduled for syncing.
|
||||
func TestEmptyStateSync(t *testing.T) {
|
||||
empty := common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
|
||||
sync := NewStateSync(empty, rawdb.NewMemoryDatabase(), trie.NewSyncBloom(1, memorydb.New()), nil)
|
||||
sync := NewStateSync(empty, rawdb.NewMemoryDatabase(), nil)
|
||||
if nodes, paths, codes := sync.Missing(1); len(nodes) != 0 || len(paths) != 0 || len(codes) != 0 {
|
||||
t.Errorf(" content requested for empty state: %v, %v, %v", nodes, paths, codes)
|
||||
}
|
||||
@@ -171,7 +170,7 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool) {
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb := rawdb.NewMemoryDatabase()
|
||||
sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb), nil)
|
||||
sched := NewStateSync(srcRoot, dstDb, nil)
|
||||
|
||||
nodes, paths, codes := sched.Missing(count)
|
||||
var (
|
||||
@@ -250,7 +249,7 @@ func TestIterativeDelayedStateSync(t *testing.T) {
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb := rawdb.NewMemoryDatabase()
|
||||
sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb), nil)
|
||||
sched := NewStateSync(srcRoot, dstDb, nil)
|
||||
|
||||
nodes, _, codes := sched.Missing(0)
|
||||
queue := append(append([]common.Hash{}, nodes...), codes...)
|
||||
@@ -298,7 +297,7 @@ func testIterativeRandomStateSync(t *testing.T, count int) {
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb := rawdb.NewMemoryDatabase()
|
||||
sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb), nil)
|
||||
sched := NewStateSync(srcRoot, dstDb, nil)
|
||||
|
||||
queue := make(map[common.Hash]struct{})
|
||||
nodes, _, codes := sched.Missing(count)
|
||||
@@ -348,7 +347,7 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb := rawdb.NewMemoryDatabase()
|
||||
sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb), nil)
|
||||
sched := NewStateSync(srcRoot, dstDb, nil)
|
||||
|
||||
queue := make(map[common.Hash]struct{})
|
||||
nodes, _, codes := sched.Missing(0)
|
||||
@@ -415,7 +414,7 @@ func TestIncompleteStateSync(t *testing.T) {
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb := rawdb.NewMemoryDatabase()
|
||||
sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb), nil)
|
||||
sched := NewStateSync(srcRoot, dstDb, nil)
|
||||
|
||||
var added []common.Hash
|
||||
|
||||
|
||||
Reference in New Issue
Block a user