github.com/MetalBlockchain/subnet-evm@v0.4.9/trie/test_trie.go (about) 1 // (c) 2021-2022, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package trie 5 6 import ( 7 cryptoRand "crypto/rand" 8 "encoding/binary" 9 "math/big" 10 "math/rand" 11 "testing" 12 13 "github.com/MetalBlockchain/metalgo/utils/wrappers" 14 "github.com/MetalBlockchain/subnet-evm/accounts/keystore" 15 "github.com/MetalBlockchain/subnet-evm/core/types" 16 17 "github.com/ethereum/go-ethereum/common" 18 "github.com/ethereum/go-ethereum/rlp" 19 "github.com/stretchr/testify/assert" 20 ) 21 22 // GenerateTrie creates a trie with [numKeys] key-value pairs inside of [trieDB]. 23 // Returns the root of the generated trie, the slice of keys inserted into the trie in lexicographical 24 // order, and the slice of corresponding values. 25 // GenerateTrie reads from [rand] and the caller should call rand.Seed(n) for deterministic results 26 func GenerateTrie(t *testing.T, trieDB *Database, numKeys int, keySize int) (common.Hash, [][]byte, [][]byte) { 27 if keySize < wrappers.LongLen+1 { 28 t.Fatal("key size must be at least 9 bytes (8 bytes for uint64 and 1 random byte)") 29 } 30 testTrie := NewEmpty(trieDB) 31 32 keys, values := FillTrie(t, numKeys, keySize, testTrie) 33 34 // Commit the root to [trieDB] 35 root, nodes, err := testTrie.Commit(false) 36 assert.NoError(t, err) 37 err = trieDB.Update(NewWithNodeSet(nodes)) 38 assert.NoError(t, err) 39 err = trieDB.Commit(root, false, nil) 40 assert.NoError(t, err) 41 42 return root, keys, values 43 } 44 45 // FillTrie fills a given trie with [numKeys] number of keys, each of size [keySize] 46 // returns inserted keys and values 47 // FillTrie reads from [rand] and the caller should call rand.Seed(n) for deterministic results 48 func FillTrie(t *testing.T, numKeys int, keySize int, testTrie *Trie) ([][]byte, [][]byte) { 49 keys := make([][]byte, 0, numKeys) 50 values := make([][]byte, 0, numKeys) 51 52 // Generate key-value pairs 53 for i := 0; i < numKeys; i++ { 54 key := make([]byte, keySize) 55 binary.BigEndian.PutUint64(key[:wrappers.LongLen], uint64(i+1)) 56 _, err := rand.Read(key[wrappers.LongLen:]) 57 assert.NoError(t, err) 58 59 value := make([]byte, rand.Intn(128)+128) // min 128 bytes, max 256 bytes 60 _, err = rand.Read(value) 61 assert.NoError(t, err) 62 63 if err = testTrie.TryUpdate(key, value); err != nil { 64 t.Fatal("error updating trie", err) 65 } 66 67 keys = append(keys, key) 68 values = append(values, value) 69 } 70 return keys, values 71 } 72 73 // AssertTrieConsistency ensures given trieDB [a] and [b] both have the same 74 // non-empty trie at [root]. (all key/value pairs must be equal) 75 func AssertTrieConsistency(t testing.TB, root common.Hash, a, b *Database, onLeaf func(key, val []byte) error) { 76 trieA, err := New(common.Hash{}, root, a) 77 if err != nil { 78 t.Fatalf("error creating trieA, root=%s, err=%v", root, err) 79 } 80 trieB, err := New(common.Hash{}, root, b) 81 if err != nil { 82 t.Fatalf("error creating trieB, root=%s, err=%v", root, err) 83 } 84 85 itA := NewIterator(trieA.NodeIterator(nil)) 86 itB := NewIterator(trieB.NodeIterator(nil)) 87 count := 0 88 for itA.Next() && itB.Next() { 89 count++ 90 assert.Equal(t, itA.Key, itB.Key) 91 assert.Equal(t, itA.Value, itB.Value) 92 if onLeaf != nil { 93 if err := onLeaf(itA.Key, itA.Value); err != nil { 94 t.Fatalf("error in onLeaf callback: %v", err) 95 } 96 } 97 } 98 assert.NoError(t, itA.Err) 99 assert.NoError(t, itB.Err) 100 assert.False(t, itA.Next()) 101 assert.False(t, itB.Next()) 102 assert.Greater(t, count, 0) 103 } 104 105 // CorruptTrie deletes every [n]th trie node from the trie given by [root] from the trieDB. 106 // Assumes that the trie given by root can be iterated without issue. 107 func CorruptTrie(t *testing.T, trieDB *Database, root common.Hash, n int) { 108 batch := trieDB.DiskDB().NewBatch() 109 // next delete some trie nodes 110 tr, err := New(common.Hash{}, root, trieDB) 111 if err != nil { 112 t.Fatal(err) 113 } 114 115 nodeIt := tr.NodeIterator(nil) 116 count := 0 117 for nodeIt.Next(true) { 118 count++ 119 if count%n == 0 && nodeIt.Hash() != (common.Hash{}) { 120 if err := batch.Delete(nodeIt.Hash().Bytes()); err != nil { 121 t.Fatal(err) 122 } 123 } 124 } 125 if err := nodeIt.Error(); err != nil { 126 t.Fatal(err) 127 } 128 129 if err := batch.Write(); err != nil { 130 t.Fatal(err) 131 } 132 } 133 134 // FillAccounts adds [numAccounts] randomly generated accounts to the secure trie at [root] and commits it to [trieDB]. 135 // [onAccount] is called if non-nil (so the caller can modify the account before it is stored in the secure trie). 136 // returns the new trie root and a map of funded keys to StateAccount structs. 137 func FillAccounts( 138 t *testing.T, trieDB *Database, root common.Hash, numAccounts int, 139 onAccount func(*testing.T, int, types.StateAccount) types.StateAccount, 140 ) (common.Hash, map[*keystore.Key]*types.StateAccount) { 141 var ( 142 minBalance = big.NewInt(3000000000000000000) 143 randBalance = big.NewInt(1000000000000000000) 144 maxNonce = 10 145 accounts = make(map[*keystore.Key]*types.StateAccount, numAccounts) 146 ) 147 148 tr, err := NewStateTrie(common.Hash{}, root, trieDB) 149 if err != nil { 150 t.Fatalf("error opening trie: %v", err) 151 } 152 153 for i := 0; i < numAccounts; i++ { 154 acc := types.StateAccount{ 155 Nonce: uint64(rand.Intn(maxNonce)), 156 Balance: new(big.Int).Add(minBalance, randBalance), 157 CodeHash: types.EmptyCodeHash[:], 158 Root: types.EmptyRootHash, 159 } 160 if onAccount != nil { 161 acc = onAccount(t, i, acc) 162 } 163 164 accBytes, err := rlp.EncodeToBytes(&acc) 165 if err != nil { 166 t.Fatalf("failed to rlp encode account: %v", err) 167 } 168 169 key, err := keystore.NewKey(cryptoRand.Reader) 170 if err != nil { 171 t.Fatal(err) 172 } 173 if err = tr.TryUpdate(key.Address[:], accBytes); err != nil { 174 t.Fatalf("error updating trie with account, address=%s, err=%v", key.Address, err) 175 } 176 accounts[key] = &acc 177 } 178 179 newRoot, nodes, err := tr.Commit(false) 180 if err != nil { 181 t.Fatalf("error committing trie: %v", err) 182 } 183 if err := trieDB.Update(NewWithNodeSet(nodes)); err != nil { 184 t.Fatalf("error updating trieDB: %v", err) 185 } 186 if err := trieDB.Commit(newRoot, false, nil); err != nil { 187 t.Fatalf("error committing trieDB: %v", err) 188 } 189 return newRoot, accounts 190 }