github.com/theQRL/go-zond@v0.2.1/core/state/statedb_test.go (about) 1 // Copyright 2016 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 "sync" 30 "testing" 31 "testing/quick" 32 33 "github.com/holiman/uint256" 34 "github.com/theQRL/go-zond/common" 35 "github.com/theQRL/go-zond/core/rawdb" 36 "github.com/theQRL/go-zond/core/state/snapshot" 37 "github.com/theQRL/go-zond/core/types" 38 "github.com/theQRL/go-zond/crypto" 39 "github.com/theQRL/go-zond/rlp" 40 "github.com/theQRL/go-zond/trie" 41 "github.com/theQRL/go-zond/trie/triedb/hashdb" 42 "github.com/theQRL/go-zond/trie/triedb/pathdb" 43 "github.com/theQRL/go-zond/trie/trienode" 44 ) 45 46 // Tests that updating a state trie does not leak any database writes prior to 47 // actually committing the state. 48 func TestUpdateLeaks(t *testing.T) { 49 // Create an empty state database 50 var ( 51 db = rawdb.NewMemoryDatabase() 52 tdb = trie.NewDatabase(db, nil) 53 ) 54 state, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(db, tdb), nil) 55 56 // Update it with some accounts 57 for i := byte(0); i < 255; i++ { 58 addr := common.BytesToAddress([]byte{i}) 59 state.AddBalance(addr, big.NewInt(int64(11*i))) 60 state.SetNonce(addr, uint64(42*i)) 61 if i%2 == 0 { 62 state.SetState(addr, common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i})) 63 } 64 if i%3 == 0 { 65 state.SetCode(addr, []byte{i, i, i, i, i}) 66 } 67 } 68 69 root := state.IntermediateRoot(false) 70 if err := tdb.Commit(root, false); err != nil { 71 t.Errorf("can not commit trie %v to persistent database", root.Hex()) 72 } 73 74 // Ensure that no data was leaked into the database 75 it := db.NewIterator(nil, nil) 76 for it.Next() { 77 t.Errorf("State leaked into database: %x -> %x", it.Key(), it.Value()) 78 } 79 it.Release() 80 } 81 82 // Tests that no intermediate state of an object is stored into the database, 83 // only the one right before the commit. 84 func TestIntermediateLeaks(t *testing.T) { 85 // Create two state databases, one transitioning to the final state, the other final from the beginning 86 transDb := rawdb.NewMemoryDatabase() 87 finalDb := rawdb.NewMemoryDatabase() 88 transNdb := trie.NewDatabase(transDb, nil) 89 finalNdb := trie.NewDatabase(finalDb, nil) 90 transState, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(transDb, transNdb), nil) 91 finalState, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(finalDb, finalNdb), nil) 92 93 modify := func(state *StateDB, addr common.Address, i, tweak byte) { 94 state.SetBalance(addr, big.NewInt(int64(11*i)+int64(tweak))) 95 state.SetNonce(addr, uint64(42*i+tweak)) 96 if i%2 == 0 { 97 state.SetState(addr, common.Hash{i, i, i, 0}, common.Hash{}) 98 state.SetState(addr, common.Hash{i, i, i, tweak}, common.Hash{i, i, i, i, tweak}) 99 } 100 if i%3 == 0 { 101 state.SetCode(addr, []byte{i, i, i, i, i, tweak}) 102 } 103 } 104 105 // Modify the transient state. 106 for i := byte(0); i < 255; i++ { 107 modify(transState, common.Address{i}, i, 0) 108 } 109 // Write modifications to trie. 110 transState.IntermediateRoot(false) 111 112 // Overwrite all the data with new values in the transient database. 113 for i := byte(0); i < 255; i++ { 114 modify(transState, common.Address{i}, i, 99) 115 modify(finalState, common.Address{i}, i, 99) 116 } 117 118 // Commit and cross check the databases. 119 transRoot, err := transState.Commit(0, false) 120 if err != nil { 121 t.Fatalf("failed to commit transition state: %v", err) 122 } 123 if err = transNdb.Commit(transRoot, false); err != nil { 124 t.Errorf("can not commit trie %v to persistent database", transRoot.Hex()) 125 } 126 127 finalRoot, err := finalState.Commit(0, false) 128 if err != nil { 129 t.Fatalf("failed to commit final state: %v", err) 130 } 131 if err = finalNdb.Commit(finalRoot, false); err != nil { 132 t.Errorf("can not commit trie %v to persistent database", finalRoot.Hex()) 133 } 134 135 it := finalDb.NewIterator(nil, nil) 136 for it.Next() { 137 key, fvalue := it.Key(), it.Value() 138 tvalue, err := transDb.Get(key) 139 if err != nil { 140 t.Errorf("entry missing from the transition database: %x -> %x", key, fvalue) 141 } 142 if !bytes.Equal(fvalue, tvalue) { 143 t.Errorf("value mismatch at key %x: %x in transition database, %x in final database", key, tvalue, fvalue) 144 } 145 } 146 it.Release() 147 148 it = transDb.NewIterator(nil, nil) 149 for it.Next() { 150 key, tvalue := it.Key(), it.Value() 151 fvalue, err := finalDb.Get(key) 152 if err != nil { 153 t.Errorf("extra entry in the transition database: %x -> %x", key, it.Value()) 154 } 155 if !bytes.Equal(fvalue, tvalue) { 156 t.Errorf("value mismatch at key %x: %x in transition database, %x in final database", key, tvalue, fvalue) 157 } 158 } 159 } 160 161 // TestCopy tests that copying a StateDB object indeed makes the original and 162 // the copy independent of each other. This test is a regression test against 163 // https://github.com/theQRL/go-zond/pull/15549. 164 func TestCopy(t *testing.T) { 165 // Create a random state test to copy and modify "independently" 166 orig, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil) 167 168 for i := byte(0); i < 255; i++ { 169 obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) 170 obj.AddBalance(big.NewInt(int64(i))) 171 orig.updateStateObject(obj) 172 } 173 orig.Finalise(false) 174 175 // Copy the state 176 copy := orig.Copy() 177 178 // Copy the copy state 179 ccopy := copy.Copy() 180 181 // modify all in memory 182 for i := byte(0); i < 255; i++ { 183 origObj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) 184 copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) 185 ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) 186 187 origObj.AddBalance(big.NewInt(2 * int64(i))) 188 copyObj.AddBalance(big.NewInt(3 * int64(i))) 189 ccopyObj.AddBalance(big.NewInt(4 * int64(i))) 190 191 orig.updateStateObject(origObj) 192 copy.updateStateObject(copyObj) 193 ccopy.updateStateObject(copyObj) 194 } 195 196 // Finalise the changes on all concurrently 197 finalise := func(wg *sync.WaitGroup, db *StateDB) { 198 defer wg.Done() 199 db.Finalise(true) 200 } 201 202 var wg sync.WaitGroup 203 wg.Add(3) 204 go finalise(&wg, orig) 205 go finalise(&wg, copy) 206 go finalise(&wg, ccopy) 207 wg.Wait() 208 209 // Verify that the three states have been updated independently 210 for i := byte(0); i < 255; i++ { 211 origObj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) 212 copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) 213 ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) 214 215 if want := big.NewInt(3 * int64(i)); origObj.Balance().Cmp(want) != 0 { 216 t.Errorf("orig obj %d: balance mismatch: have %v, want %v", i, origObj.Balance(), want) 217 } 218 if want := big.NewInt(4 * int64(i)); copyObj.Balance().Cmp(want) != 0 { 219 t.Errorf("copy obj %d: balance mismatch: have %v, want %v", i, copyObj.Balance(), want) 220 } 221 if want := big.NewInt(5 * int64(i)); ccopyObj.Balance().Cmp(want) != 0 { 222 t.Errorf("copy obj %d: balance mismatch: have %v, want %v", i, ccopyObj.Balance(), want) 223 } 224 } 225 } 226 227 func TestSnapshotRandom(t *testing.T) { 228 config := &quick.Config{MaxCount: 1000} 229 err := quick.Check((*snapshotTest).run, config) 230 if cerr, ok := err.(*quick.CheckError); ok { 231 test := cerr.In[0].(*snapshotTest) 232 t.Errorf("%v:\n%s", test.err, test) 233 } else if err != nil { 234 t.Error(err) 235 } 236 } 237 238 // A snapshotTest checks that reverting StateDB snapshots properly undoes all changes 239 // captured by the snapshot. Instances of this test with pseudorandom content are created 240 // by Generate. 241 // 242 // The test works as follows: 243 // 244 // A new state is created and all actions are applied to it. Several snapshots are taken 245 // in between actions. The test then reverts each snapshot. For each snapshot the actions 246 // leading up to it are replayed on a fresh, empty state. The behaviour of all public 247 // accessor methods on the reverted state must match the return value of the equivalent 248 // methods on the replayed state. 249 type snapshotTest struct { 250 addrs []common.Address // all account addresses 251 actions []testAction // modifications to the state 252 snapshots []int // actions indexes at which snapshot is taken 253 err error // failure details are reported through this field 254 } 255 256 type testAction struct { 257 name string 258 fn func(testAction, *StateDB) 259 args []int64 260 noAddr bool 261 } 262 263 // newTestAction creates a random action that changes state. 264 func newTestAction(addr common.Address, r *rand.Rand) testAction { 265 actions := []testAction{ 266 { 267 name: "SetBalance", 268 fn: func(a testAction, s *StateDB) { 269 s.SetBalance(addr, big.NewInt(a.args[0])) 270 }, 271 args: make([]int64, 1), 272 }, 273 { 274 name: "AddBalance", 275 fn: func(a testAction, s *StateDB) { 276 s.AddBalance(addr, big.NewInt(a.args[0])) 277 }, 278 args: make([]int64, 1), 279 }, 280 { 281 name: "SetNonce", 282 fn: func(a testAction, s *StateDB) { 283 s.SetNonce(addr, uint64(a.args[0])) 284 }, 285 args: make([]int64, 1), 286 }, 287 { 288 name: "SetState", 289 fn: func(a testAction, s *StateDB) { 290 var key, val common.Hash 291 binary.BigEndian.PutUint16(key[:], uint16(a.args[0])) 292 binary.BigEndian.PutUint16(val[:], uint16(a.args[1])) 293 s.SetState(addr, key, val) 294 }, 295 args: make([]int64, 2), 296 }, 297 { 298 name: "SetCode", 299 fn: func(a testAction, s *StateDB) { 300 code := make([]byte, 16) 301 binary.BigEndian.PutUint64(code, uint64(a.args[0])) 302 binary.BigEndian.PutUint64(code[8:], uint64(a.args[1])) 303 s.SetCode(addr, code) 304 }, 305 args: make([]int64, 2), 306 }, 307 { 308 name: "CreateAccount", 309 fn: func(a testAction, s *StateDB) { 310 s.CreateAccount(addr) 311 }, 312 }, 313 { 314 name: "AddRefund", 315 fn: func(a testAction, s *StateDB) { 316 s.AddRefund(uint64(a.args[0])) 317 }, 318 args: make([]int64, 1), 319 noAddr: true, 320 }, 321 { 322 name: "AddLog", 323 fn: func(a testAction, s *StateDB) { 324 data := make([]byte, 2) 325 binary.BigEndian.PutUint16(data, uint16(a.args[0])) 326 s.AddLog(&types.Log{Address: addr, Data: data}) 327 }, 328 args: make([]int64, 1), 329 }, 330 { 331 name: "AddPreimage", 332 fn: func(a testAction, s *StateDB) { 333 preimage := []byte{1} 334 hash := common.BytesToHash(preimage) 335 s.AddPreimage(hash, preimage) 336 }, 337 args: make([]int64, 1), 338 }, 339 { 340 name: "AddAddressToAccessList", 341 fn: func(a testAction, s *StateDB) { 342 s.AddAddressToAccessList(addr) 343 }, 344 }, 345 { 346 name: "AddSlotToAccessList", 347 fn: func(a testAction, s *StateDB) { 348 s.AddSlotToAccessList(addr, 349 common.Hash{byte(a.args[0])}) 350 }, 351 args: make([]int64, 1), 352 }, 353 } 354 action := actions[r.Intn(len(actions))] 355 var nameargs []string 356 if !action.noAddr { 357 nameargs = append(nameargs, addr.Hex()) 358 } 359 for i := range action.args { 360 action.args[i] = rand.Int63n(100) 361 nameargs = append(nameargs, fmt.Sprint(action.args[i])) 362 } 363 action.name += strings.Join(nameargs, ", ") 364 return action 365 } 366 367 // Generate returns a new snapshot test of the given size. All randomness is 368 // derived from r. 369 func (*snapshotTest) Generate(r *rand.Rand, size int) reflect.Value { 370 // Generate random actions. 371 addrs := make([]common.Address, 50) 372 for i := range addrs { 373 addrs[i][0] = byte(i) 374 } 375 actions := make([]testAction, size) 376 for i := range actions { 377 addr := addrs[r.Intn(len(addrs))] 378 actions[i] = newTestAction(addr, r) 379 } 380 // Generate snapshot indexes. 381 nsnapshots := int(math.Sqrt(float64(size))) 382 if size > 0 && nsnapshots == 0 { 383 nsnapshots = 1 384 } 385 snapshots := make([]int, nsnapshots) 386 snaplen := len(actions) / nsnapshots 387 for i := range snapshots { 388 // Try to place the snapshots some number of actions apart from each other. 389 snapshots[i] = (i * snaplen) + r.Intn(snaplen) 390 } 391 return reflect.ValueOf(&snapshotTest{addrs, actions, snapshots, nil}) 392 } 393 394 func (test *snapshotTest) String() string { 395 out := new(bytes.Buffer) 396 sindex := 0 397 for i, action := range test.actions { 398 if len(test.snapshots) > sindex && i == test.snapshots[sindex] { 399 fmt.Fprintf(out, "---- snapshot %d ----\n", sindex) 400 sindex++ 401 } 402 fmt.Fprintf(out, "%4d: %s\n", i, action.name) 403 } 404 return out.String() 405 } 406 407 func (test *snapshotTest) run() bool { 408 // Run all actions and create snapshots. 409 var ( 410 state, _ = New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil) 411 snapshotRevs = make([]int, len(test.snapshots)) 412 sindex = 0 413 ) 414 for i, action := range test.actions { 415 if len(test.snapshots) > sindex && i == test.snapshots[sindex] { 416 snapshotRevs[sindex] = state.Snapshot() 417 sindex++ 418 } 419 action.fn(action, state) 420 } 421 // Revert all snapshots in reverse order. Each revert must yield a state 422 // that is equivalent to fresh state with all actions up the snapshot applied. 423 for sindex--; sindex >= 0; sindex-- { 424 checkstate, _ := New(types.EmptyRootHash, state.Database(), nil) 425 for _, action := range test.actions[:test.snapshots[sindex]] { 426 action.fn(action, checkstate) 427 } 428 state.RevertToSnapshot(snapshotRevs[sindex]) 429 if err := test.checkEqual(state, checkstate); err != nil { 430 test.err = fmt.Errorf("state mismatch after revert to snapshot %d\n%v", sindex, err) 431 return false 432 } 433 } 434 return true 435 } 436 437 func forEachStorage(s *StateDB, addr common.Address, cb func(key, value common.Hash) bool) error { 438 so := s.getStateObject(addr) 439 if so == nil { 440 return nil 441 } 442 tr, err := so.getTrie() 443 if err != nil { 444 return err 445 } 446 trieIt, err := tr.NodeIterator(nil) 447 if err != nil { 448 return err 449 } 450 it := trie.NewIterator(trieIt) 451 452 for it.Next() { 453 key := common.BytesToHash(s.trie.GetKey(it.Key)) 454 if value, dirty := so.dirtyStorage[key]; dirty { 455 if !cb(key, value) { 456 return nil 457 } 458 continue 459 } 460 461 if len(it.Value) > 0 { 462 _, content, _, err := rlp.Split(it.Value) 463 if err != nil { 464 return err 465 } 466 if !cb(key, common.BytesToHash(content)) { 467 return nil 468 } 469 } 470 } 471 return nil 472 } 473 474 // checkEqual checks that methods of state and checkstate return the same values. 475 func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error { 476 for _, addr := range test.addrs { 477 var err error 478 checkeq := func(op string, a, b interface{}) bool { 479 if err == nil && !reflect.DeepEqual(a, b) { 480 err = fmt.Errorf("got %s(%s) == %v, want %v", op, addr.Hex(), a, b) 481 return false 482 } 483 return true 484 } 485 // Check basic accessor methods. 486 checkeq("Exist", state.Exist(addr), checkstate.Exist(addr)) 487 checkeq("GetBalance", state.GetBalance(addr), checkstate.GetBalance(addr)) 488 checkeq("GetNonce", state.GetNonce(addr), checkstate.GetNonce(addr)) 489 checkeq("GetCode", state.GetCode(addr), checkstate.GetCode(addr)) 490 checkeq("GetCodeHash", state.GetCodeHash(addr), checkstate.GetCodeHash(addr)) 491 checkeq("GetCodeSize", state.GetCodeSize(addr), checkstate.GetCodeSize(addr)) 492 // Check storage. 493 if obj := state.getStateObject(addr); obj != nil { 494 forEachStorage(state, addr, func(key, value common.Hash) bool { 495 return checkeq("GetState("+key.Hex()+")", checkstate.GetState(addr, key), value) 496 }) 497 forEachStorage(checkstate, addr, func(key, value common.Hash) bool { 498 return checkeq("GetState("+key.Hex()+")", checkstate.GetState(addr, key), value) 499 }) 500 } 501 if err != nil { 502 return err 503 } 504 } 505 506 if state.GetRefund() != checkstate.GetRefund() { 507 return fmt.Errorf("got GetRefund() == %d, want GetRefund() == %d", 508 state.GetRefund(), checkstate.GetRefund()) 509 } 510 if !reflect.DeepEqual(state.GetLogs(common.Hash{}, 0, common.Hash{}), checkstate.GetLogs(common.Hash{}, 0, common.Hash{})) { 511 return fmt.Errorf("got GetLogs(common.Hash{}) == %v, want GetLogs(common.Hash{}) == %v", 512 state.GetLogs(common.Hash{}, 0, common.Hash{}), checkstate.GetLogs(common.Hash{}, 0, common.Hash{})) 513 } 514 return nil 515 } 516 517 func TestTouchDelete(t *testing.T) { 518 s := newStateEnv() 519 s.state.GetOrNewStateObject(common.Address{}) 520 root, _ := s.state.Commit(0, false) 521 s.state, _ = New(root, s.state.db, s.state.snaps) 522 523 snapshot := s.state.Snapshot() 524 s.state.AddBalance(common.Address{}, new(big.Int)) 525 526 if len(s.state.journal.dirties) != 1 { 527 t.Fatal("expected one dirty state object") 528 } 529 s.state.RevertToSnapshot(snapshot) 530 if len(s.state.journal.dirties) != 0 { 531 t.Fatal("expected no dirty state object") 532 } 533 } 534 535 // TestCopyOfCopy tests that modified objects are carried over to the copy, and the copy of the copy. 536 // See https://github.com/theQRL/go-zond/pull/15225#issuecomment-380191512 537 func TestCopyOfCopy(t *testing.T) { 538 state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil) 539 addr, _ := common.NewAddressFromString("Z000000000000000000000000000000000000aaaa") 540 state.SetBalance(addr, big.NewInt(42)) 541 542 if got := state.Copy().GetBalance(addr).Uint64(); got != 42 { 543 t.Fatalf("1st copy fail, expected 42, got %v", got) 544 } 545 if got := state.Copy().Copy().GetBalance(addr).Uint64(); got != 42 { 546 t.Fatalf("2nd copy fail, expected 42, got %v", got) 547 } 548 } 549 550 // Tests a regression where committing a copy lost some internal meta information, 551 // leading to corrupted subsequent copies. 552 // 553 // See https://github.com/theQRL/go-zond/issues/20106. 554 func TestCopyCommitCopy(t *testing.T) { 555 tdb := NewDatabase(rawdb.NewMemoryDatabase()) 556 state, _ := New(types.EmptyRootHash, tdb, nil) 557 558 // Create an account and check if the retrieved balance is correct 559 addr, _ := common.NewAddressFromString("Zaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe") 560 skey := common.HexToHash("aaa") 561 sval := common.HexToHash("bbb") 562 563 state.SetBalance(addr, big.NewInt(42)) // Change the account trie 564 state.SetCode(addr, []byte("hello")) // Change an external metadata 565 state.SetState(addr, skey, sval) // Change the storage trie 566 567 if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { 568 t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42) 569 } 570 if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) { 571 t.Fatalf("initial code mismatch: have %x, want %x", code, []byte("hello")) 572 } 573 if val := state.GetState(addr, skey); val != sval { 574 t.Fatalf("initial non-committed storage slot mismatch: have %x, want %x", val, sval) 575 } 576 if val := state.GetCommittedState(addr, skey); val != (common.Hash{}) { 577 t.Fatalf("initial committed storage slot mismatch: have %x, want %x", val, common.Hash{}) 578 } 579 // Copy the non-committed state database and check pre/post commit balance 580 copyOne := state.Copy() 581 if balance := copyOne.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { 582 t.Fatalf("first copy pre-commit balance mismatch: have %v, want %v", balance, 42) 583 } 584 if code := copyOne.GetCode(addr); !bytes.Equal(code, []byte("hello")) { 585 t.Fatalf("first copy pre-commit code mismatch: have %x, want %x", code, []byte("hello")) 586 } 587 if val := copyOne.GetState(addr, skey); val != sval { 588 t.Fatalf("first copy pre-commit non-committed storage slot mismatch: have %x, want %x", val, sval) 589 } 590 if val := copyOne.GetCommittedState(addr, skey); val != (common.Hash{}) { 591 t.Fatalf("first copy pre-commit committed storage slot mismatch: have %x, want %x", val, common.Hash{}) 592 } 593 // Copy the copy and check the balance once more 594 copyTwo := copyOne.Copy() 595 if balance := copyTwo.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { 596 t.Fatalf("second copy balance mismatch: have %v, want %v", balance, 42) 597 } 598 if code := copyTwo.GetCode(addr); !bytes.Equal(code, []byte("hello")) { 599 t.Fatalf("second copy code mismatch: have %x, want %x", code, []byte("hello")) 600 } 601 if val := copyTwo.GetState(addr, skey); val != sval { 602 t.Fatalf("second copy non-committed storage slot mismatch: have %x, want %x", val, sval) 603 } 604 if val := copyTwo.GetCommittedState(addr, skey); val != (common.Hash{}) { 605 t.Fatalf("second copy committed storage slot mismatch: have %x, want %x", val, sval) 606 } 607 // Commit state, ensure states can be loaded from disk 608 root, _ := state.Commit(0, false) 609 state, _ = New(root, tdb, nil) 610 if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { 611 t.Fatalf("state post-commit balance mismatch: have %v, want %v", balance, 42) 612 } 613 if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) { 614 t.Fatalf("state post-commit code mismatch: have %x, want %x", code, []byte("hello")) 615 } 616 if val := state.GetState(addr, skey); val != sval { 617 t.Fatalf("state post-commit non-committed storage slot mismatch: have %x, want %x", val, sval) 618 } 619 if val := state.GetCommittedState(addr, skey); val != sval { 620 t.Fatalf("state post-commit committed storage slot mismatch: have %x, want %x", val, sval) 621 } 622 } 623 624 // Tests a regression where committing a copy lost some internal meta information, 625 // leading to corrupted subsequent copies. 626 // 627 // See https://github.com/theQRL/go-zond/issues/20106. 628 func TestCopyCopyCommitCopy(t *testing.T) { 629 state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil) 630 631 // Create an account and check if the retrieved balance is correct 632 addr, _ := common.NewAddressFromString("Zaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe") 633 skey := common.HexToHash("aaa") 634 sval := common.HexToHash("bbb") 635 636 state.SetBalance(addr, big.NewInt(42)) // Change the account trie 637 state.SetCode(addr, []byte("hello")) // Change an external metadata 638 state.SetState(addr, skey, sval) // Change the storage trie 639 640 if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { 641 t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42) 642 } 643 if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) { 644 t.Fatalf("initial code mismatch: have %x, want %x", code, []byte("hello")) 645 } 646 if val := state.GetState(addr, skey); val != sval { 647 t.Fatalf("initial non-committed storage slot mismatch: have %x, want %x", val, sval) 648 } 649 if val := state.GetCommittedState(addr, skey); val != (common.Hash{}) { 650 t.Fatalf("initial committed storage slot mismatch: have %x, want %x", val, common.Hash{}) 651 } 652 // Copy the non-committed state database and check pre/post commit balance 653 copyOne := state.Copy() 654 if balance := copyOne.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { 655 t.Fatalf("first copy balance mismatch: have %v, want %v", balance, 42) 656 } 657 if code := copyOne.GetCode(addr); !bytes.Equal(code, []byte("hello")) { 658 t.Fatalf("first copy code mismatch: have %x, want %x", code, []byte("hello")) 659 } 660 if val := copyOne.GetState(addr, skey); val != sval { 661 t.Fatalf("first copy non-committed storage slot mismatch: have %x, want %x", val, sval) 662 } 663 if val := copyOne.GetCommittedState(addr, skey); val != (common.Hash{}) { 664 t.Fatalf("first copy committed storage slot mismatch: have %x, want %x", val, common.Hash{}) 665 } 666 // Copy the copy and check the balance once more 667 copyTwo := copyOne.Copy() 668 if balance := copyTwo.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { 669 t.Fatalf("second copy pre-commit balance mismatch: have %v, want %v", balance, 42) 670 } 671 if code := copyTwo.GetCode(addr); !bytes.Equal(code, []byte("hello")) { 672 t.Fatalf("second copy pre-commit code mismatch: have %x, want %x", code, []byte("hello")) 673 } 674 if val := copyTwo.GetState(addr, skey); val != sval { 675 t.Fatalf("second copy pre-commit non-committed storage slot mismatch: have %x, want %x", val, sval) 676 } 677 if val := copyTwo.GetCommittedState(addr, skey); val != (common.Hash{}) { 678 t.Fatalf("second copy pre-commit committed storage slot mismatch: have %x, want %x", val, common.Hash{}) 679 } 680 // Copy the copy-copy and check the balance once more 681 copyThree := copyTwo.Copy() 682 if balance := copyThree.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { 683 t.Fatalf("third copy balance mismatch: have %v, want %v", balance, 42) 684 } 685 if code := copyThree.GetCode(addr); !bytes.Equal(code, []byte("hello")) { 686 t.Fatalf("third copy code mismatch: have %x, want %x", code, []byte("hello")) 687 } 688 if val := copyThree.GetState(addr, skey); val != sval { 689 t.Fatalf("third copy non-committed storage slot mismatch: have %x, want %x", val, sval) 690 } 691 if val := copyThree.GetCommittedState(addr, skey); val != (common.Hash{}) { 692 t.Fatalf("third copy committed storage slot mismatch: have %x, want %x", val, sval) 693 } 694 } 695 696 // TestCommitCopy tests the copy from a committed state is not functional. 697 func TestCommitCopy(t *testing.T) { 698 state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil) 699 700 // Create an account and check if the retrieved balance is correct 701 addr, _ := common.NewAddressFromString("Zaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe") 702 skey := common.HexToHash("aaa") 703 sval := common.HexToHash("bbb") 704 705 state.SetBalance(addr, big.NewInt(42)) // Change the account trie 706 state.SetCode(addr, []byte("hello")) // Change an external metadata 707 state.SetState(addr, skey, sval) // Change the storage trie 708 709 if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { 710 t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42) 711 } 712 if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) { 713 t.Fatalf("initial code mismatch: have %x, want %x", code, []byte("hello")) 714 } 715 if val := state.GetState(addr, skey); val != sval { 716 t.Fatalf("initial non-committed storage slot mismatch: have %x, want %x", val, sval) 717 } 718 if val := state.GetCommittedState(addr, skey); val != (common.Hash{}) { 719 t.Fatalf("initial committed storage slot mismatch: have %x, want %x", val, common.Hash{}) 720 } 721 // Copy the committed state database, the copied one is not functional. 722 state.Commit(0, true) 723 copied := state.Copy() 724 if balance := copied.GetBalance(addr); balance.Cmp(big.NewInt(0)) != 0 { 725 t.Fatalf("unexpected balance: have %v", balance) 726 } 727 if code := copied.GetCode(addr); code != nil { 728 t.Fatalf("unexpected code: have %x", code) 729 } 730 if val := copied.GetState(addr, skey); val != (common.Hash{}) { 731 t.Fatalf("unexpected storage slot: have %x", val) 732 } 733 if val := copied.GetCommittedState(addr, skey); val != (common.Hash{}) { 734 t.Fatalf("unexpected storage slot: have %x", val) 735 } 736 if !errors.Is(copied.Error(), trie.ErrCommitted) { 737 t.Fatalf("unexpected state error, %v", copied.Error()) 738 } 739 } 740 741 // TestMissingTrieNodes tests that if the StateDB fails to load parts of the trie, 742 // the Commit operation fails with an error 743 // If we are missing trie nodes, we should not continue writing to the trie 744 func TestMissingTrieNodes(t *testing.T) { 745 testMissingTrieNodes(t, rawdb.HashScheme) 746 testMissingTrieNodes(t, rawdb.PathScheme) 747 } 748 749 func testMissingTrieNodes(t *testing.T, scheme string) { 750 // Create an initial state with a few accounts 751 var ( 752 triedb *trie.Database 753 memDb = rawdb.NewMemoryDatabase() 754 ) 755 if scheme == rawdb.PathScheme { 756 triedb = trie.NewDatabase(memDb, &trie.Config{PathDB: &pathdb.Config{ 757 CleanCacheSize: 0, 758 DirtyCacheSize: 0, 759 }}) // disable caching 760 } else { 761 triedb = trie.NewDatabase(memDb, &trie.Config{HashDB: &hashdb.Config{ 762 CleanCacheSize: 0, 763 }}) // disable caching 764 } 765 db := NewDatabaseWithNodeDB(memDb, triedb) 766 767 var root common.Hash 768 state, _ := New(types.EmptyRootHash, db, nil) 769 addr := common.BytesToAddress([]byte("so")) 770 { 771 state.SetBalance(addr, big.NewInt(1)) 772 state.SetCode(addr, []byte{1, 2, 3}) 773 a2 := common.BytesToAddress([]byte("another")) 774 state.SetBalance(a2, big.NewInt(100)) 775 state.SetCode(a2, []byte{1, 2, 4}) 776 root, _ = state.Commit(0, false) 777 t.Logf("root: %x", root) 778 // force-flush 779 triedb.Commit(root, false) 780 } 781 // Create a new state on the old root 782 state, _ = New(root, db, nil) 783 // Now we clear out the memdb 784 it := memDb.NewIterator(nil, nil) 785 for it.Next() { 786 k := it.Key() 787 // Leave the root intact 788 if !bytes.Equal(k, root[:]) { 789 t.Logf("key: %x", k) 790 memDb.Delete(k) 791 } 792 } 793 balance := state.GetBalance(addr) 794 // The removed elem should lead to it returning zero balance 795 if exp, got := uint64(0), balance.Uint64(); got != exp { 796 t.Errorf("expected %d, got %d", exp, got) 797 } 798 // Modify the state 799 state.SetBalance(addr, big.NewInt(2)) 800 root, err := state.Commit(0, false) 801 if err == nil { 802 t.Fatalf("expected error, got root :%x", root) 803 } 804 } 805 806 func TestStateDBAccessList(t *testing.T) { 807 // Some helpers 808 addr := func(a string) common.Address { 809 addr, _ := common.NewAddressFromString(a) 810 return addr 811 } 812 slot := func(a string) common.Hash { 813 return common.HexToHash(a) 814 } 815 816 memDb := rawdb.NewMemoryDatabase() 817 db := NewDatabase(memDb) 818 state, _ := New(types.EmptyRootHash, db, nil) 819 state.accessList = newAccessList() 820 821 verifyAddrs := func(astrings ...string) { 822 t.Helper() 823 // convert to common.Address form 824 var addresses []common.Address 825 var addressMap = make(map[common.Address]struct{}) 826 for _, astring := range astrings { 827 address := addr(astring) 828 addresses = append(addresses, address) 829 addressMap[address] = struct{}{} 830 } 831 // Check that the given addresses are in the access list 832 for _, address := range addresses { 833 if !state.AddressInAccessList(address) { 834 t.Fatalf("expected %x to be in access list", address) 835 } 836 } 837 // Check that only the expected addresses are present in the access list 838 for address := range state.accessList.addresses { 839 if _, exist := addressMap[address]; !exist { 840 t.Fatalf("extra address %x in access list", address) 841 } 842 } 843 } 844 verifySlots := func(addrString string, slotStrings ...string) { 845 if !state.AddressInAccessList(addr(addrString)) { 846 t.Fatalf("scope missing address/slots %v", addrString) 847 } 848 var address = addr(addrString) 849 // convert to common.Hash form 850 var slots []common.Hash 851 var slotMap = make(map[common.Hash]struct{}) 852 for _, slotString := range slotStrings { 853 s := slot(slotString) 854 slots = append(slots, s) 855 slotMap[s] = struct{}{} 856 } 857 // Check that the expected items are in the access list 858 for i, s := range slots { 859 if _, slotPresent := state.SlotInAccessList(address, s); !slotPresent { 860 t.Fatalf("input %d: scope missing slot %v (address %v)", i, s, addrString) 861 } 862 } 863 // Check that no extra elements are in the access list 864 index := state.accessList.addresses[address] 865 if index >= 0 { 866 stateSlots := state.accessList.slots[index] 867 for s := range stateSlots { 868 if _, slotPresent := slotMap[s]; !slotPresent { 869 t.Fatalf("scope has extra slot %v (address %v)", s, addrString) 870 } 871 } 872 } 873 } 874 875 state.AddAddressToAccessList(addr("Z00000000000000000000000000000000000000aa")) // 1 876 state.AddSlotToAccessList(addr("Z00000000000000000000000000000000000000bb"), slot("01")) // 2,3 877 state.AddSlotToAccessList(addr("Z00000000000000000000000000000000000000bb"), slot("02")) // 4 878 verifyAddrs("Z00000000000000000000000000000000000000aa", "Z00000000000000000000000000000000000000bb") 879 verifySlots("Z00000000000000000000000000000000000000bb", "01", "02") 880 881 // Make a copy 882 stateCopy1 := state.Copy() 883 if exp, got := 4, state.journal.length(); exp != got { 884 t.Fatalf("journal length mismatch: have %d, want %d", got, exp) 885 } 886 887 // same again, should cause no journal entries 888 state.AddSlotToAccessList(addr("Z00000000000000000000000000000000000000bb"), slot("01")) 889 state.AddSlotToAccessList(addr("Z00000000000000000000000000000000000000bb"), slot("02")) 890 state.AddAddressToAccessList(addr("Z00000000000000000000000000000000000000aa")) 891 if exp, got := 4, state.journal.length(); exp != got { 892 t.Fatalf("journal length mismatch: have %d, want %d", got, exp) 893 } 894 // some new ones 895 state.AddSlotToAccessList(addr("Z00000000000000000000000000000000000000bb"), slot("03")) // 5 896 state.AddSlotToAccessList(addr("Z00000000000000000000000000000000000000aa"), slot("01")) // 6 897 state.AddSlotToAccessList(addr("Z00000000000000000000000000000000000000cc"), slot("01")) // 7,8 898 state.AddAddressToAccessList(addr("Z00000000000000000000000000000000000000cc")) 899 if exp, got := 8, state.journal.length(); exp != got { 900 t.Fatalf("journal length mismatch: have %d, want %d", got, exp) 901 } 902 903 verifyAddrs("Z00000000000000000000000000000000000000aa", "Z00000000000000000000000000000000000000bb", "Z00000000000000000000000000000000000000cc") 904 verifySlots("Z00000000000000000000000000000000000000aa", "01") 905 verifySlots("Z00000000000000000000000000000000000000bb", "01", "02", "03") 906 verifySlots("Z00000000000000000000000000000000000000cc", "01") 907 908 // now start rolling back changes 909 state.journal.revert(state, 7) 910 if _, ok := state.SlotInAccessList(addr("Z00000000000000000000000000000000000000cc"), slot("01")); ok { 911 t.Fatalf("slot present, expected missing") 912 } 913 verifyAddrs("Z00000000000000000000000000000000000000aa", "Z00000000000000000000000000000000000000bb", "Z00000000000000000000000000000000000000cc") 914 verifySlots("Z00000000000000000000000000000000000000aa", "01") 915 verifySlots("Z00000000000000000000000000000000000000bb", "01", "02", "03") 916 917 state.journal.revert(state, 6) 918 if state.AddressInAccessList(addr("Z00000000000000000000000000000000000000cc")) { 919 t.Fatalf("addr present, expected missing") 920 } 921 verifyAddrs("Z00000000000000000000000000000000000000aa", "Z00000000000000000000000000000000000000bb") 922 verifySlots("Z00000000000000000000000000000000000000aa", "01") 923 verifySlots("Z00000000000000000000000000000000000000bb", "01", "02", "03") 924 925 state.journal.revert(state, 5) 926 if _, ok := state.SlotInAccessList(addr("Z00000000000000000000000000000000000000aa"), slot("01")); ok { 927 t.Fatalf("slot present, expected missing") 928 } 929 verifyAddrs("Z00000000000000000000000000000000000000aa", "Z00000000000000000000000000000000000000bb") 930 verifySlots("Z00000000000000000000000000000000000000bb", "01", "02", "03") 931 932 state.journal.revert(state, 4) 933 if _, ok := state.SlotInAccessList(addr("Z00000000000000000000000000000000000000bb"), slot("03")); ok { 934 t.Fatalf("slot present, expected missing") 935 } 936 verifyAddrs("Z00000000000000000000000000000000000000aa", "Z00000000000000000000000000000000000000bb") 937 verifySlots("Z00000000000000000000000000000000000000bb", "01", "02") 938 939 state.journal.revert(state, 3) 940 if _, ok := state.SlotInAccessList(addr("Z00000000000000000000000000000000000000bb"), slot("02")); ok { 941 t.Fatalf("slot present, expected missing") 942 } 943 verifyAddrs("Z00000000000000000000000000000000000000aa", "Z00000000000000000000000000000000000000bb") 944 verifySlots("Z00000000000000000000000000000000000000bb", "01") 945 946 state.journal.revert(state, 2) 947 if _, ok := state.SlotInAccessList(addr("Z00000000000000000000000000000000000000bb"), slot("01")); ok { 948 t.Fatalf("slot present, expected missing") 949 } 950 verifyAddrs("Z00000000000000000000000000000000000000aa", "Z00000000000000000000000000000000000000bb") 951 952 state.journal.revert(state, 1) 953 if state.AddressInAccessList(addr("Z00000000000000000000000000000000000000bb")) { 954 t.Fatalf("addr present, expected missing") 955 } 956 verifyAddrs("Z00000000000000000000000000000000000000aa") 957 958 state.journal.revert(state, 0) 959 if state.AddressInAccessList(addr("Z00000000000000000000000000000000000000aa")) { 960 t.Fatalf("addr present, expected missing") 961 } 962 if got, exp := len(state.accessList.addresses), 0; got != exp { 963 t.Fatalf("expected empty, got %d", got) 964 } 965 if got, exp := len(state.accessList.slots), 0; got != exp { 966 t.Fatalf("expected empty, got %d", got) 967 } 968 // Check the copy 969 // Make a copy 970 state = stateCopy1 971 verifyAddrs("Z00000000000000000000000000000000000000aa", "Z00000000000000000000000000000000000000bb") 972 verifySlots("Z00000000000000000000000000000000000000bb", "01", "02") 973 if got, exp := len(state.accessList.addresses), 2; got != exp { 974 t.Fatalf("expected empty, got %d", got) 975 } 976 if got, exp := len(state.accessList.slots), 1; got != exp { 977 t.Fatalf("expected empty, got %d", got) 978 } 979 } 980 981 // Tests that account and storage tries are flushed in the correct order and that 982 // no data loss occurs. 983 func TestFlushOrderDataLoss(t *testing.T) { 984 // Create a state trie with many accounts and slots 985 var ( 986 memdb = rawdb.NewMemoryDatabase() 987 triedb = trie.NewDatabase(memdb, nil) 988 statedb = NewDatabaseWithNodeDB(memdb, triedb) 989 state, _ = New(types.EmptyRootHash, statedb, nil) 990 ) 991 for a := byte(0); a < 10; a++ { 992 state.CreateAccount(common.Address{a}) 993 for s := byte(0); s < 10; s++ { 994 state.SetState(common.Address{a}, common.Hash{a, s}, common.Hash{a, s}) 995 } 996 } 997 root, err := state.Commit(0, false) 998 if err != nil { 999 t.Fatalf("failed to commit state trie: %v", err) 1000 } 1001 triedb.Reference(root, common.Hash{}) 1002 if err := triedb.Cap(1024); err != nil { 1003 t.Fatalf("failed to cap trie dirty cache: %v", err) 1004 } 1005 if err := triedb.Commit(root, false); err != nil { 1006 t.Fatalf("failed to commit state trie: %v", err) 1007 } 1008 // Reopen the state trie from flushed disk and verify it 1009 state, err = New(root, NewDatabase(memdb), nil) 1010 if err != nil { 1011 t.Fatalf("failed to reopen state trie: %v", err) 1012 } 1013 for a := byte(0); a < 10; a++ { 1014 for s := byte(0); s < 10; s++ { 1015 if have := state.GetState(common.Address{a}, common.Hash{a, s}); have != (common.Hash{a, s}) { 1016 t.Errorf("account %d: slot %d: state mismatch: have %x, want %x", a, s, have, common.Hash{a, s}) 1017 } 1018 } 1019 } 1020 } 1021 1022 func TestResetObject(t *testing.T) { 1023 var ( 1024 disk = rawdb.NewMemoryDatabase() 1025 tdb = trie.NewDatabase(disk, nil) 1026 db = NewDatabaseWithNodeDB(disk, tdb) 1027 snaps, _ = snapshot.New(snapshot.Config{CacheSize: 10}, disk, tdb, types.EmptyRootHash) 1028 state, _ = New(types.EmptyRootHash, db, snaps) 1029 addr, _ = common.NewAddressFromString("Z0000000000000000000000000000000000000001") 1030 slotA = common.HexToHash("0x1") 1031 slotB = common.HexToHash("0x2") 1032 ) 1033 // Initialize account with balance and storage in first transaction. 1034 state.SetBalance(addr, big.NewInt(1)) 1035 state.SetState(addr, slotA, common.BytesToHash([]byte{0x1})) 1036 state.IntermediateRoot(true) 1037 1038 // Reset account and mutate balance and storages 1039 state.CreateAccount(addr) 1040 state.SetBalance(addr, big.NewInt(2)) 1041 state.SetState(addr, slotB, common.BytesToHash([]byte{0x2})) 1042 root, _ := state.Commit(0, true) 1043 1044 // Ensure the original account is wiped properly 1045 snap := snaps.Snapshot(root) 1046 slot, _ := snap.Storage(crypto.Keccak256Hash(addr.Bytes()), crypto.Keccak256Hash(slotA.Bytes())) 1047 if len(slot) != 0 { 1048 t.Fatalf("Unexpected storage slot") 1049 } 1050 slot, _ = snap.Storage(crypto.Keccak256Hash(addr.Bytes()), crypto.Keccak256Hash(slotB.Bytes())) 1051 if !bytes.Equal(slot, []byte{0x2}) { 1052 t.Fatalf("Unexpected storage slot value %v", slot) 1053 } 1054 } 1055 1056 func TestDeleteStorage(t *testing.T) { 1057 var ( 1058 disk = rawdb.NewMemoryDatabase() 1059 tdb = trie.NewDatabase(disk, nil) 1060 db = NewDatabaseWithNodeDB(disk, tdb) 1061 snaps, _ = snapshot.New(snapshot.Config{CacheSize: 10}, disk, tdb, types.EmptyRootHash) 1062 state, _ = New(types.EmptyRootHash, db, snaps) 1063 addr, _ = common.NewAddressFromString("Z0000000000000000000000000000000000000001") 1064 ) 1065 // Initialize account and populate storage 1066 state.SetBalance(addr, big.NewInt(1)) 1067 state.CreateAccount(addr) 1068 for i := 0; i < 1000; i++ { 1069 slot := common.Hash(uint256.NewInt(uint64(i)).Bytes32()) 1070 value := common.Hash(uint256.NewInt(uint64(10 * i)).Bytes32()) 1071 state.SetState(addr, slot, value) 1072 } 1073 root, _ := state.Commit(0, true) 1074 // Init phase done, create two states, one with snap and one without 1075 fastState, _ := New(root, db, snaps) 1076 slowState, _ := New(root, db, nil) 1077 1078 obj := fastState.GetOrNewStateObject(addr) 1079 storageRoot := obj.data.Root 1080 1081 _, _, fastNodes, err := fastState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot) 1082 if err != nil { 1083 t.Fatal(err) 1084 } 1085 1086 _, _, slowNodes, err := slowState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot) 1087 if err != nil { 1088 t.Fatal(err) 1089 } 1090 check := func(set *trienode.NodeSet) string { 1091 var a []string 1092 set.ForEachWithOrder(func(path string, n *trienode.Node) { 1093 if n.Hash != (common.Hash{}) { 1094 t.Fatal("delete should have empty hashes") 1095 } 1096 if len(n.Blob) != 0 { 1097 t.Fatal("delete should have have empty blobs") 1098 } 1099 a = append(a, fmt.Sprintf("%x", path)) 1100 }) 1101 return strings.Join(a, ",") 1102 } 1103 slowRes := check(slowNodes) 1104 fastRes := check(fastNodes) 1105 if slowRes != fastRes { 1106 t.Fatalf("difference found:\nfast: %v\nslow: %v\n", fastRes, slowRes) 1107 } 1108 }