github.com/theQRL/go-zond@v0.1.1/core/state/statedb_fuzz_test.go (about) 1 // Copyright 2023 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/> 16 17 package state 18 19 import ( 20 "bytes" 21 "encoding/binary" 22 "errors" 23 "fmt" 24 "math" 25 "math/big" 26 "math/rand" 27 "reflect" 28 "strings" 29 "testing" 30 "testing/quick" 31 32 "github.com/theQRL/go-zond/common" 33 "github.com/theQRL/go-zond/core/rawdb" 34 "github.com/theQRL/go-zond/core/state/snapshot" 35 "github.com/theQRL/go-zond/core/types" 36 "github.com/theQRL/go-zond/crypto" 37 "github.com/theQRL/go-zond/rlp" 38 "github.com/theQRL/go-zond/trie" 39 "github.com/theQRL/go-zond/trie/triedb/pathdb" 40 "github.com/theQRL/go-zond/trie/triestate" 41 ) 42 43 // A stateTest checks that the state changes are correctly captured. Instances 44 // of this test with pseudorandom content are created by Generate. 45 // 46 // The test works as follows: 47 // 48 // A list of states are created by applying actions. The state changes between 49 // each state instance are tracked and be verified. 50 type stateTest struct { 51 addrs []common.Address // all account addresses 52 actions [][]testAction // modifications to the state, grouped by block 53 chunk int // The number of actions per chunk 54 err error // failure details are reported through this field 55 } 56 57 // newStateTestAction creates a random action that changes state. 58 func newStateTestAction(addr common.Address, r *rand.Rand, index int) testAction { 59 actions := []testAction{ 60 { 61 name: "SetBalance", 62 fn: func(a testAction, s *StateDB) { 63 s.SetBalance(addr, big.NewInt(a.args[0])) 64 }, 65 args: make([]int64, 1), 66 }, 67 { 68 name: "SetNonce", 69 fn: func(a testAction, s *StateDB) { 70 s.SetNonce(addr, uint64(a.args[0])) 71 }, 72 args: make([]int64, 1), 73 }, 74 { 75 name: "SetState", 76 fn: func(a testAction, s *StateDB) { 77 var key, val common.Hash 78 binary.BigEndian.PutUint16(key[:], uint16(a.args[0])) 79 binary.BigEndian.PutUint16(val[:], uint16(a.args[1])) 80 s.SetState(addr, key, val) 81 }, 82 args: make([]int64, 2), 83 }, 84 { 85 name: "SetCode", 86 fn: func(a testAction, s *StateDB) { 87 code := make([]byte, 16) 88 binary.BigEndian.PutUint64(code, uint64(a.args[0])) 89 binary.BigEndian.PutUint64(code[8:], uint64(a.args[1])) 90 s.SetCode(addr, code) 91 }, 92 args: make([]int64, 2), 93 }, 94 { 95 name: "CreateAccount", 96 fn: func(a testAction, s *StateDB) { 97 s.CreateAccount(addr) 98 }, 99 }, 100 { 101 name: "Selfdestruct", 102 fn: func(a testAction, s *StateDB) { 103 s.SelfDestruct(addr) 104 }, 105 }, 106 } 107 var nonRandom = index != -1 108 if index == -1 { 109 index = r.Intn(len(actions)) 110 } 111 action := actions[index] 112 var names []string 113 if !action.noAddr { 114 names = append(names, addr.Hex()) 115 } 116 for i := range action.args { 117 if nonRandom { 118 action.args[i] = rand.Int63n(10000) + 1 // set balance to non-zero 119 } else { 120 action.args[i] = rand.Int63n(10000) 121 } 122 names = append(names, fmt.Sprint(action.args[i])) 123 } 124 action.name += " " + strings.Join(names, ", ") 125 return action 126 } 127 128 // Generate returns a new snapshot test of the given size. All randomness is 129 // derived from r. 130 func (*stateTest) Generate(r *rand.Rand, size int) reflect.Value { 131 addrs := make([]common.Address, 5) 132 for i := range addrs { 133 addrs[i][0] = byte(i) 134 } 135 actions := make([][]testAction, rand.Intn(5)+1) 136 137 for i := 0; i < len(actions); i++ { 138 actions[i] = make([]testAction, size) 139 for j := range actions[i] { 140 if j == 0 { 141 // Always include a set balance action to make sure 142 // the state changes are not empty. 143 actions[i][j] = newStateTestAction(common.HexToAddress("0xdeadbeef"), r, 0) 144 continue 145 } 146 actions[i][j] = newStateTestAction(addrs[r.Intn(len(addrs))], r, -1) 147 } 148 } 149 chunk := int(math.Sqrt(float64(size))) 150 if size > 0 && chunk == 0 { 151 chunk = 1 152 } 153 return reflect.ValueOf(&stateTest{ 154 addrs: addrs, 155 actions: actions, 156 chunk: chunk, 157 }) 158 } 159 160 func (test *stateTest) String() string { 161 out := new(bytes.Buffer) 162 for i, actions := range test.actions { 163 fmt.Fprintf(out, "---- block %d ----\n", i) 164 for j, action := range actions { 165 if j%test.chunk == 0 { 166 fmt.Fprintf(out, "---- transaction %d ----\n", j/test.chunk) 167 } 168 fmt.Fprintf(out, "%4d: %s\n", j%test.chunk, action.name) 169 } 170 } 171 return out.String() 172 } 173 174 func (test *stateTest) run() bool { 175 var ( 176 roots []common.Hash 177 accountList []map[common.Address][]byte 178 storageList []map[common.Address]map[common.Hash][]byte 179 onCommit = func(states *triestate.Set) { 180 accountList = append(accountList, copySet(states.Accounts)) 181 storageList = append(storageList, copy2DSet(states.Storages)) 182 } 183 disk = rawdb.NewMemoryDatabase() 184 tdb = trie.NewDatabase(disk, &trie.Config{PathDB: pathdb.Defaults}) 185 sdb = NewDatabaseWithNodeDB(disk, tdb) 186 byzantium = rand.Intn(2) == 0 187 ) 188 defer disk.Close() 189 defer tdb.Close() 190 191 var snaps *snapshot.Tree 192 if rand.Intn(3) == 0 { 193 snaps, _ = snapshot.New(snapshot.Config{ 194 CacheSize: 1, 195 Recovery: false, 196 NoBuild: false, 197 AsyncBuild: false, 198 }, disk, tdb, types.EmptyRootHash) 199 } 200 for i, actions := range test.actions { 201 root := types.EmptyRootHash 202 if i != 0 { 203 root = roots[len(roots)-1] 204 } 205 state, err := New(root, sdb, snaps) 206 if err != nil { 207 panic(err) 208 } 209 state.onCommit = onCommit 210 211 for i, action := range actions { 212 if i%test.chunk == 0 && i != 0 { 213 if byzantium { 214 state.Finalise(true) // call finalise at the transaction boundary 215 } else { 216 state.IntermediateRoot(true) // call intermediateRoot at the transaction boundary 217 } 218 } 219 action.fn(action, state) 220 } 221 if byzantium { 222 state.Finalise(true) // call finalise at the transaction boundary 223 } else { 224 state.IntermediateRoot(true) // call intermediateRoot at the transaction boundary 225 } 226 nroot, err := state.Commit(0, true) // call commit at the block boundary 227 if err != nil { 228 panic(err) 229 } 230 if nroot == root { 231 return true // filter out non-change state transition 232 } 233 roots = append(roots, nroot) 234 } 235 for i := 0; i < len(test.actions); i++ { 236 root := types.EmptyRootHash 237 if i != 0 { 238 root = roots[i-1] 239 } 240 test.err = test.verify(root, roots[i], tdb, accountList[i], storageList[i]) 241 if test.err != nil { 242 return false 243 } 244 } 245 return true 246 } 247 248 // verifyAccountCreation this function is called once the state diff says that 249 // specific account was not present. A serial of checks will be performed to 250 // ensure the state diff is correct, includes: 251 // 252 // - the account was indeed not present in trie 253 // - the account is present in new trie, nil->nil is regarded as invalid 254 // - the slots transition is correct 255 func (test *stateTest) verifyAccountCreation(next common.Hash, db *trie.Database, otr, ntr *trie.Trie, addr common.Address, slots map[common.Hash][]byte) error { 256 // Verify account change 257 addrHash := crypto.Keccak256Hash(addr.Bytes()) 258 oBlob, err := otr.Get(addrHash.Bytes()) 259 if err != nil { 260 return err 261 } 262 nBlob, err := ntr.Get(addrHash.Bytes()) 263 if err != nil { 264 return err 265 } 266 if len(oBlob) != 0 { 267 return fmt.Errorf("unexpected account in old trie, %x", addrHash) 268 } 269 if len(nBlob) == 0 { 270 return fmt.Errorf("missing account in new trie, %x", addrHash) 271 } 272 273 // Verify storage changes 274 var nAcct types.StateAccount 275 if err := rlp.DecodeBytes(nBlob, &nAcct); err != nil { 276 return err 277 } 278 // Account has no slot, empty slot set is expected 279 if nAcct.Root == types.EmptyRootHash { 280 if len(slots) != 0 { 281 return fmt.Errorf("unexpected slot changes %x", addrHash) 282 } 283 return nil 284 } 285 // Account has slots, ensure all new slots are contained 286 st, err := trie.New(trie.StorageTrieID(next, addrHash, nAcct.Root), db) 287 if err != nil { 288 return err 289 } 290 for key, val := range slots { 291 st.Update(key.Bytes(), val) 292 } 293 if st.Hash() != types.EmptyRootHash { 294 return errors.New("invalid slot changes") 295 } 296 return nil 297 } 298 299 // verifyAccountUpdate this function is called once the state diff says that 300 // specific account was present. A serial of checks will be performed to 301 // ensure the state diff is correct, includes: 302 // 303 // - the account was indeed present in trie 304 // - the account in old trie matches the provided value 305 // - the slots transition is correct 306 func (test *stateTest) verifyAccountUpdate(next common.Hash, db *trie.Database, otr, ntr *trie.Trie, addr common.Address, origin []byte, slots map[common.Hash][]byte) error { 307 // Verify account change 308 addrHash := crypto.Keccak256Hash(addr.Bytes()) 309 oBlob, err := otr.Get(addrHash.Bytes()) 310 if err != nil { 311 return err 312 } 313 nBlob, err := ntr.Get(addrHash.Bytes()) 314 if err != nil { 315 return err 316 } 317 if len(oBlob) == 0 { 318 return fmt.Errorf("missing account in old trie, %x", addrHash) 319 } 320 full, err := types.FullAccountRLP(origin) 321 if err != nil { 322 return err 323 } 324 if !bytes.Equal(full, oBlob) { 325 return fmt.Errorf("account value is not matched, %x", addrHash) 326 } 327 328 // Decode accounts 329 var ( 330 oAcct types.StateAccount 331 nAcct types.StateAccount 332 nRoot common.Hash 333 ) 334 if err := rlp.DecodeBytes(oBlob, &oAcct); err != nil { 335 return err 336 } 337 if len(nBlob) == 0 { 338 nRoot = types.EmptyRootHash 339 } else { 340 if err := rlp.DecodeBytes(nBlob, &nAcct); err != nil { 341 return err 342 } 343 nRoot = nAcct.Root 344 } 345 346 // Verify storage 347 st, err := trie.New(trie.StorageTrieID(next, addrHash, nRoot), db) 348 if err != nil { 349 return err 350 } 351 for key, val := range slots { 352 st.Update(key.Bytes(), val) 353 } 354 if st.Hash() != oAcct.Root { 355 return errors.New("invalid slot changes") 356 } 357 return nil 358 } 359 360 func (test *stateTest) verify(root common.Hash, next common.Hash, db *trie.Database, accountsOrigin map[common.Address][]byte, storagesOrigin map[common.Address]map[common.Hash][]byte) error { 361 otr, err := trie.New(trie.StateTrieID(root), db) 362 if err != nil { 363 return err 364 } 365 ntr, err := trie.New(trie.StateTrieID(next), db) 366 if err != nil { 367 return err 368 } 369 for addr, account := range accountsOrigin { 370 var err error 371 if len(account) == 0 { 372 err = test.verifyAccountCreation(next, db, otr, ntr, addr, storagesOrigin[addr]) 373 } else { 374 err = test.verifyAccountUpdate(next, db, otr, ntr, addr, accountsOrigin[addr], storagesOrigin[addr]) 375 } 376 if err != nil { 377 return err 378 } 379 } 380 return nil 381 } 382 383 func TestStateChanges(t *testing.T) { 384 config := &quick.Config{MaxCount: 1000} 385 err := quick.Check((*stateTest).run, config) 386 if cerr, ok := err.(*quick.CheckError); ok { 387 test := cerr.In[0].(*stateTest) 388 t.Errorf("%v:\n%s", test.err, test) 389 } else if err != nil { 390 t.Error(err) 391 } 392 }