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