github.com/ethereum/go-ethereum@v1.14.3/tests/state_test.go (about)

     1  // Copyright 2015 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 tests
    18  
    19  import (
    20  	"bufio"
    21  	"bytes"
    22  	"fmt"
    23  	"math/big"
    24  	"math/rand"
    25  	"os"
    26  	"path/filepath"
    27  	"reflect"
    28  	"strings"
    29  	"testing"
    30  	"time"
    31  
    32  	"github.com/ethereum/go-ethereum/common"
    33  	"github.com/ethereum/go-ethereum/core"
    34  	"github.com/ethereum/go-ethereum/core/rawdb"
    35  	"github.com/ethereum/go-ethereum/core/types"
    36  	"github.com/ethereum/go-ethereum/core/vm"
    37  	"github.com/ethereum/go-ethereum/eth/tracers/logger"
    38  	"github.com/holiman/uint256"
    39  )
    40  
    41  func initMatcher(st *testMatcher) {
    42  	// Long tests:
    43  	st.slow(`^stAttackTest/ContractCreationSpam`)
    44  	st.slow(`^stBadOpcode/badOpcodes`)
    45  	st.slow(`^stPreCompiledContracts/modexp`)
    46  	st.slow(`^stQuadraticComplexityTest/`)
    47  	st.slow(`^stStaticCall/static_Call50000`)
    48  	st.slow(`^stStaticCall/static_Return50000`)
    49  	st.slow(`^stSystemOperationsTest/CallRecursiveBomb`)
    50  	st.slow(`^stTransactionTest/Opcodes_TransactionInit`)
    51  	// Very time consuming
    52  	st.skipLoad(`^stTimeConsuming/`)
    53  	st.skipLoad(`.*vmPerformance/loop.*`)
    54  	// Uses 1GB RAM per tested fork
    55  	st.skipLoad(`^stStaticCall/static_Call1MB`)
    56  
    57  	// These tests fail as of https://github.com/ethereum/go-ethereum/pull/28666, since we
    58  	// no longer delete "leftover storage" when deploying a contract.
    59  	st.skipLoad(`^stSStoreTest/InitCollision\.json`)
    60  	st.skipLoad(`^stRevertTest/RevertInCreateInInit\.json`)
    61  	st.skipLoad(`^stExtCodeHash/dynamicAccountOverwriteEmpty\.json`)
    62  	st.skipLoad(`^stCreate2/create2collisionStorage\.json`)
    63  	st.skipLoad(`^stCreate2/RevertInCreateInInitCreate2\.json`)
    64  
    65  	// Broken tests:
    66  	// EOF is not part of cancun
    67  	st.skipLoad(`^stEOF/`)
    68  
    69  	// The tests under Pyspecs are the ones that are published as execution-spec tests.
    70  	// We run these tests separately, no need to _also_ run them as part of the
    71  	// reference tests.
    72  	st.skipLoad(`^Pyspecs/`)
    73  }
    74  
    75  func TestState(t *testing.T) {
    76  	t.Parallel()
    77  
    78  	st := new(testMatcher)
    79  	initMatcher(st)
    80  	for _, dir := range []string{
    81  		filepath.Join(baseDir, "EIPTests", "StateTests"),
    82  		stateTestDir,
    83  		benchmarksDir,
    84  	} {
    85  		st.walk(t, dir, func(t *testing.T, name string, test *StateTest) {
    86  			execStateTest(t, st, test)
    87  		})
    88  	}
    89  }
    90  
    91  // TestLegacyState tests some older tests, which were moved to the folder
    92  // 'LegacyTests' for the Istanbul fork.
    93  func TestLegacyState(t *testing.T) {
    94  	st := new(testMatcher)
    95  	initMatcher(st)
    96  	st.walk(t, legacyStateTestDir, func(t *testing.T, name string, test *StateTest) {
    97  		execStateTest(t, st, test)
    98  	})
    99  }
   100  
   101  // TestExecutionSpecState runs the test fixtures from execution-spec-tests.
   102  func TestExecutionSpecState(t *testing.T) {
   103  	if !common.FileExist(executionSpecStateTestDir) {
   104  		t.Skipf("directory %s does not exist", executionSpecStateTestDir)
   105  	}
   106  	st := new(testMatcher)
   107  
   108  	st.walk(t, executionSpecStateTestDir, func(t *testing.T, name string, test *StateTest) {
   109  		execStateTest(t, st, test)
   110  	})
   111  }
   112  
   113  func execStateTest(t *testing.T, st *testMatcher, test *StateTest) {
   114  	for _, subtest := range test.Subtests() {
   115  		subtest := subtest
   116  		key := fmt.Sprintf("%s/%d", subtest.Fork, subtest.Index)
   117  
   118  		// If -short flag is used, we don't execute all four permutations, only
   119  		// one.
   120  		executionMask := 0xf
   121  		if testing.Short() {
   122  			executionMask = (1 << (rand.Int63() & 4))
   123  		}
   124  		t.Run(key+"/hash/trie", func(t *testing.T) {
   125  			if executionMask&0x1 == 0 {
   126  				t.Skip("test (randomly) skipped due to short-tag")
   127  			}
   128  			withTrace(t, test.gasLimit(subtest), func(vmconfig vm.Config) error {
   129  				var result error
   130  				test.Run(subtest, vmconfig, false, rawdb.HashScheme, func(err error, state *StateTestState) {
   131  					result = st.checkFailure(t, err)
   132  				})
   133  				return result
   134  			})
   135  		})
   136  		t.Run(key+"/hash/snap", func(t *testing.T) {
   137  			if executionMask&0x2 == 0 {
   138  				t.Skip("test (randomly) skipped due to short-tag")
   139  			}
   140  			withTrace(t, test.gasLimit(subtest), func(vmconfig vm.Config) error {
   141  				var result error
   142  				test.Run(subtest, vmconfig, true, rawdb.HashScheme, func(err error, state *StateTestState) {
   143  					if state.Snapshots != nil && state.StateDB != nil {
   144  						if _, err := state.Snapshots.Journal(state.StateDB.IntermediateRoot(false)); err != nil {
   145  							result = err
   146  							return
   147  						}
   148  					}
   149  					result = st.checkFailure(t, err)
   150  				})
   151  				return result
   152  			})
   153  		})
   154  		t.Run(key+"/path/trie", func(t *testing.T) {
   155  			if executionMask&0x4 == 0 {
   156  				t.Skip("test (randomly) skipped due to short-tag")
   157  			}
   158  			withTrace(t, test.gasLimit(subtest), func(vmconfig vm.Config) error {
   159  				var result error
   160  				test.Run(subtest, vmconfig, false, rawdb.PathScheme, func(err error, state *StateTestState) {
   161  					result = st.checkFailure(t, err)
   162  				})
   163  				return result
   164  			})
   165  		})
   166  		t.Run(key+"/path/snap", func(t *testing.T) {
   167  			if executionMask&0x8 == 0 {
   168  				t.Skip("test (randomly) skipped due to short-tag")
   169  			}
   170  			withTrace(t, test.gasLimit(subtest), func(vmconfig vm.Config) error {
   171  				var result error
   172  				test.Run(subtest, vmconfig, true, rawdb.PathScheme, func(err error, state *StateTestState) {
   173  					if state.Snapshots != nil && state.StateDB != nil {
   174  						if _, err := state.Snapshots.Journal(state.StateDB.IntermediateRoot(false)); err != nil {
   175  							result = err
   176  							return
   177  						}
   178  					}
   179  					result = st.checkFailure(t, err)
   180  				})
   181  				return result
   182  			})
   183  		})
   184  	}
   185  }
   186  
   187  // Transactions with gasLimit above this value will not get a VM trace on failure.
   188  const traceErrorLimit = 400000
   189  
   190  func withTrace(t *testing.T, gasLimit uint64, test func(vm.Config) error) {
   191  	// Use config from command line arguments.
   192  	config := vm.Config{}
   193  	err := test(config)
   194  	if err == nil {
   195  		return
   196  	}
   197  
   198  	// Test failed, re-run with tracing enabled.
   199  	t.Error(err)
   200  	if gasLimit > traceErrorLimit {
   201  		t.Log("gas limit too high for EVM trace")
   202  		return
   203  	}
   204  	buf := new(bytes.Buffer)
   205  	w := bufio.NewWriter(buf)
   206  	config.Tracer = logger.NewJSONLogger(&logger.Config{}, w)
   207  	err2 := test(config)
   208  	if !reflect.DeepEqual(err, err2) {
   209  		t.Errorf("different error for second run: %v", err2)
   210  	}
   211  	w.Flush()
   212  	if buf.Len() == 0 {
   213  		t.Log("no EVM operation logs generated")
   214  	} else {
   215  		t.Log("EVM operation log:\n" + buf.String())
   216  	}
   217  	// t.Logf("EVM output: 0x%x", tracer.Output())
   218  	// t.Logf("EVM error: %v", tracer.Error())
   219  }
   220  
   221  func BenchmarkEVM(b *testing.B) {
   222  	// Walk the directory.
   223  	dir := benchmarksDir
   224  	dirinfo, err := os.Stat(dir)
   225  	if os.IsNotExist(err) || !dirinfo.IsDir() {
   226  		fmt.Fprintf(os.Stderr, "can't find test files in %s, did you clone the evm-benchmarks submodule?\n", dir)
   227  		b.Skip("missing test files")
   228  	}
   229  	err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
   230  		if info.IsDir() {
   231  			return nil
   232  		}
   233  		if ext := filepath.Ext(path); ext == ".json" {
   234  			name := filepath.ToSlash(strings.TrimPrefix(strings.TrimSuffix(path, ext), dir+string(filepath.Separator)))
   235  			b.Run(name, func(b *testing.B) { runBenchmarkFile(b, path) })
   236  		}
   237  		return nil
   238  	})
   239  	if err != nil {
   240  		b.Fatal(err)
   241  	}
   242  }
   243  
   244  func runBenchmarkFile(b *testing.B, path string) {
   245  	m := make(map[string]StateTest)
   246  	if err := readJSONFile(path, &m); err != nil {
   247  		b.Fatal(err)
   248  		return
   249  	}
   250  	if len(m) != 1 {
   251  		b.Fatal("expected single benchmark in a file")
   252  		return
   253  	}
   254  	for _, t := range m {
   255  		t := t
   256  		runBenchmark(b, &t)
   257  	}
   258  }
   259  
   260  func runBenchmark(b *testing.B, t *StateTest) {
   261  	for _, subtest := range t.Subtests() {
   262  		subtest := subtest
   263  		key := fmt.Sprintf("%s/%d", subtest.Fork, subtest.Index)
   264  
   265  		b.Run(key, func(b *testing.B) {
   266  			vmconfig := vm.Config{}
   267  
   268  			config, eips, err := GetChainConfig(subtest.Fork)
   269  			if err != nil {
   270  				b.Error(err)
   271  				return
   272  			}
   273  			var rules = config.Rules(new(big.Int), false, 0)
   274  
   275  			vmconfig.ExtraEips = eips
   276  			block := t.genesis(config).ToBlock()
   277  			state := MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre, false, rawdb.HashScheme)
   278  			defer state.Close()
   279  
   280  			var baseFee *big.Int
   281  			if rules.IsLondon {
   282  				baseFee = t.json.Env.BaseFee
   283  				if baseFee == nil {
   284  					// Retesteth uses `0x10` for genesis baseFee. Therefore, it defaults to
   285  					// parent - 2 : 0xa as the basefee for 'this' context.
   286  					baseFee = big.NewInt(0x0a)
   287  				}
   288  			}
   289  			post := t.json.Post[subtest.Fork][subtest.Index]
   290  			msg, err := t.json.Tx.toMessage(post, baseFee)
   291  			if err != nil {
   292  				b.Error(err)
   293  				return
   294  			}
   295  
   296  			// Try to recover tx with current signer
   297  			if len(post.TxBytes) != 0 {
   298  				var ttx types.Transaction
   299  				err := ttx.UnmarshalBinary(post.TxBytes)
   300  				if err != nil {
   301  					b.Error(err)
   302  					return
   303  				}
   304  
   305  				if _, err := types.Sender(types.LatestSigner(config), &ttx); err != nil {
   306  					b.Error(err)
   307  					return
   308  				}
   309  			}
   310  
   311  			// Prepare the EVM.
   312  			txContext := core.NewEVMTxContext(msg)
   313  			context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase)
   314  			context.GetHash = vmTestBlockHash
   315  			context.BaseFee = baseFee
   316  			evm := vm.NewEVM(context, txContext, state.StateDB, config, vmconfig)
   317  
   318  			// Create "contract" for sender to cache code analysis.
   319  			sender := vm.NewContract(vm.AccountRef(msg.From), vm.AccountRef(msg.From),
   320  				nil, 0)
   321  
   322  			var (
   323  				gasUsed uint64
   324  				elapsed uint64
   325  				refund  uint64
   326  			)
   327  			b.ResetTimer()
   328  			for n := 0; n < b.N; n++ {
   329  				snapshot := state.StateDB.Snapshot()
   330  				state.StateDB.Prepare(rules, msg.From, context.Coinbase, msg.To, vm.ActivePrecompiles(rules), msg.AccessList)
   331  				b.StartTimer()
   332  				start := time.Now()
   333  
   334  				// Execute the message.
   335  				_, leftOverGas, err := evm.Call(sender, *msg.To, msg.Data, msg.GasLimit, uint256.MustFromBig(msg.Value))
   336  				if err != nil {
   337  					b.Error(err)
   338  					return
   339  				}
   340  
   341  				b.StopTimer()
   342  				elapsed += uint64(time.Since(start))
   343  				refund += state.StateDB.GetRefund()
   344  				gasUsed += msg.GasLimit - leftOverGas
   345  
   346  				state.StateDB.RevertToSnapshot(snapshot)
   347  			}
   348  			if elapsed < 1 {
   349  				elapsed = 1
   350  			}
   351  			// Keep it as uint64, multiply 100 to get two digit float later
   352  			mgasps := (100 * 1000 * (gasUsed - refund)) / elapsed
   353  			b.ReportMetric(float64(mgasps)/100, "mgas/s")
   354  		})
   355  	}
   356  }