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