github.com/ethereum/go-ethereum@v1.16.1/core/state/statedb_fuzz_test.go (about)

     1  // Copyright 2023 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  	"maps"
    25  	"math"
    26  	"math/rand"
    27  	"reflect"
    28  	"strings"
    29  	"testing"
    30  	"testing/quick"
    31  
    32  	"github.com/ethereum/go-ethereum/common"
    33  	"github.com/ethereum/go-ethereum/core/rawdb"
    34  	"github.com/ethereum/go-ethereum/core/state/snapshot"
    35  	"github.com/ethereum/go-ethereum/core/tracing"
    36  	"github.com/ethereum/go-ethereum/core/types"
    37  	"github.com/ethereum/go-ethereum/crypto"
    38  	"github.com/ethereum/go-ethereum/rlp"
    39  	"github.com/ethereum/go-ethereum/trie"
    40  	"github.com/ethereum/go-ethereum/triedb"
    41  	"github.com/ethereum/go-ethereum/triedb/pathdb"
    42  	"github.com/holiman/uint256"
    43  )
    44  
    45  // A stateTest checks that the state changes are correctly captured. Instances
    46  // of this test with pseudorandom content are created by Generate.
    47  //
    48  // The test works as follows:
    49  //
    50  // A list of states are created by applying actions. The state changes between
    51  // each state instance are tracked and be verified.
    52  type stateTest struct {
    53  	addrs   []common.Address // all account addresses
    54  	actions [][]testAction   // modifications to the state, grouped by block
    55  	chunk   int              // The number of actions per chunk
    56  	err     error            // failure details are reported through this field
    57  }
    58  
    59  // newStateTestAction creates a random action that changes state.
    60  func newStateTestAction(addr common.Address, r *rand.Rand, index int) testAction {
    61  	actions := []testAction{
    62  		{
    63  			name: "SetBalance",
    64  			fn: func(a testAction, s *StateDB) {
    65  				s.SetBalance(addr, uint256.NewInt(uint64(a.args[0])), tracing.BalanceChangeUnspecified)
    66  			},
    67  			args: make([]int64, 1),
    68  		},
    69  		{
    70  			name: "SetNonce",
    71  			fn: func(a testAction, s *StateDB) {
    72  				s.SetNonce(addr, uint64(a.args[0]), tracing.NonceChangeUnspecified)
    73  			},
    74  			args: make([]int64, 1),
    75  		},
    76  		{
    77  			name: "SetStorage",
    78  			fn: func(a testAction, s *StateDB) {
    79  				var key, val common.Hash
    80  				binary.BigEndian.PutUint16(key[:], uint16(a.args[0]))
    81  				binary.BigEndian.PutUint16(val[:], uint16(a.args[1]))
    82  				s.SetState(addr, key, val)
    83  			},
    84  			args: make([]int64, 2),
    85  		},
    86  		{
    87  			name: "SetCode",
    88  			fn: func(a testAction, s *StateDB) {
    89  				code := make([]byte, 16)
    90  				binary.BigEndian.PutUint64(code, uint64(a.args[0]))
    91  				binary.BigEndian.PutUint64(code[8:], uint64(a.args[1]))
    92  				s.SetCode(addr, code)
    93  			},
    94  			args: make([]int64, 2),
    95  		},
    96  		{
    97  			name: "CreateAccount",
    98  			fn: func(a testAction, s *StateDB) {
    99  				if !s.Exist(addr) {
   100  					s.CreateAccount(addr)
   101  				}
   102  			},
   103  		},
   104  		{
   105  			name: "Selfdestruct",
   106  			fn: func(a testAction, s *StateDB) {
   107  				s.SelfDestruct(addr)
   108  			},
   109  		},
   110  	}
   111  	var nonRandom = index != -1
   112  	if index == -1 {
   113  		index = r.Intn(len(actions))
   114  	}
   115  	action := actions[index]
   116  	var names []string
   117  	if !action.noAddr {
   118  		names = append(names, addr.Hex())
   119  	}
   120  	for i := range action.args {
   121  		if nonRandom {
   122  			action.args[i] = rand.Int63n(10000) + 1 // set balance to non-zero
   123  		} else {
   124  			action.args[i] = rand.Int63n(10000)
   125  		}
   126  		names = append(names, fmt.Sprint(action.args[i]))
   127  	}
   128  	action.name += " " + strings.Join(names, ", ")
   129  	return action
   130  }
   131  
   132  // Generate returns a new snapshot test of the given size. All randomness is
   133  // derived from r.
   134  func (*stateTest) Generate(r *rand.Rand, size int) reflect.Value {
   135  	addrs := make([]common.Address, 5)
   136  	for i := range addrs {
   137  		addrs[i][0] = byte(i)
   138  	}
   139  	actions := make([][]testAction, rand.Intn(5)+1)
   140  
   141  	for i := 0; i < len(actions); i++ {
   142  		actions[i] = make([]testAction, size)
   143  		for j := range actions[i] {
   144  			if j == 0 {
   145  				// Always include a set balance action to make sure
   146  				// the state changes are not empty.
   147  				actions[i][j] = newStateTestAction(common.HexToAddress("0xdeadbeef"), r, 0)
   148  				continue
   149  			}
   150  			actions[i][j] = newStateTestAction(addrs[r.Intn(len(addrs))], r, -1)
   151  		}
   152  	}
   153  	chunk := int(math.Sqrt(float64(size)))
   154  	if size > 0 && chunk == 0 {
   155  		chunk = 1
   156  	}
   157  	return reflect.ValueOf(&stateTest{
   158  		addrs:   addrs,
   159  		actions: actions,
   160  		chunk:   chunk,
   161  	})
   162  }
   163  
   164  func (test *stateTest) String() string {
   165  	out := new(bytes.Buffer)
   166  	for i, actions := range test.actions {
   167  		fmt.Fprintf(out, "---- block %d ----\n", i)
   168  		for j, action := range actions {
   169  			if j%test.chunk == 0 {
   170  				fmt.Fprintf(out, "---- transaction %d ----\n", j/test.chunk)
   171  			}
   172  			fmt.Fprintf(out, "%4d: %s\n", j%test.chunk, action.name)
   173  		}
   174  	}
   175  	return out.String()
   176  }
   177  
   178  func (test *stateTest) run() bool {
   179  	var (
   180  		roots         []common.Hash
   181  		accounts      []map[common.Hash][]byte
   182  		accountOrigin []map[common.Address][]byte
   183  		storages      []map[common.Hash]map[common.Hash][]byte
   184  		storageOrigin []map[common.Address]map[common.Hash][]byte
   185  		copyUpdate    = func(update *stateUpdate) {
   186  			accounts = append(accounts, maps.Clone(update.accounts))
   187  			accountOrigin = append(accountOrigin, maps.Clone(update.accountsOrigin))
   188  			storages = append(storages, maps.Clone(update.storages))
   189  			storageOrigin = append(storageOrigin, maps.Clone(update.storagesOrigin))
   190  		}
   191  		disk      = rawdb.NewMemoryDatabase()
   192  		tdb       = triedb.NewDatabase(disk, &triedb.Config{PathDB: pathdb.Defaults})
   193  		byzantium = rand.Intn(2) == 0
   194  	)
   195  	defer disk.Close()
   196  	defer tdb.Close()
   197  
   198  	var snaps *snapshot.Tree
   199  	if rand.Intn(3) == 0 {
   200  		snaps, _ = snapshot.New(snapshot.Config{
   201  			CacheSize:  1,
   202  			Recovery:   false,
   203  			NoBuild:    false,
   204  			AsyncBuild: false,
   205  		}, disk, tdb, types.EmptyRootHash)
   206  	}
   207  	for i, actions := range test.actions {
   208  		root := types.EmptyRootHash
   209  		if i != 0 {
   210  			root = roots[len(roots)-1]
   211  		}
   212  		state, err := New(root, NewDatabase(tdb, snaps))
   213  		if err != nil {
   214  			panic(err)
   215  		}
   216  		for i, action := range actions {
   217  			if i%test.chunk == 0 && i != 0 {
   218  				if byzantium {
   219  					state.Finalise(true) // call finalise at the transaction boundary
   220  				} else {
   221  					state.IntermediateRoot(true) // call intermediateRoot at the transaction boundary
   222  				}
   223  			}
   224  			action.fn(action, state)
   225  		}
   226  		if byzantium {
   227  			state.Finalise(true) // call finalise at the transaction boundary
   228  		} else {
   229  			state.IntermediateRoot(true) // call intermediateRoot at the transaction boundary
   230  		}
   231  		ret, err := state.commitAndFlush(0, true, false) // call commit at the block boundary
   232  		if err != nil {
   233  			panic(err)
   234  		}
   235  		if ret.empty() {
   236  			return true
   237  		}
   238  		copyUpdate(ret)
   239  		roots = append(roots, ret.root)
   240  	}
   241  	for i := 0; i < len(test.actions); i++ {
   242  		root := types.EmptyRootHash
   243  		if i != 0 {
   244  			root = roots[i-1]
   245  		}
   246  		test.err = test.verify(root, roots[i], tdb, accounts[i], accountOrigin[i], storages[i], storageOrigin[i])
   247  		if test.err != nil {
   248  			return false
   249  		}
   250  	}
   251  	return true
   252  }
   253  
   254  // verifyAccountCreation this function is called once the state diff says that
   255  // specific account was not present. A serial of checks will be performed to
   256  // ensure the state diff is correct, includes:
   257  //
   258  // - the account was indeed not present in trie
   259  // - the account is present in new trie, nil->nil is regarded as invalid
   260  // - the slots transition is correct
   261  func (test *stateTest) verifyAccountCreation(next common.Hash, db *triedb.Database, otr, ntr *trie.Trie, addr common.Address, account []byte, storages map[common.Hash][]byte, storagesOrigin map[common.Hash][]byte) error {
   262  	// Verify account change
   263  	addrHash := crypto.Keccak256Hash(addr.Bytes())
   264  	oBlob, err := otr.Get(addrHash.Bytes())
   265  	if err != nil {
   266  		return err
   267  	}
   268  	nBlob, err := ntr.Get(addrHash.Bytes())
   269  	if err != nil {
   270  		return err
   271  	}
   272  	if len(oBlob) != 0 {
   273  		return fmt.Errorf("unexpected account in old trie, %x", addrHash)
   274  	}
   275  	if len(nBlob) == 0 {
   276  		return fmt.Errorf("missing account in new trie, %x", addrHash)
   277  	}
   278  	full, err := types.FullAccountRLP(account)
   279  	if err != nil {
   280  		return err
   281  	}
   282  	if !bytes.Equal(nBlob, full) {
   283  		return fmt.Errorf("unexpected account data, want: %v, got: %v", full, nBlob)
   284  	}
   285  
   286  	// Verify storage changes
   287  	var nAcct types.StateAccount
   288  	if err := rlp.DecodeBytes(nBlob, &nAcct); err != nil {
   289  		return err
   290  	}
   291  	// Account has no slot, empty slot set is expected
   292  	if nAcct.Root == types.EmptyRootHash {
   293  		if len(storagesOrigin) != 0 {
   294  			return fmt.Errorf("unexpected slot changes %x", addrHash)
   295  		}
   296  		if len(storages) != 0 {
   297  			return fmt.Errorf("unexpected slot changes %x", addrHash)
   298  		}
   299  		return nil
   300  	}
   301  	// Account has slots, ensure all new slots are contained
   302  	st, err := trie.New(trie.StorageTrieID(next, addrHash, nAcct.Root), db)
   303  	if err != nil {
   304  		return err
   305  	}
   306  	for key, val := range storagesOrigin {
   307  		if _, exist := storages[key]; !exist {
   308  			return errors.New("storage data is not found")
   309  		}
   310  		got, err := st.Get(key.Bytes())
   311  		if err != nil {
   312  			return err
   313  		}
   314  		if !bytes.Equal(got, storages[key]) {
   315  			return fmt.Errorf("unexpected storage data, want: %v, got: %v", storages[key], got)
   316  		}
   317  		st.Update(key.Bytes(), val)
   318  	}
   319  	if len(storagesOrigin) != len(storages) {
   320  		return fmt.Errorf("extra storage found, want: %d, got: %d", len(storagesOrigin), len(storages))
   321  	}
   322  	if st.Hash() != types.EmptyRootHash {
   323  		return errors.New("invalid slot changes")
   324  	}
   325  	return nil
   326  }
   327  
   328  // verifyAccountUpdate this function is called once the state diff says that
   329  // specific account was present. A serial of checks will be performed to
   330  // ensure the state diff is correct, includes:
   331  //
   332  // - the account was indeed present in trie
   333  // - the account in old trie matches the provided value
   334  // - the slots transition is correct
   335  func (test *stateTest) verifyAccountUpdate(next common.Hash, db *triedb.Database, otr, ntr *trie.Trie, addr common.Address, account []byte, accountOrigin []byte, storages map[common.Hash][]byte, storageOrigin map[common.Hash][]byte) error {
   336  	// Verify account change
   337  	addrHash := crypto.Keccak256Hash(addr.Bytes())
   338  	oBlob, err := otr.Get(addrHash.Bytes())
   339  	if err != nil {
   340  		return err
   341  	}
   342  	nBlob, err := ntr.Get(addrHash.Bytes())
   343  	if err != nil {
   344  		return err
   345  	}
   346  	if len(oBlob) == 0 {
   347  		return fmt.Errorf("missing account in old trie, %x", addrHash)
   348  	}
   349  	full, err := types.FullAccountRLP(accountOrigin)
   350  	if err != nil {
   351  		return err
   352  	}
   353  	if !bytes.Equal(full, oBlob) {
   354  		return fmt.Errorf("account value is not matched, %x", addrHash)
   355  	}
   356  	if len(nBlob) == 0 {
   357  		if len(account) != 0 {
   358  			return errors.New("unexpected account data")
   359  		}
   360  	} else {
   361  		full, _ = types.FullAccountRLP(account)
   362  		if !bytes.Equal(full, nBlob) {
   363  			return fmt.Errorf("unexpected account data, %x, want %v, got: %v", addrHash, full, nBlob)
   364  		}
   365  	}
   366  	// Decode accounts
   367  	var (
   368  		oAcct types.StateAccount
   369  		nAcct types.StateAccount
   370  		nRoot common.Hash
   371  	)
   372  	if err := rlp.DecodeBytes(oBlob, &oAcct); err != nil {
   373  		return err
   374  	}
   375  	if len(nBlob) == 0 {
   376  		nRoot = types.EmptyRootHash
   377  	} else {
   378  		if err := rlp.DecodeBytes(nBlob, &nAcct); err != nil {
   379  			return err
   380  		}
   381  		nRoot = nAcct.Root
   382  	}
   383  
   384  	// Verify storage
   385  	st, err := trie.New(trie.StorageTrieID(next, addrHash, nRoot), db)
   386  	if err != nil {
   387  		return err
   388  	}
   389  	for key, val := range storageOrigin {
   390  		if _, exist := storages[key]; !exist {
   391  			return errors.New("storage data is not found")
   392  		}
   393  		got, err := st.Get(key.Bytes())
   394  		if err != nil {
   395  			return err
   396  		}
   397  		if !bytes.Equal(got, storages[key]) {
   398  			return fmt.Errorf("unexpected storage data, want: %v, got: %v", storages[key], got)
   399  		}
   400  		st.Update(key.Bytes(), val)
   401  	}
   402  	if len(storageOrigin) != len(storages) {
   403  		return fmt.Errorf("extra storage found, want: %d, got: %d", len(storageOrigin), len(storages))
   404  	}
   405  	if st.Hash() != oAcct.Root {
   406  		return errors.New("invalid slot changes")
   407  	}
   408  	return nil
   409  }
   410  
   411  func (test *stateTest) verify(root common.Hash, next common.Hash, db *triedb.Database, accounts map[common.Hash][]byte, accountsOrigin map[common.Address][]byte, storages map[common.Hash]map[common.Hash][]byte, storagesOrigin map[common.Address]map[common.Hash][]byte) error {
   412  	otr, err := trie.New(trie.StateTrieID(root), db)
   413  	if err != nil {
   414  		return err
   415  	}
   416  	ntr, err := trie.New(trie.StateTrieID(next), db)
   417  	if err != nil {
   418  		return err
   419  	}
   420  	for addr, accountOrigin := range accountsOrigin {
   421  		var (
   422  			err      error
   423  			addrHash = crypto.Keccak256Hash(addr.Bytes())
   424  		)
   425  		if len(accountOrigin) == 0 {
   426  			err = test.verifyAccountCreation(next, db, otr, ntr, addr, accounts[addrHash], storages[addrHash], storagesOrigin[addr])
   427  		} else {
   428  			err = test.verifyAccountUpdate(next, db, otr, ntr, addr, accounts[addrHash], accountsOrigin[addr], storages[addrHash], storagesOrigin[addr])
   429  		}
   430  		if err != nil {
   431  			return err
   432  		}
   433  	}
   434  	return nil
   435  }
   436  
   437  func TestStateChanges(t *testing.T) {
   438  	config := &quick.Config{MaxCount: 1000}
   439  	err := quick.Check((*stateTest).run, config)
   440  	if cerr, ok := err.(*quick.CheckError); ok {
   441  		test := cerr.In[0].(*stateTest)
   442  		t.Errorf("%v:\n%s", test.err, test)
   443  	} else if err != nil {
   444  		t.Error(err)
   445  	}
   446  }