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