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