github.com/gochain-io/gochain@v2.2.26+incompatible/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 "context" 22 "encoding/binary" 23 "fmt" 24 "math" 25 "math/big" 26 "math/rand" 27 "reflect" 28 "strings" 29 "testing" 30 "testing/quick" 31 32 check "gopkg.in/check.v1" 33 34 "github.com/gochain-io/gochain/common" 35 "github.com/gochain-io/gochain/core/types" 36 "github.com/gochain-io/gochain/ethdb" 37 ) 38 39 // Tests that updating a state trie does not leak any database writes prior to 40 // actually committing the state. 41 func TestUpdateLeaks(t *testing.T) { 42 // Create an empty state database 43 db := ethdb.NewMemDatabase() 44 state, _ := New(common.Hash{}, NewDatabase(db)) 45 46 // Update it with some accounts 47 for i := byte(0); i < 255; i++ { 48 addr := common.BytesToAddress([]byte{i}) 49 state.AddBalance(addr, big.NewInt(int64(11*i))) 50 state.SetNonce(addr, uint64(42*i)) 51 if i%2 == 0 { 52 state.SetState(addr, common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i})) 53 } 54 if i%3 == 0 { 55 state.SetCode(addr, []byte{i, i, i, i, i}) 56 } 57 state.IntermediateRoot(false) 58 } 59 // Ensure that no data was leaked into the database 60 for _, key := range db.Keys() { 61 value, _ := db.Get(key) 62 t.Errorf("State leaked into database: %x -> %x", key, value) 63 } 64 } 65 66 // Tests that no intermediate state of an object is stored into the database, 67 // only the one right before the commit. 68 func TestIntermediateLeaks(t *testing.T) { 69 // Create two state databases, one transitioning to the final state, the other final from the beginning 70 transDb := ethdb.NewMemDatabase() 71 finalDb := ethdb.NewMemDatabase() 72 transState, _ := New(common.Hash{}, NewDatabase(transDb)) 73 finalState, _ := New(common.Hash{}, NewDatabase(finalDb)) 74 75 modify := func(state *StateDB, addr common.Address, i, tweak byte) { 76 state.SetBalance(addr, big.NewInt(int64(11*i)+int64(tweak))) 77 state.SetNonce(addr, uint64(42*i+tweak)) 78 if i%2 == 0 { 79 state.SetState(addr, common.Hash{i, i, i, 0}, common.Hash{}) 80 state.SetState(addr, common.Hash{i, i, i, tweak}, common.Hash{i, i, i, i, tweak}) 81 } 82 if i%3 == 0 { 83 state.SetCode(addr, []byte{i, i, i, i, i, tweak}) 84 } 85 } 86 87 // Modify the transient state. 88 for i := byte(0); i < 255; i++ { 89 modify(transState, common.Address{byte(i)}, i, 0) 90 } 91 // Write modifications to trie. 92 transState.IntermediateRoot(false) 93 94 // Overwrite all the data with new values in the transient database. 95 for i := byte(0); i < 255; i++ { 96 modify(transState, common.Address{byte(i)}, i, 99) 97 modify(finalState, common.Address{byte(i)}, i, 99) 98 } 99 100 // Commit and cross check the databases. 101 if _, err := transState.Commit(false); err != nil { 102 t.Fatalf("failed to commit transition state: %v", err) 103 } 104 if _, err := finalState.Commit(false); err != nil { 105 t.Fatalf("failed to commit final state: %v", err) 106 } 107 for _, key := range finalDb.Keys() { 108 if _, err := transDb.Get(key); err != nil { 109 val, _ := finalDb.Get(key) 110 t.Errorf("entry missing from the transition database: %x -> %x", key, val) 111 } 112 } 113 for _, key := range transDb.Keys() { 114 if _, err := finalDb.Get(key); err != nil { 115 val, _ := transDb.Get(key) 116 t.Errorf("extra entry in the transition database: %x -> %x", key, val) 117 } 118 } 119 } 120 121 // TestCopy tests that copying a statedb object indeed makes the original and 122 // the copy independent of each other. This test is a regression test against 123 // https://github.com/ethereum/go-ethereum/pull/15549. 124 func TestCopy(t *testing.T) { 125 // Create a random state test to copy and modify "independently" 126 db := ethdb.NewMemDatabase() 127 orig, _ := New(common.Hash{}, NewDatabase(db)) 128 129 for i := byte(0); i < 255; i++ { 130 obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) 131 obj.AddBalance(big.NewInt(int64(i))) 132 orig.updateStateObject(obj) 133 } 134 orig.Finalise(false) 135 136 // Copy the state, modify both in-memory 137 copy := orig.Copy(context.Background()) 138 139 for i := byte(0); i < 255; i++ { 140 origObj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) 141 copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) 142 143 origObj.AddBalance(big.NewInt(2 * int64(i))) 144 copyObj.AddBalance(big.NewInt(3 * int64(i))) 145 146 orig.updateStateObject(origObj) 147 copy.updateStateObject(copyObj) 148 } 149 // Finalise the changes on both concurrently 150 done := make(chan struct{}) 151 go func() { 152 orig.Finalise(true) 153 close(done) 154 }() 155 copy.Finalise(true) 156 <-done 157 158 // Verify that the two states have been updated independently 159 for i := byte(0); i < 255; i++ { 160 origObj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) 161 copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) 162 163 if want := big.NewInt(3 * int64(i)); origObj.Balance().Cmp(want) != 0 { 164 t.Errorf("orig obj %d: balance mismatch: have %v, want %v", i, origObj.Balance(), want) 165 } 166 if want := big.NewInt(4 * int64(i)); copyObj.Balance().Cmp(want) != 0 { 167 t.Errorf("copy obj %d: balance mismatch: have %v, want %v", i, copyObj.Balance(), want) 168 } 169 } 170 } 171 172 func TestSnapshotRandom(t *testing.T) { 173 config := &quick.Config{MaxCount: 1000} 174 err := quick.Check((*snapshotTest).run, config) 175 if cerr, ok := err.(*quick.CheckError); ok { 176 test := cerr.In[0].(*snapshotTest) 177 t.Errorf("%v:\n%s", test.err, test) 178 } else if err != nil { 179 t.Error(err) 180 } 181 } 182 183 // A snapshotTest checks that reverting StateDB snapshots properly undoes all changes 184 // captured by the snapshot. Instances of this test with pseudorandom content are created 185 // by Generate. 186 // 187 // The test works as follows: 188 // 189 // A new state is created and all actions are applied to it. Several snapshots are taken 190 // in between actions. The test then reverts each snapshot. For each snapshot the actions 191 // leading up to it are replayed on a fresh, empty state. The behaviour of all public 192 // accessor methods on the reverted state must match the return value of the equivalent 193 // methods on the replayed state. 194 type snapshotTest struct { 195 addrs []common.Address // all account addresses 196 actions []testAction // modifications to the state 197 snapshots []int // actions indexes at which snapshot is taken 198 err error // failure details are reported through this field 199 } 200 201 type testAction struct { 202 name string 203 fn func(testAction, *StateDB) 204 args []int64 205 noAddr bool 206 } 207 208 // newTestAction creates a random action that changes state. 209 func newTestAction(addr common.Address, r *rand.Rand) testAction { 210 actions := []testAction{ 211 { 212 name: "SetBalance", 213 fn: func(a testAction, s *StateDB) { 214 s.SetBalance(addr, big.NewInt(a.args[0])) 215 }, 216 args: make([]int64, 1), 217 }, 218 { 219 name: "AddBalance", 220 fn: func(a testAction, s *StateDB) { 221 s.AddBalance(addr, big.NewInt(a.args[0])) 222 }, 223 args: make([]int64, 1), 224 }, 225 { 226 name: "SetNonce", 227 fn: func(a testAction, s *StateDB) { 228 s.SetNonce(addr, uint64(a.args[0])) 229 }, 230 args: make([]int64, 1), 231 }, 232 { 233 name: "SetState", 234 fn: func(a testAction, s *StateDB) { 235 var key, val common.Hash 236 binary.BigEndian.PutUint16(key[:], uint16(a.args[0])) 237 binary.BigEndian.PutUint16(val[:], uint16(a.args[1])) 238 s.SetState(addr, key, val) 239 }, 240 args: make([]int64, 2), 241 }, 242 { 243 name: "SetCode", 244 fn: func(a testAction, s *StateDB) { 245 code := make([]byte, 16) 246 binary.BigEndian.PutUint64(code, uint64(a.args[0])) 247 binary.BigEndian.PutUint64(code[8:], uint64(a.args[1])) 248 s.SetCode(addr, code) 249 }, 250 args: make([]int64, 2), 251 }, 252 { 253 name: "CreateAccount", 254 fn: func(a testAction, s *StateDB) { 255 s.CreateAccount(addr) 256 }, 257 }, 258 { 259 name: "Suicide", 260 fn: func(a testAction, s *StateDB) { 261 s.Suicide(addr) 262 }, 263 }, 264 { 265 name: "AddRefund", 266 fn: func(a testAction, s *StateDB) { 267 s.AddRefund(uint64(a.args[0])) 268 }, 269 args: make([]int64, 1), 270 noAddr: true, 271 }, 272 { 273 name: "AddLog", 274 fn: func(a testAction, s *StateDB) { 275 data := make([]byte, 2) 276 binary.BigEndian.PutUint16(data, uint16(a.args[0])) 277 s.AddLog(&types.Log{Address: addr, Data: data}) 278 }, 279 args: make([]int64, 1), 280 }, 281 } 282 action := actions[r.Intn(len(actions))] 283 var nameargs []string 284 if !action.noAddr { 285 nameargs = append(nameargs, addr.Hex()) 286 } 287 for _, i := range action.args { 288 action.args[i] = rand.Int63n(100) 289 nameargs = append(nameargs, fmt.Sprint(action.args[i])) 290 } 291 action.name += strings.Join(nameargs, ", ") 292 return action 293 } 294 295 // Generate returns a new snapshot test of the given size. All randomness is 296 // derived from r. 297 func (*snapshotTest) Generate(r *rand.Rand, size int) reflect.Value { 298 // Generate random actions. 299 addrs := make([]common.Address, 50) 300 for i := range addrs { 301 addrs[i][0] = byte(i) 302 } 303 actions := make([]testAction, size) 304 for i := range actions { 305 addr := addrs[r.Intn(len(addrs))] 306 actions[i] = newTestAction(addr, r) 307 } 308 // Generate snapshot indexes. 309 nsnapshots := int(math.Sqrt(float64(size))) 310 if size > 0 && nsnapshots == 0 { 311 nsnapshots = 1 312 } 313 snapshots := make([]int, nsnapshots) 314 snaplen := len(actions) / nsnapshots 315 for i := range snapshots { 316 // Try to place the snapshots some number of actions apart from each other. 317 snapshots[i] = (i * snaplen) + r.Intn(snaplen) 318 } 319 return reflect.ValueOf(&snapshotTest{addrs, actions, snapshots, nil}) 320 } 321 322 func (test *snapshotTest) String() string { 323 out := new(bytes.Buffer) 324 sindex := 0 325 for i, action := range test.actions { 326 if len(test.snapshots) > sindex && i == test.snapshots[sindex] { 327 fmt.Fprintf(out, "---- snapshot %d ----\n", sindex) 328 sindex++ 329 } 330 fmt.Fprintf(out, "%4d: %s\n", i, action.name) 331 } 332 return out.String() 333 } 334 335 func (test *snapshotTest) run() bool { 336 // Run all actions and create snapshots. 337 var ( 338 db = ethdb.NewMemDatabase() 339 state, _ = New(common.Hash{}, NewDatabase(db)) 340 snapshotRevs = make([]int, len(test.snapshots)) 341 sindex = 0 342 ) 343 for i, action := range test.actions { 344 if len(test.snapshots) > sindex && i == test.snapshots[sindex] { 345 snapshotRevs[sindex] = state.Snapshot() 346 sindex++ 347 } 348 action.fn(action, state) 349 } 350 // Revert all snapshots in reverse order. Each revert must yield a state 351 // that is equivalent to fresh state with all actions up the snapshot applied. 352 for sindex--; sindex >= 0; sindex-- { 353 checkstate, _ := New(common.Hash{}, state.Database()) 354 for _, action := range test.actions[:test.snapshots[sindex]] { 355 action.fn(action, checkstate) 356 } 357 state.RevertToSnapshot(snapshotRevs[sindex]) 358 if err := test.checkEqual(state, checkstate); err != nil { 359 test.err = fmt.Errorf("state mismatch after revert to snapshot %d\n%v", sindex, err) 360 return false 361 } 362 } 363 return true 364 } 365 366 // checkEqual checks that methods of state and checkstate return the same values. 367 func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error { 368 for _, addr := range test.addrs { 369 var err error 370 checkeq := func(op string, a, b interface{}) bool { 371 if err == nil && !reflect.DeepEqual(a, b) { 372 err = fmt.Errorf("got %s(%s) == %v, want %v", op, addr.Hex(), a, b) 373 return false 374 } 375 return true 376 } 377 // Check basic accessor methods. 378 checkeq("Exist", state.Exist(addr), checkstate.Exist(addr)) 379 checkeq("HasSuicided", state.HasSuicided(addr), checkstate.HasSuicided(addr)) 380 checkeq("GetBalance", state.GetBalance(addr), checkstate.GetBalance(addr)) 381 checkeq("GetNonce", state.GetNonce(addr), checkstate.GetNonce(addr)) 382 checkeq("GetCode", state.GetCode(addr), checkstate.GetCode(addr)) 383 checkeq("GetCodeHash", state.GetCodeHash(addr), checkstate.GetCodeHash(addr)) 384 checkeq("GetCodeSize", state.GetCodeSize(addr), checkstate.GetCodeSize(addr)) 385 // Check storage. 386 if obj, err := state.getStateObject(addr); err != nil { 387 return fmt.Errorf("failed to get state: %s", err) 388 } else if obj != nil { 389 state.ForEachStorage(addr, func(key, value common.Hash) bool { 390 return checkeq("GetState("+key.Hex()+")", value, checkstate.GetState(addr, key)) 391 }) 392 checkstate.ForEachStorage(addr, func(key, value common.Hash) bool { 393 return checkeq("GetState("+key.Hex()+")", state.GetState(addr, key), value) 394 }) 395 } 396 if err != nil { 397 return err 398 } 399 } 400 401 if state.GetRefund() != checkstate.GetRefund() { 402 return fmt.Errorf("got GetRefund() == %d, want GetRefund() == %d", 403 state.GetRefund(), checkstate.GetRefund()) 404 } 405 if !reflect.DeepEqual(state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{})) { 406 return fmt.Errorf("got GetLogs(common.Hash{}) == %v, want GetLogs(common.Hash{}) == %v", 407 state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{})) 408 } 409 return nil 410 } 411 412 func (s *StateSuite) TestTouchDelete(c *check.C) { 413 s.state.GetOrNewStateObject(common.Address{}) 414 root, _ := s.state.Commit(false) 415 s.state.Reset(root) 416 417 snapshot := s.state.Snapshot() 418 s.state.AddBalance(common.Address{}, new(big.Int)) 419 420 if len(s.state.journal.dirties) != 1 { 421 c.Fatal("expected one dirty state object") 422 } 423 s.state.RevertToSnapshot(snapshot) 424 if len(s.state.journal.dirties) != 0 { 425 c.Fatal("expected no dirty state object") 426 } 427 } 428 429 // TestCopyOfCopy tests that modified objects are carried over to the copy, and the copy of the copy. 430 // See https://github.com/ethereum/go-ethereum/pull/15225#issuecomment-380191512 431 func TestCopyOfCopy(t *testing.T) { 432 sdb, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase())) 433 addr := common.HexToAddress("aaaa") 434 sdb.SetBalance(addr, big.NewInt(42)) 435 ctx := context.Background() 436 if got := sdb.Copy(ctx).GetBalance(addr).Uint64(); got != 42 { 437 t.Fatalf("1st copy fail, expected 42, got %v", got) 438 } 439 if got := sdb.Copy(ctx).Copy(ctx).GetBalance(addr).Uint64(); got != 42 { 440 t.Fatalf("2nd copy fail, expected 42, got %v", got) 441 } 442 }