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 }