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