123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122 |
- // Copyright 2016 The go-ethereum Authors
- // This file is part of the go-ethereum library.
- //
- // The go-ethereum library is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Lesser General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // The go-ethereum library is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Lesser General Public License for more details.
- //
- // You should have received a copy of the GNU Lesser General Public License
- // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
- package state
- import (
- "bytes"
- "encoding/binary"
- "fmt"
- "math"
- "math/big"
- "math/rand"
- "reflect"
- "strings"
- "sync"
- "testing"
- "testing/quick"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/private/engine"
- )
- // Tests that updating a state trie does not leak any database writes prior to
- // actually committing the state.
- func TestUpdateLeaks(t *testing.T) {
- // Create an empty state database
- db := rawdb.NewMemoryDatabase()
- state, _ := New(common.Hash{}, NewDatabase(db), nil)
- // Update it with some accounts
- for i := byte(0); i < 255; i++ {
- addr := common.BytesToAddress([]byte{i})
- state.AddBalance(addr, big.NewInt(int64(11*i)))
- state.SetNonce(addr, uint64(42*i))
- if i%2 == 0 {
- state.SetState(addr, common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i}))
- }
- if i%3 == 0 {
- state.SetCode(addr, []byte{i, i, i, i, i})
- }
- }
- root := state.IntermediateRoot(false)
- if err := state.Database().TrieDB().Commit(root, false, nil); err != nil {
- t.Errorf("can not commit trie %v to persistent database", root.Hex())
- }
- // Ensure that no data was leaked into the database
- it := db.NewIterator(nil, nil)
- for it.Next() {
- t.Errorf("State leaked into database: %x -> %x", it.Key(), it.Value())
- }
- it.Release()
- }
- // Tests that no intermediate state of an object is stored into the database,
- // only the one right before the commit.
- func TestIntermediateLeaks(t *testing.T) {
- // Create two state databases, one transitioning to the final state, the other final from the beginning
- transDb := rawdb.NewMemoryDatabase()
- finalDb := rawdb.NewMemoryDatabase()
- transState, _ := New(common.Hash{}, NewDatabase(transDb), nil)
- finalState, _ := New(common.Hash{}, NewDatabase(finalDb), nil)
- modify := func(state *StateDB, addr common.Address, i, tweak byte) {
- state.SetBalance(addr, big.NewInt(int64(11*i)+int64(tweak)))
- state.SetNonce(addr, uint64(42*i+tweak))
- if i%2 == 0 {
- state.SetState(addr, common.Hash{i, i, i, 0}, common.Hash{})
- state.SetState(addr, common.Hash{i, i, i, tweak}, common.Hash{i, i, i, i, tweak})
- }
- if i%3 == 0 {
- state.SetCode(addr, []byte{i, i, i, i, i, tweak})
- }
- }
- // Modify the transient state.
- for i := byte(0); i < 255; i++ {
- modify(transState, common.Address{i}, i, 0)
- }
- // Write modifications to trie.
- transState.IntermediateRoot(false)
- // Overwrite all the data with new values in the transient database.
- for i := byte(0); i < 255; i++ {
- modify(transState, common.Address{i}, i, 99)
- modify(finalState, common.Address{i}, i, 99)
- }
- // Commit and cross check the databases.
- transRoot, err := transState.Commit(false)
- if err != nil {
- t.Fatalf("failed to commit transition state: %v", err)
- }
- if err = transState.Database().TrieDB().Commit(transRoot, false, nil); err != nil {
- t.Errorf("can not commit trie %v to persistent database", transRoot.Hex())
- }
- finalRoot, err := finalState.Commit(false)
- if err != nil {
- t.Fatalf("failed to commit final state: %v", err)
- }
- if err = finalState.Database().TrieDB().Commit(finalRoot, false, nil); err != nil {
- t.Errorf("can not commit trie %v to persistent database", finalRoot.Hex())
- }
- it := finalDb.NewIterator(nil, nil)
- for it.Next() {
- key, fvalue := it.Key(), it.Value()
- tvalue, err := transDb.Get(key)
- if err != nil {
- t.Errorf("entry missing from the transition database: %x -> %x", key, fvalue)
- }
- if !bytes.Equal(fvalue, tvalue) {
- t.Errorf("the value associate key %x is mismatch,: %x in transition database ,%x in final database", key, tvalue, fvalue)
- }
- }
- it.Release()
- it = transDb.NewIterator(nil, nil)
- for it.Next() {
- key, tvalue := it.Key(), it.Value()
- fvalue, err := finalDb.Get(key)
- if err != nil {
- t.Errorf("extra entry in the transition database: %x -> %x", key, it.Value())
- }
- if !bytes.Equal(fvalue, tvalue) {
- t.Errorf("the value associate key %x is mismatch,: %x in transition database ,%x in final database", key, tvalue, fvalue)
- }
- }
- }
- // TestCopy tests that copying a StateDB object indeed makes the original and
- // the copy independent of each other. This test is a regression test against
- // https://github.com/ethereum/go-ethereum/pull/15549.
- func TestCopy(t *testing.T) {
- // Create a random state test to copy and modify "independently"
- orig, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()), nil)
- for i := byte(0); i < 255; i++ {
- obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
- obj.AddBalance(big.NewInt(int64(i)))
- orig.updateStateObject(obj)
- }
- orig.Finalise(false)
- // Copy the state
- copy := orig.Copy()
- // Copy the copy state
- ccopy := copy.Copy()
- // modify all in memory
- for i := byte(0); i < 255; i++ {
- origObj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
- copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
- ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
- origObj.AddBalance(big.NewInt(2 * int64(i)))
- copyObj.AddBalance(big.NewInt(3 * int64(i)))
- ccopyObj.AddBalance(big.NewInt(4 * int64(i)))
- orig.updateStateObject(origObj)
- copy.updateStateObject(copyObj)
- ccopy.updateStateObject(copyObj)
- }
- // Finalise the changes on all concurrently
- finalise := func(wg *sync.WaitGroup, db *StateDB) {
- defer wg.Done()
- db.Finalise(true)
- }
- var wg sync.WaitGroup
- wg.Add(3)
- go finalise(&wg, orig)
- go finalise(&wg, copy)
- go finalise(&wg, ccopy)
- wg.Wait()
- // Verify that the three states have been updated independently
- for i := byte(0); i < 255; i++ {
- origObj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
- copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
- ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
- if want := big.NewInt(3 * int64(i)); origObj.Balance().Cmp(want) != 0 {
- t.Errorf("orig obj %d: balance mismatch: have %v, want %v", i, origObj.Balance(), want)
- }
- if want := big.NewInt(4 * int64(i)); copyObj.Balance().Cmp(want) != 0 {
- t.Errorf("copy obj %d: balance mismatch: have %v, want %v", i, copyObj.Balance(), want)
- }
- if want := big.NewInt(5 * int64(i)); ccopyObj.Balance().Cmp(want) != 0 {
- t.Errorf("copy obj %d: balance mismatch: have %v, want %v", i, ccopyObj.Balance(), want)
- }
- }
- }
- func TestSnapshotRandom(t *testing.T) {
- config := &quick.Config{MaxCount: 1000}
- err := quick.Check((*snapshotTest).run, config)
- if cerr, ok := err.(*quick.CheckError); ok {
- test := cerr.In[0].(*snapshotTest)
- t.Errorf("%v:\n%s", test.err, test)
- } else if err != nil {
- t.Error(err)
- }
- }
- // A snapshotTest checks that reverting StateDB snapshots properly undoes all changes
- // captured by the snapshot. Instances of this test with pseudorandom content are created
- // by Generate.
- //
- // The test works as follows:
- //
- // A new state is created and all actions are applied to it. Several snapshots are taken
- // in between actions. The test then reverts each snapshot. For each snapshot the actions
- // leading up to it are replayed on a fresh, empty state. The behaviour of all public
- // accessor methods on the reverted state must match the return value of the equivalent
- // methods on the replayed state.
- type snapshotTest struct {
- addrs []common.Address // all account addresses
- actions []testAction // modifications to the state
- snapshots []int // actions indexes at which snapshot is taken
- err error // failure details are reported through this field
- }
- type testAction struct {
- name string
- fn func(testAction, *StateDB)
- args []int64
- noAddr bool
- }
- // newTestAction creates a random action that changes state.
- func newTestAction(addr common.Address, r *rand.Rand) testAction {
- actions := []testAction{
- {
- name: "SetBalance",
- fn: func(a testAction, s *StateDB) {
- s.SetBalance(addr, big.NewInt(a.args[0]))
- },
- args: make([]int64, 1),
- },
- {
- name: "AddBalance",
- fn: func(a testAction, s *StateDB) {
- s.AddBalance(addr, big.NewInt(a.args[0]))
- },
- args: make([]int64, 1),
- },
- {
- name: "SetNonce",
- fn: func(a testAction, s *StateDB) {
- s.SetNonce(addr, uint64(a.args[0]))
- },
- args: make([]int64, 1),
- },
- {
- name: "SetState",
- fn: func(a testAction, s *StateDB) {
- var key, val common.Hash
- binary.BigEndian.PutUint16(key[:], uint16(a.args[0]))
- binary.BigEndian.PutUint16(val[:], uint16(a.args[1]))
- s.SetState(addr, key, val)
- },
- args: make([]int64, 2),
- },
- {
- name: "SetCode",
- fn: func(a testAction, s *StateDB) {
- code := make([]byte, 16)
- binary.BigEndian.PutUint64(code, uint64(a.args[0]))
- binary.BigEndian.PutUint64(code[8:], uint64(a.args[1]))
- s.SetCode(addr, code)
- },
- args: make([]int64, 2),
- },
- {
- name: "SetPrivacyMetadata",
- fn: func(a testAction, s *StateDB) {
- privFlag := engine.PrivacyFlagType((uint64(a.args[0])%2)*2 + 1) // the only possible values should be 1 and 3
- b := make([]byte, 8)
- binary.BigEndian.PutUint64(b, uint64(a.args[1]))
- hash := common.BytesToEncryptedPayloadHash(b)
- s.SetPrivacyMetadata(addr, &PrivacyMetadata{
- CreationTxHash: hash,
- PrivacyFlag: privFlag,
- })
- },
- args: make([]int64, 2),
- },
- {
- name: "CreateAccount",
- fn: func(a testAction, s *StateDB) {
- s.CreateAccount(addr)
- },
- },
- {
- name: "Suicide",
- fn: func(a testAction, s *StateDB) {
- s.Suicide(addr)
- },
- },
- {
- name: "AddRefund",
- fn: func(a testAction, s *StateDB) {
- s.AddRefund(uint64(a.args[0]))
- },
- args: make([]int64, 1),
- noAddr: true,
- },
- {
- name: "AddLog",
- fn: func(a testAction, s *StateDB) {
- data := make([]byte, 2)
- binary.BigEndian.PutUint16(data, uint16(a.args[0]))
- s.AddLog(&types.Log{Address: addr, Data: data})
- },
- args: make([]int64, 1),
- },
- {
- name: "AddPreimage",
- fn: func(a testAction, s *StateDB) {
- preimage := []byte{1}
- hash := common.BytesToHash(preimage)
- s.AddPreimage(hash, preimage)
- },
- args: make([]int64, 1),
- },
- {
- name: "AddAddressToAccessList",
- fn: func(a testAction, s *StateDB) {
- s.AddAddressToAccessList(addr)
- },
- },
- {
- name: "AddSlotToAccessList",
- fn: func(a testAction, s *StateDB) {
- s.AddSlotToAccessList(addr,
- common.Hash{byte(a.args[0])})
- },
- args: make([]int64, 1),
- },
- }
- action := actions[r.Intn(len(actions))]
- var nameargs []string
- if !action.noAddr {
- nameargs = append(nameargs, addr.Hex())
- }
- for i := range action.args {
- action.args[i] = rand.Int63n(100)
- nameargs = append(nameargs, fmt.Sprint(action.args[i]))
- }
- action.name += strings.Join(nameargs, ", ")
- return action
- }
- // Generate returns a new snapshot test of the given size. All randomness is
- // derived from r.
- func (*snapshotTest) Generate(r *rand.Rand, size int) reflect.Value {
- // Generate random actions.
- addrs := make([]common.Address, 50)
- for i := range addrs {
- addrs[i][0] = byte(i)
- }
- actions := make([]testAction, size)
- for i := range actions {
- addr := addrs[r.Intn(len(addrs))]
- actions[i] = newTestAction(addr, r)
- }
- // Generate snapshot indexes.
- nsnapshots := int(math.Sqrt(float64(size)))
- if size > 0 && nsnapshots == 0 {
- nsnapshots = 1
- }
- snapshots := make([]int, nsnapshots)
- snaplen := len(actions) / nsnapshots
- for i := range snapshots {
- // Try to place the snapshots some number of actions apart from each other.
- snapshots[i] = (i * snaplen) + r.Intn(snaplen)
- }
- return reflect.ValueOf(&snapshotTest{addrs, actions, snapshots, nil})
- }
- func (test *snapshotTest) String() string {
- out := new(bytes.Buffer)
- sindex := 0
- for i, action := range test.actions {
- if len(test.snapshots) > sindex && i == test.snapshots[sindex] {
- fmt.Fprintf(out, "---- snapshot %d ----\n", sindex)
- sindex++
- }
- fmt.Fprintf(out, "%4d: %s\n", i, action.name)
- }
- return out.String()
- }
- func (test *snapshotTest) run() bool {
- // Run all actions and create snapshots.
- var (
- state, _ = New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()), nil)
- snapshotRevs = make([]int, len(test.snapshots))
- sindex = 0
- )
- for i, action := range test.actions {
- if len(test.snapshots) > sindex && i == test.snapshots[sindex] {
- snapshotRevs[sindex] = state.Snapshot()
- sindex++
- }
- action.fn(action, state)
- }
- // Revert all snapshots in reverse order. Each revert must yield a state
- // that is equivalent to fresh state with all actions up the snapshot applied.
- for sindex--; sindex >= 0; sindex-- {
- checkstate, _ := New(common.Hash{}, state.Database(), nil)
- for _, action := range test.actions[:test.snapshots[sindex]] {
- action.fn(action, checkstate)
- }
- state.RevertToSnapshot(snapshotRevs[sindex])
- if err := test.checkEqual(state, checkstate); err != nil {
- test.err = fmt.Errorf("state mismatch after revert to snapshot %d\n%v", sindex, err)
- return false
- }
- }
- return true
- }
- // checkEqual checks that methods of state and checkstate return the same values.
- func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
- for _, addr := range test.addrs {
- var err error
- checkeq := func(op string, a, b interface{}) bool {
- if err == nil && !reflect.DeepEqual(a, b) {
- err = fmt.Errorf("got %s(%s) == %v, want %v", op, addr.Hex(), a, b)
- return false
- }
- return true
- }
- // Check basic accessor methods.
- checkeq("Exist", state.Exist(addr), checkstate.Exist(addr))
- checkeq("HasSuicided", state.HasSuicided(addr), checkstate.HasSuicided(addr))
- checkeq("GetBalance", state.GetBalance(addr), checkstate.GetBalance(addr))
- checkeq("GetNonce", state.GetNonce(addr), checkstate.GetNonce(addr))
- checkeq("GetCode", state.GetCode(addr), checkstate.GetCode(addr))
- checkeq("GetCodeHash", state.GetCodeHash(addr), checkstate.GetCodeHash(addr))
- checkeq("GetCodeSize", state.GetCodeSize(addr), checkstate.GetCodeSize(addr))
- statePM, _ := state.GetPrivacyMetadata(addr)
- checkStatePM, _ := checkstate.GetPrivacyMetadata(addr)
- checkeq("GetPrivacyMetadata", statePM, checkStatePM)
- // Check storage.
- if obj := state.getStateObject(addr); obj != nil {
- state.ForEachStorage(addr, func(key, value common.Hash) bool {
- return checkeq("GetState("+key.Hex()+")", checkstate.GetState(addr, key), value)
- })
- checkstate.ForEachStorage(addr, func(key, value common.Hash) bool {
- return checkeq("GetState("+key.Hex()+")", checkstate.GetState(addr, key), value)
- })
- }
- if err != nil {
- return err
- }
- }
- if state.GetRefund() != checkstate.GetRefund() {
- return fmt.Errorf("got GetRefund() == %d, want GetRefund() == %d",
- state.GetRefund(), checkstate.GetRefund())
- }
- if !reflect.DeepEqual(state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{})) {
- return fmt.Errorf("got GetLogs(common.Hash{}) == %v, want GetLogs(common.Hash{}) == %v",
- state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{}))
- }
- return nil
- }
- func TestTouchDelete(t *testing.T) {
- s := newStateTest()
- s.state.GetOrNewStateObject(common.Address{})
- root, _ := s.state.Commit(false)
- s.state, _ = New(root, s.state.db, s.state.snaps)
- snapshot := s.state.Snapshot()
- s.state.AddBalance(common.Address{}, new(big.Int))
- if len(s.state.journal.dirties) != 1 {
- t.Fatal("expected one dirty state object")
- }
- s.state.RevertToSnapshot(snapshot)
- if len(s.state.journal.dirties) != 0 {
- t.Fatal("expected no dirty state object")
- }
- }
- // TestCopyOfCopy tests that modified objects are carried over to the copy, and the copy of the copy.
- // See https://github.com/ethereum/go-ethereum/pull/15225#issuecomment-380191512
- func TestCopyOfCopy(t *testing.T) {
- state, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()), nil)
- addr := common.HexToAddress("aaaa")
- state.SetBalance(addr, big.NewInt(42))
- if got := state.Copy().GetBalance(addr).Uint64(); got != 42 {
- t.Fatalf("1st copy fail, expected 42, got %v", got)
- }
- if got := state.Copy().Copy().GetBalance(addr).Uint64(); got != 42 {
- t.Fatalf("2nd copy fail, expected 42, got %v", got)
- }
- }
- // Tests a regression where committing a copy lost some internal meta information,
- // leading to corrupted subsequent copies.
- //
- // See https://github.com/ethereum/go-ethereum/issues/20106.
- func TestCopyCommitCopy(t *testing.T) {
- state, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()), nil)
- // Create an account and check if the retrieved balance is correct
- addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe")
- skey := common.HexToHash("aaa")
- sval := common.HexToHash("bbb")
- state.SetBalance(addr, big.NewInt(42)) // Change the account trie
- state.SetCode(addr, []byte("hello")) // Change an external metadata
- state.SetState(addr, skey, sval) // Change the storage trie
- if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
- t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42)
- }
- if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
- t.Fatalf("initial code mismatch: have %x, want %x", code, []byte("hello"))
- }
- if val := state.GetState(addr, skey); val != sval {
- t.Fatalf("initial non-committed storage slot mismatch: have %x, want %x", val, sval)
- }
- if val := state.GetCommittedState(addr, skey); val != (common.Hash{}) {
- t.Fatalf("initial committed storage slot mismatch: have %x, want %x", val, common.Hash{})
- }
- // Copy the non-committed state database and check pre/post commit balance
- copyOne := state.Copy()
- if balance := copyOne.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
- t.Fatalf("first copy pre-commit balance mismatch: have %v, want %v", balance, 42)
- }
- if code := copyOne.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
- t.Fatalf("first copy pre-commit code mismatch: have %x, want %x", code, []byte("hello"))
- }
- if val := copyOne.GetState(addr, skey); val != sval {
- t.Fatalf("first copy pre-commit non-committed storage slot mismatch: have %x, want %x", val, sval)
- }
- if val := copyOne.GetCommittedState(addr, skey); val != (common.Hash{}) {
- t.Fatalf("first copy pre-commit committed storage slot mismatch: have %x, want %x", val, common.Hash{})
- }
- copyOne.Commit(false)
- if balance := copyOne.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
- t.Fatalf("first copy post-commit balance mismatch: have %v, want %v", balance, 42)
- }
- if code := copyOne.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
- t.Fatalf("first copy post-commit code mismatch: have %x, want %x", code, []byte("hello"))
- }
- if val := copyOne.GetState(addr, skey); val != sval {
- t.Fatalf("first copy post-commit non-committed storage slot mismatch: have %x, want %x", val, sval)
- }
- if val := copyOne.GetCommittedState(addr, skey); val != sval {
- t.Fatalf("first copy post-commit committed storage slot mismatch: have %x, want %x", val, sval)
- }
- // Copy the copy and check the balance once more
- copyTwo := copyOne.Copy()
- if balance := copyTwo.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
- t.Fatalf("second copy balance mismatch: have %v, want %v", balance, 42)
- }
- if code := copyTwo.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
- t.Fatalf("second copy code mismatch: have %x, want %x", code, []byte("hello"))
- }
- if val := copyTwo.GetState(addr, skey); val != sval {
- t.Fatalf("second copy non-committed storage slot mismatch: have %x, want %x", val, sval)
- }
- if val := copyTwo.GetCommittedState(addr, skey); val != sval {
- t.Fatalf("second copy post-commit committed storage slot mismatch: have %x, want %x", val, sval)
- }
- }
- // Tests a regression where committing a copy lost some internal meta information,
- // leading to corrupted subsequent copies.
- //
- // See https://github.com/ethereum/go-ethereum/issues/20106.
- func TestCopyCopyCommitCopy(t *testing.T) {
- state, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()), nil)
- // Create an account and check if the retrieved balance is correct
- addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe")
- skey := common.HexToHash("aaa")
- sval := common.HexToHash("bbb")
- state.SetBalance(addr, big.NewInt(42)) // Change the account trie
- state.SetCode(addr, []byte("hello")) // Change an external metadata
- state.SetState(addr, skey, sval) // Change the storage trie
- if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
- t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42)
- }
- if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
- t.Fatalf("initial code mismatch: have %x, want %x", code, []byte("hello"))
- }
- if val := state.GetState(addr, skey); val != sval {
- t.Fatalf("initial non-committed storage slot mismatch: have %x, want %x", val, sval)
- }
- if val := state.GetCommittedState(addr, skey); val != (common.Hash{}) {
- t.Fatalf("initial committed storage slot mismatch: have %x, want %x", val, common.Hash{})
- }
- // Copy the non-committed state database and check pre/post commit balance
- copyOne := state.Copy()
- if balance := copyOne.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
- t.Fatalf("first copy balance mismatch: have %v, want %v", balance, 42)
- }
- if code := copyOne.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
- t.Fatalf("first copy code mismatch: have %x, want %x", code, []byte("hello"))
- }
- if val := copyOne.GetState(addr, skey); val != sval {
- t.Fatalf("first copy non-committed storage slot mismatch: have %x, want %x", val, sval)
- }
- if val := copyOne.GetCommittedState(addr, skey); val != (common.Hash{}) {
- t.Fatalf("first copy committed storage slot mismatch: have %x, want %x", val, common.Hash{})
- }
- // Copy the copy and check the balance once more
- copyTwo := copyOne.Copy()
- if balance := copyTwo.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
- t.Fatalf("second copy pre-commit balance mismatch: have %v, want %v", balance, 42)
- }
- if code := copyTwo.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
- t.Fatalf("second copy pre-commit code mismatch: have %x, want %x", code, []byte("hello"))
- }
- if val := copyTwo.GetState(addr, skey); val != sval {
- t.Fatalf("second copy pre-commit non-committed storage slot mismatch: have %x, want %x", val, sval)
- }
- if val := copyTwo.GetCommittedState(addr, skey); val != (common.Hash{}) {
- t.Fatalf("second copy pre-commit committed storage slot mismatch: have %x, want %x", val, common.Hash{})
- }
- copyTwo.Commit(false)
- if balance := copyTwo.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
- t.Fatalf("second copy post-commit balance mismatch: have %v, want %v", balance, 42)
- }
- if code := copyTwo.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
- t.Fatalf("second copy post-commit code mismatch: have %x, want %x", code, []byte("hello"))
- }
- if val := copyTwo.GetState(addr, skey); val != sval {
- t.Fatalf("second copy post-commit non-committed storage slot mismatch: have %x, want %x", val, sval)
- }
- if val := copyTwo.GetCommittedState(addr, skey); val != sval {
- t.Fatalf("second copy post-commit committed storage slot mismatch: have %x, want %x", val, sval)
- }
- // Copy the copy-copy and check the balance once more
- copyThree := copyTwo.Copy()
- if balance := copyThree.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
- t.Fatalf("third copy balance mismatch: have %v, want %v", balance, 42)
- }
- if code := copyThree.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
- t.Fatalf("third copy code mismatch: have %x, want %x", code, []byte("hello"))
- }
- if val := copyThree.GetState(addr, skey); val != sval {
- t.Fatalf("third copy non-committed storage slot mismatch: have %x, want %x", val, sval)
- }
- if val := copyThree.GetCommittedState(addr, skey); val != sval {
- t.Fatalf("third copy committed storage slot mismatch: have %x, want %x", val, sval)
- }
- }
- // TestDeleteCreateRevert tests a weird state transition corner case that we hit
- // while changing the internals of StateDB. The workflow is that a contract is
- // self-destructed, then in a follow-up transaction (but same block) it's created
- // again and the transaction reverted.
- //
- // The original StateDB implementation flushed dirty objects to the tries after
- // each transaction, so this works ok. The rework accumulated writes in memory
- // first, but the journal wiped the entire state object on create-revert.
- func TestDeleteCreateRevert(t *testing.T) {
- // Create an initial state with a single contract
- state, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()), nil)
- addr := common.BytesToAddress([]byte("so"))
- state.SetBalance(addr, big.NewInt(1))
- root, _ := state.Commit(false)
- state, _ = New(root, state.db, state.snaps)
- // Simulate self-destructing in one transaction, then create-reverting in another
- state.Suicide(addr)
- state.Finalise(true)
- id := state.Snapshot()
- state.SetBalance(addr, big.NewInt(2))
- state.RevertToSnapshot(id)
- // Commit the entire state and make sure we don't crash and have the correct state
- root, _ = state.Commit(true)
- state, _ = New(root, state.db, state.snaps)
- if state.getStateObject(addr) != nil {
- t.Fatalf("self-destructed contract came alive")
- }
- }
- // TestMissingTrieNodes tests that if the StateDB fails to load parts of the trie,
- // the Commit operation fails with an error
- // If we are missing trie nodes, we should not continue writing to the trie
- func TestMissingTrieNodes(t *testing.T) {
- // Create an initial state with a few accounts
- memDb := rawdb.NewMemoryDatabase()
- db := NewDatabase(memDb)
- var root common.Hash
- state, _ := New(common.Hash{}, db, nil)
- addr := common.BytesToAddress([]byte("so"))
- {
- state.SetBalance(addr, big.NewInt(1))
- state.SetCode(addr, []byte{1, 2, 3})
- a2 := common.BytesToAddress([]byte("another"))
- state.SetBalance(a2, big.NewInt(100))
- state.SetCode(a2, []byte{1, 2, 4})
- root, _ = state.Commit(false)
- t.Logf("root: %x", root)
- // force-flush
- state.Database().TrieDB().Cap(0)
- }
- // Create a new state on the old root
- state, _ = New(root, db, nil)
- // Now we clear out the memdb
- it := memDb.NewIterator(nil, nil)
- for it.Next() {
- k := it.Key()
- // Leave the root intact
- if !bytes.Equal(k, root[:]) {
- t.Logf("key: %x", k)
- memDb.Delete(k)
- }
- }
- balance := state.GetBalance(addr)
- // The removed elem should lead to it returning zero balance
- if exp, got := uint64(0), balance.Uint64(); got != exp {
- t.Errorf("expected %d, got %d", exp, got)
- }
- // Modify the state
- state.SetBalance(addr, big.NewInt(2))
- root, err := state.Commit(false)
- if err == nil {
- t.Fatalf("expected error, got root :%x", root)
- }
- }
- func TestStateDBAccessList(t *testing.T) {
- // Some helpers
- addr := func(a string) common.Address {
- return common.HexToAddress(a)
- }
- slot := func(a string) common.Hash {
- return common.HexToHash(a)
- }
- memDb := rawdb.NewMemoryDatabase()
- db := NewDatabase(memDb)
- state, _ := New(common.Hash{}, db, nil)
- state.accessList = newAccessList()
- verifyAddrs := func(astrings ...string) {
- t.Helper()
- // convert to common.Address form
- var addresses []common.Address
- var addressMap = make(map[common.Address]struct{})
- for _, astring := range astrings {
- address := addr(astring)
- addresses = append(addresses, address)
- addressMap[address] = struct{}{}
- }
- // Check that the given addresses are in the access list
- for _, address := range addresses {
- if !state.AddressInAccessList(address) {
- t.Fatalf("expected %x to be in access list", address)
- }
- }
- // Check that only the expected addresses are present in the acesslist
- for address := range state.accessList.addresses {
- if _, exist := addressMap[address]; !exist {
- t.Fatalf("extra address %x in access list", address)
- }
- }
- }
- verifySlots := func(addrString string, slotStrings ...string) {
- if !state.AddressInAccessList(addr(addrString)) {
- t.Fatalf("scope missing address/slots %v", addrString)
- }
- var address = addr(addrString)
- // convert to common.Hash form
- var slots []common.Hash
- var slotMap = make(map[common.Hash]struct{})
- for _, slotString := range slotStrings {
- s := slot(slotString)
- slots = append(slots, s)
- slotMap[s] = struct{}{}
- }
- // Check that the expected items are in the access list
- for i, s := range slots {
- if _, slotPresent := state.SlotInAccessList(address, s); !slotPresent {
- t.Fatalf("input %d: scope missing slot %v (address %v)", i, s, addrString)
- }
- }
- // Check that no extra elements are in the access list
- index := state.accessList.addresses[address]
- if index >= 0 {
- stateSlots := state.accessList.slots[index]
- for s := range stateSlots {
- if _, slotPresent := slotMap[s]; !slotPresent {
- t.Fatalf("scope has extra slot %v (address %v)", s, addrString)
- }
- }
- }
- }
- state.AddAddressToAccessList(addr("aa")) // 1
- state.AddSlotToAccessList(addr("bb"), slot("01")) // 2,3
- state.AddSlotToAccessList(addr("bb"), slot("02")) // 4
- verifyAddrs("aa", "bb")
- verifySlots("bb", "01", "02")
- // Make a copy
- stateCopy1 := state.Copy()
- if exp, got := 4, state.journal.length(); exp != got {
- t.Fatalf("journal length mismatch: have %d, want %d", got, exp)
- }
- // same again, should cause no journal entries
- state.AddSlotToAccessList(addr("bb"), slot("01"))
- state.AddSlotToAccessList(addr("bb"), slot("02"))
- state.AddAddressToAccessList(addr("aa"))
- if exp, got := 4, state.journal.length(); exp != got {
- t.Fatalf("journal length mismatch: have %d, want %d", got, exp)
- }
- // some new ones
- state.AddSlotToAccessList(addr("bb"), slot("03")) // 5
- state.AddSlotToAccessList(addr("aa"), slot("01")) // 6
- state.AddSlotToAccessList(addr("cc"), slot("01")) // 7,8
- state.AddAddressToAccessList(addr("cc"))
- if exp, got := 8, state.journal.length(); exp != got {
- t.Fatalf("journal length mismatch: have %d, want %d", got, exp)
- }
- verifyAddrs("aa", "bb", "cc")
- verifySlots("aa", "01")
- verifySlots("bb", "01", "02", "03")
- verifySlots("cc", "01")
- // now start rolling back changes
- state.journal.revert(state, 7)
- if _, ok := state.SlotInAccessList(addr("cc"), slot("01")); ok {
- t.Fatalf("slot present, expected missing")
- }
- verifyAddrs("aa", "bb", "cc")
- verifySlots("aa", "01")
- verifySlots("bb", "01", "02", "03")
- state.journal.revert(state, 6)
- if state.AddressInAccessList(addr("cc")) {
- t.Fatalf("addr present, expected missing")
- }
- verifyAddrs("aa", "bb")
- verifySlots("aa", "01")
- verifySlots("bb", "01", "02", "03")
- state.journal.revert(state, 5)
- if _, ok := state.SlotInAccessList(addr("aa"), slot("01")); ok {
- t.Fatalf("slot present, expected missing")
- }
- verifyAddrs("aa", "bb")
- verifySlots("bb", "01", "02", "03")
- state.journal.revert(state, 4)
- if _, ok := state.SlotInAccessList(addr("bb"), slot("03")); ok {
- t.Fatalf("slot present, expected missing")
- }
- verifyAddrs("aa", "bb")
- verifySlots("bb", "01", "02")
- state.journal.revert(state, 3)
- if _, ok := state.SlotInAccessList(addr("bb"), slot("02")); ok {
- t.Fatalf("slot present, expected missing")
- }
- verifyAddrs("aa", "bb")
- verifySlots("bb", "01")
- state.journal.revert(state, 2)
- if _, ok := state.SlotInAccessList(addr("bb"), slot("01")); ok {
- t.Fatalf("slot present, expected missing")
- }
- verifyAddrs("aa", "bb")
- state.journal.revert(state, 1)
- if state.AddressInAccessList(addr("bb")) {
- t.Fatalf("addr present, expected missing")
- }
- verifyAddrs("aa")
- state.journal.revert(state, 0)
- if state.AddressInAccessList(addr("aa")) {
- t.Fatalf("addr present, expected missing")
- }
- if got, exp := len(state.accessList.addresses), 0; got != exp {
- t.Fatalf("expected empty, got %d", got)
- }
- if got, exp := len(state.accessList.slots), 0; got != exp {
- t.Fatalf("expected empty, got %d", got)
- }
- // Check the copy
- // Make a copy
- state = stateCopy1
- verifyAddrs("aa", "bb")
- verifySlots("bb", "01", "02")
- if got, exp := len(state.accessList.addresses), 2; got != exp {
- t.Fatalf("expected empty, got %d", got)
- }
- if got, exp := len(state.accessList.slots), 1; got != exp {
- t.Fatalf("expected empty, got %d", got)
- }
- }
- // Quorum
- func TestStorageRoot(t *testing.T) {
- var (
- mem = rawdb.NewMemoryDatabase()
- db = NewDatabase(mem)
- state, _ = New(common.Hash{}, db, nil)
- addr = common.Address{1}
- key = common.Hash{1}
- value = common.Hash{42}
- empty = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
- )
- so := state.GetOrNewStateObject(addr)
- emptyRoot := so.storageRoot(db)
- if emptyRoot != empty {
- t.Errorf("Invalid empty storage root, expected %x, got %x", empty, emptyRoot)
- }
- // add a bit of state
- so.SetState(db, key, value)
- state.Commit(false)
- root := so.storageRoot(db)
- expected := common.HexToHash("63511abd258fa907afa30cb118b53744a4f49055bb3f531da512c6b866fc2ffb")
- if expected != root {
- t.Errorf("Invalid storage root, expected %x, got %x", expected, root)
- }
- }
- // End Quorum
- // Quorum - Privacy Enhancements
- func TestPrivacyMetadataIsSavedOnStateDbCommit(t *testing.T) {
- ethDb := rawdb.NewMemoryDatabase()
- stateDb := NewDatabase(ethDb)
- state, _ := New(common.Hash{}, stateDb, nil)
- addr := common.Address{1}
- state.CreateAccount(addr)
- state.SetNonce(addr, uint64(1))
- state.SetPrivacyMetadata(addr, &PrivacyMetadata{
- PrivacyFlag: engine.PrivacyFlagPartyProtection,
- CreationTxHash: common.EncryptedPayloadHash{1},
- })
- privMetaData, _ := state.GetCommittedStatePrivacyMetadata(addr)
- if privMetaData != nil {
- t.Errorf("privacy metadata should not have been stored before commit")
- }
- state.Commit(false)
- privMetaData, _ = state.GetCommittedStatePrivacyMetadata(addr)
- if privMetaData == nil {
- t.Errorf("privacy metadata should have been stored during commit")
- }
- }
- func TestPrivacyMetadataIsUpdatedOnAccountReCreateWithDifferentPrivacyMetadata(t *testing.T) {
- ethDb := rawdb.NewMemoryDatabase()
- stateDb := NewDatabase(ethDb)
- state, _ := New(common.Hash{}, stateDb, nil)
- addr := common.Address{1}
- state.CreateAccount(addr)
- state.SetNonce(addr, uint64(1))
- state.SetPrivacyMetadata(addr, &PrivacyMetadata{
- PrivacyFlag: engine.PrivacyFlagPartyProtection,
- CreationTxHash: common.EncryptedPayloadHash{1},
- })
- state.Commit(false)
- privMetaData, _ := state.GetCommittedStatePrivacyMetadata(addr)
- if privMetaData == nil {
- t.Errorf("privacy metadata should have been stored during commit")
- }
- state.CreateAccount(addr)
- state.SetNonce(addr, uint64(1))
- state.SetPrivacyMetadata(addr, &PrivacyMetadata{
- PrivacyFlag: engine.PrivacyFlagStateValidation,
- CreationTxHash: common.EncryptedPayloadHash{1},
- })
- state.Commit(false)
- privMetaData, _ = state.GetCommittedStatePrivacyMetadata(addr)
- if privMetaData == nil {
- t.Errorf("privacy metadata should have been updated during commit")
- } else if privMetaData.PrivacyFlag != engine.PrivacyFlagStateValidation {
- t.Errorf("privacy metadata should have StateValidation as the the privacy flag")
- }
- }
- func TestPrivacyMetadataIsRemovedOnAccountSuicide(t *testing.T) {
- ethDb := rawdb.NewMemoryDatabase()
- stateDb := NewDatabase(ethDb)
- state, _ := New(common.Hash{}, stateDb, nil)
- addr := common.Address{1}
- state.CreateAccount(addr)
- state.SetNonce(addr, uint64(1))
- state.SetPrivacyMetadata(addr, &PrivacyMetadata{
- PrivacyFlag: engine.PrivacyFlagPartyProtection,
- CreationTxHash: common.EncryptedPayloadHash{1},
- })
- state.Commit(false)
- privMetaData, _ := state.GetCommittedStatePrivacyMetadata(addr)
- if privMetaData == nil {
- t.Errorf("privacy metadata should have been stored during commit")
- }
- state.Suicide(addr)
- state.Commit(false)
- privMetaData, _ = state.GetCommittedStatePrivacyMetadata(addr)
- if privMetaData != nil {
- t.Errorf("privacy metadata should have been deleted during account suicide")
- }
- }
- func TestPrivacyMetadataChangesAreRolledBackOnRevert(t *testing.T) {
- ethDb := rawdb.NewMemoryDatabase()
- stateDb := NewDatabase(ethDb)
- state, _ := New(common.Hash{}, stateDb, nil)
- addr := common.Address{1}
- state.CreateAccount(addr)
- state.SetNonce(addr, uint64(1))
- state.SetPrivacyMetadata(addr, &PrivacyMetadata{
- PrivacyFlag: engine.PrivacyFlagPartyProtection,
- CreationTxHash: common.BytesToEncryptedPayloadHash([]byte("one")),
- })
- state.Commit(false)
- privMetaData, _ := state.GetCommittedStatePrivacyMetadata(addr)
- if privMetaData == nil {
- t.Errorf("privacy metadata should have been stored during commit")
- }
- // update privacy metadata
- state.SetPrivacyMetadata(addr, &PrivacyMetadata{
- PrivacyFlag: engine.PrivacyFlagStateValidation,
- CreationTxHash: common.BytesToEncryptedPayloadHash([]byte("two")),
- })
- // record the snapshot
- snapshot := state.Snapshot()
- privMetaData, _ = state.GetPrivacyMetadata(addr)
- if privMetaData.CreationTxHash != common.BytesToEncryptedPayloadHash([]byte("two")) {
- t.Errorf("current privacy metadata creation tx hash does not match the expected value")
- }
- // update the metadata
- state.SetPrivacyMetadata(addr, &PrivacyMetadata{
- PrivacyFlag: engine.PrivacyFlagStateValidation,
- CreationTxHash: common.BytesToEncryptedPayloadHash([]byte("three")),
- })
- privMetaData, _ = state.GetPrivacyMetadata(addr)
- if privMetaData.CreationTxHash != common.BytesToEncryptedPayloadHash([]byte("three")) {
- t.Errorf("current privacy metadata creation tx hash does not match the expected value")
- }
- // revert to snapshot
- state.RevertToSnapshot(snapshot)
- privMetaData, _ = state.GetPrivacyMetadata(addr)
- if privMetaData.CreationTxHash != common.BytesToEncryptedPayloadHash([]byte("two")) {
- t.Errorf("current privacy metadata creation tx hash does not match the expected value")
- }
- }
- // End Quorum - Privacy Enhancements
|