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