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 }