-
Notifications
You must be signed in to change notification settings - Fork 225
/
test_sync.go
138 lines (121 loc) · 5.11 KB
/
test_sync.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// (c) 2021-2022, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package statesync
import (
"bytes"
"math/rand"
"testing"
"github.com/ava-labs/subnet-evm/accounts/keystore"
"github.com/ava-labs/subnet-evm/core/rawdb"
"github.com/ava-labs/subnet-evm/core/state/snapshot"
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/ethdb"
"github.com/ava-labs/subnet-evm/trie"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
"github.com/stretchr/testify/assert"
)
// assertDBConsistency checks [serverTrieDB] and [clientTrieDB] have the same EVM state trie at [root],
// and that [clientTrieDB.DiskDB] has corresponding account & snapshot values.
// Also verifies any code referenced by the EVM state is present in [clientTrieDB] and the hash is correct.
func assertDBConsistency(t testing.TB, root common.Hash, clientDB ethdb.Database, serverTrieDB, clientTrieDB *trie.Database) {
numSnapshotAccounts := 0
accountIt := rawdb.IterateAccountSnapshots(clientDB)
defer accountIt.Release()
for accountIt.Next() {
if !bytes.HasPrefix(accountIt.Key(), rawdb.SnapshotAccountPrefix) || len(accountIt.Key()) != len(rawdb.SnapshotAccountPrefix)+common.HashLength {
continue
}
numSnapshotAccounts++
}
if err := accountIt.Error(); err != nil {
t.Fatal(err)
}
trieAccountLeaves := 0
trie.AssertTrieConsistency(t, root, serverTrieDB, clientTrieDB, func(key, val []byte) error {
trieAccountLeaves++
accHash := common.BytesToHash(key)
var acc types.StateAccount
if err := rlp.DecodeBytes(val, &acc); err != nil {
return err
}
// check snapshot consistency
snapshotVal := rawdb.ReadAccountSnapshot(clientDB, accHash)
expectedSnapshotVal := snapshot.SlimAccountRLP(acc.Nonce, acc.Balance, acc.Root, acc.CodeHash)
assert.Equal(t, expectedSnapshotVal, snapshotVal)
// check code consistency
if !bytes.Equal(acc.CodeHash, types.EmptyCodeHash[:]) {
codeHash := common.BytesToHash(acc.CodeHash)
code := rawdb.ReadCode(clientDB, codeHash)
actualHash := crypto.Keccak256Hash(code)
assert.NotZero(t, len(code))
assert.Equal(t, codeHash, actualHash)
}
if acc.Root == types.EmptyRootHash {
return nil
}
storageIt := rawdb.IterateStorageSnapshots(clientDB, accHash)
defer storageIt.Release()
snapshotStorageKeysCount := 0
for storageIt.Next() {
snapshotStorageKeysCount++
}
storageTrieLeavesCount := 0
// check storage trie and storage snapshot consistency
trie.AssertTrieConsistency(t, acc.Root, serverTrieDB, clientTrieDB, func(key, val []byte) error {
storageTrieLeavesCount++
snapshotVal := rawdb.ReadStorageSnapshot(clientDB, accHash, common.BytesToHash(key))
assert.Equal(t, val, snapshotVal)
return nil
})
assert.Equal(t, storageTrieLeavesCount, snapshotStorageKeysCount)
return nil
})
// Check that the number of accounts in the snapshot matches the number of leaves in the accounts trie
assert.Equal(t, trieAccountLeaves, numSnapshotAccounts)
}
func fillAccountsWithStorage(t *testing.T, serverDB ethdb.Database, serverTrieDB *trie.Database, root common.Hash, numAccounts int) common.Hash {
newRoot, _ := trie.FillAccounts(t, serverTrieDB, root, numAccounts, func(t *testing.T, index int, account types.StateAccount) types.StateAccount {
codeBytes := make([]byte, 256)
_, err := rand.Read(codeBytes)
if err != nil {
t.Fatalf("error reading random code bytes: %v", err)
}
codeHash := crypto.Keccak256Hash(codeBytes)
rawdb.WriteCode(serverDB, codeHash, codeBytes)
account.CodeHash = codeHash[:]
// now create state trie
numKeys := 16
account.Root, _, _ = trie.GenerateTrie(t, serverTrieDB, numKeys, common.HashLength)
return account
})
return newRoot
}
// FillAccountsWithOverlappingStorage adds [numAccounts] randomly generated accounts to the secure trie at [root]
// and commits it to [trieDB]. For each 3 accounts created:
// - One does not have a storage trie,
// - One has a storage trie shared with other accounts (total number of shared storage tries [numOverlappingStorageRoots]),
// - One has a uniquely generated storage trie,
// returns the new trie root and a map of funded keys to StateAccount structs.
func FillAccountsWithOverlappingStorage(
t *testing.T, trieDB *trie.Database, root common.Hash, numAccounts int, numOverlappingStorageRoots int,
) (common.Hash, map[*keystore.Key]*types.StateAccount) {
storageRoots := make([]common.Hash, 0, numOverlappingStorageRoots)
for i := 0; i < numOverlappingStorageRoots; i++ {
storageRoot, _, _ := trie.GenerateTrie(t, trieDB, 16, common.HashLength)
storageRoots = append(storageRoots, storageRoot)
}
storageRootIndex := 0
return trie.FillAccounts(t, trieDB, root, numAccounts, func(t *testing.T, i int, account types.StateAccount) types.StateAccount {
switch i % 3 {
case 0: // unmodified account
case 1: // account with overlapping storage root
account.Root = storageRoots[storageRootIndex%numOverlappingStorageRoots]
storageRootIndex++
case 2: // account with unique storage root
account.Root, _, _ = trie.GenerateTrie(t, trieDB, 16, common.HashLength)
}
return account
})
}