github.com/ethereum/go-ethereum@v1.16.1/cmd/evm/t8n_test.go (about) 1 // Copyright 2021 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU 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 // go-ethereum 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 General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 17 package main 18 19 import ( 20 "bufio" 21 "encoding/json" 22 "fmt" 23 "io" 24 "os" 25 "path/filepath" 26 "reflect" 27 "regexp" 28 "strings" 29 "testing" 30 31 "github.com/ethereum/go-ethereum/cmd/evm/internal/t8ntool" 32 "github.com/ethereum/go-ethereum/internal/cmdtest" 33 "github.com/ethereum/go-ethereum/internal/reexec" 34 ) 35 36 func TestMain(m *testing.M) { 37 // Run the app if we've been exec'd as "ethkey-test" in runEthkey. 38 reexec.Register("evm-test", func() { 39 if err := app.Run(os.Args); err != nil { 40 fmt.Fprintln(os.Stderr, err) 41 os.Exit(1) 42 } 43 os.Exit(0) 44 }) 45 // check if we have been reexec'd 46 if reexec.Init() { 47 return 48 } 49 os.Exit(m.Run()) 50 } 51 52 type testT8n struct { 53 *cmdtest.TestCmd 54 } 55 56 type t8nInput struct { 57 inAlloc string 58 inTxs string 59 inEnv string 60 stFork string 61 stReward string 62 } 63 64 func (args *t8nInput) get(base string) []string { 65 var out []string 66 if opt := args.inAlloc; opt != "" { 67 out = append(out, "--input.alloc") 68 out = append(out, fmt.Sprintf("%v/%v", base, opt)) 69 } 70 if opt := args.inTxs; opt != "" { 71 out = append(out, "--input.txs") 72 out = append(out, fmt.Sprintf("%v/%v", base, opt)) 73 } 74 if opt := args.inEnv; opt != "" { 75 out = append(out, "--input.env") 76 out = append(out, fmt.Sprintf("%v/%v", base, opt)) 77 } 78 if opt := args.stFork; opt != "" { 79 out = append(out, "--state.fork", opt) 80 } 81 if opt := args.stReward; opt != "" { 82 out = append(out, "--state.reward", opt) 83 } 84 return out 85 } 86 87 type t8nOutput struct { 88 alloc bool 89 result bool 90 body bool 91 } 92 93 func (args *t8nOutput) get() (out []string) { 94 if args.body { 95 out = append(out, "--output.body", "stdout") 96 } else { 97 out = append(out, "--output.body", "") // empty means ignore 98 } 99 if args.result { 100 out = append(out, "--output.result", "stdout") 101 } else { 102 out = append(out, "--output.result", "") 103 } 104 if args.alloc { 105 out = append(out, "--output.alloc", "stdout") 106 } else { 107 out = append(out, "--output.alloc", "") 108 } 109 return out 110 } 111 112 func TestT8n(t *testing.T) { 113 t.Parallel() 114 tt := new(testT8n) 115 tt.TestCmd = cmdtest.NewTestCmd(t, tt) 116 for i, tc := range []struct { 117 base string 118 input t8nInput 119 output t8nOutput 120 expExitCode int 121 expOut string 122 }{ 123 { // Test exit (3) on bad config 124 base: "./testdata/1", 125 input: t8nInput{ 126 "alloc.json", "txs.json", "env.json", "Frontier+1346", "", 127 }, 128 output: t8nOutput{alloc: true, result: true}, 129 expExitCode: 3, 130 }, 131 { 132 base: "./testdata/1", 133 input: t8nInput{ 134 "alloc.json", "txs.json", "env.json", "Byzantium", "", 135 }, 136 output: t8nOutput{alloc: true, result: true}, 137 expOut: "exp.json", 138 }, 139 { // blockhash test 140 base: "./testdata/3", 141 input: t8nInput{ 142 "alloc.json", "txs.json", "env.json", "Berlin", "", 143 }, 144 output: t8nOutput{alloc: true, result: true}, 145 expOut: "exp.json", 146 }, 147 { // missing blockhash test 148 base: "./testdata/4", 149 input: t8nInput{ 150 "alloc.json", "txs.json", "env.json", "Berlin", "", 151 }, 152 output: t8nOutput{alloc: true, result: true}, 153 expExitCode: 4, 154 }, 155 { // Uncle test 156 base: "./testdata/5", 157 input: t8nInput{ 158 "alloc.json", "txs.json", "env.json", "Byzantium", "0x80", 159 }, 160 output: t8nOutput{alloc: true, result: true}, 161 expOut: "exp.json", 162 }, 163 { // Sign json transactions 164 base: "./testdata/13", 165 input: t8nInput{ 166 "alloc.json", "txs.json", "env.json", "London", "", 167 }, 168 output: t8nOutput{body: true}, 169 expOut: "exp.json", 170 }, 171 { // Already signed transactions 172 base: "./testdata/13", 173 input: t8nInput{ 174 "alloc.json", "signed_txs.rlp", "env.json", "London", "", 175 }, 176 output: t8nOutput{result: true}, 177 expOut: "exp2.json", 178 }, 179 { // Difficulty calculation - no uncles 180 base: "./testdata/14", 181 input: t8nInput{ 182 "alloc.json", "txs.json", "env.json", "London", "", 183 }, 184 output: t8nOutput{result: true}, 185 expOut: "exp.json", 186 }, 187 { // Difficulty calculation - with uncles 188 base: "./testdata/14", 189 input: t8nInput{ 190 "alloc.json", "txs.json", "env.uncles.json", "London", "", 191 }, 192 output: t8nOutput{result: true}, 193 expOut: "exp2.json", 194 }, 195 { // Difficulty calculation - with ommers + Berlin 196 base: "./testdata/14", 197 input: t8nInput{ 198 "alloc.json", "txs.json", "env.uncles.json", "Berlin", "", 199 }, 200 output: t8nOutput{result: true}, 201 expOut: "exp_berlin.json", 202 }, 203 { // Difficulty calculation on arrow glacier 204 base: "./testdata/19", 205 input: t8nInput{ 206 "alloc.json", "txs.json", "env.json", "London", "", 207 }, 208 output: t8nOutput{result: true}, 209 expOut: "exp_london.json", 210 }, 211 { // Difficulty calculation on arrow glacier 212 base: "./testdata/19", 213 input: t8nInput{ 214 "alloc.json", "txs.json", "env.json", "ArrowGlacier", "", 215 }, 216 output: t8nOutput{result: true}, 217 expOut: "exp_arrowglacier.json", 218 }, 219 { // Difficulty calculation on gray glacier 220 base: "./testdata/19", 221 input: t8nInput{ 222 "alloc.json", "txs.json", "env.json", "GrayGlacier", "", 223 }, 224 output: t8nOutput{result: true}, 225 expOut: "exp_grayglacier.json", 226 }, 227 { // Sign unprotected (pre-EIP155) transaction 228 base: "./testdata/23", 229 input: t8nInput{ 230 "alloc.json", "txs.json", "env.json", "Berlin", "", 231 }, 232 output: t8nOutput{result: true}, 233 expOut: "exp.json", 234 }, 235 { // Test post-merge transition 236 base: "./testdata/24", 237 input: t8nInput{ 238 "alloc.json", "txs.json", "env.json", "Paris", "", 239 }, 240 output: t8nOutput{alloc: true, result: true}, 241 expOut: "exp.json", 242 }, 243 { // Test post-merge transition where input is missing random 244 base: "./testdata/24", 245 input: t8nInput{ 246 "alloc.json", "txs.json", "env-missingrandom.json", "Paris", "", 247 }, 248 output: t8nOutput{alloc: false, result: false}, 249 expExitCode: 3, 250 }, 251 { // Test base fee calculation 252 base: "./testdata/25", 253 input: t8nInput{ 254 "alloc.json", "txs.json", "env.json", "Paris", "", 255 }, 256 output: t8nOutput{alloc: true, result: true}, 257 expOut: "exp.json", 258 }, 259 { // Test withdrawals transition 260 base: "./testdata/26", 261 input: t8nInput{ 262 "alloc.json", "txs.json", "env.json", "Shanghai", "", 263 }, 264 output: t8nOutput{alloc: true, result: true}, 265 expOut: "exp.json", 266 }, 267 { // Cancun tests 268 base: "./testdata/28", 269 input: t8nInput{ 270 "alloc.json", "txs.rlp", "env.json", "Cancun", "", 271 }, 272 output: t8nOutput{alloc: true, result: true}, 273 expOut: "exp.json", 274 }, 275 { // More cancun tests 276 base: "./testdata/29", 277 input: t8nInput{ 278 "alloc.json", "txs.json", "env.json", "Cancun", "", 279 }, 280 output: t8nOutput{alloc: true, result: true}, 281 expOut: "exp.json", 282 }, 283 { // More cancun test, plus example of rlp-transaction that cannot be decoded properly 284 base: "./testdata/30", 285 input: t8nInput{ 286 "alloc.json", "txs_more.rlp", "env.json", "Cancun", "", 287 }, 288 output: t8nOutput{alloc: true, result: true}, 289 expOut: "exp.json", 290 }, 291 { // Prague test, EIP-7702 transaction 292 base: "./testdata/33", 293 input: t8nInput{ 294 "alloc.json", "txs.json", "env.json", "Prague", "", 295 }, 296 output: t8nOutput{alloc: true, result: true}, 297 expOut: "exp.json", 298 }, 299 } { 300 args := []string{"t8n"} 301 args = append(args, tc.output.get()...) 302 args = append(args, tc.input.get(tc.base)...) 303 var qArgs []string // quoted args for debugging purposes 304 for _, arg := range args { 305 if len(arg) == 0 { 306 qArgs = append(qArgs, `""`) 307 } else { 308 qArgs = append(qArgs, arg) 309 } 310 } 311 tt.Logf("args: %v\n", strings.Join(qArgs, " ")) 312 tt.Run("evm-test", args...) 313 // Compare the expected output, if provided 314 if tc.expOut != "" { 315 file := fmt.Sprintf("%v/%v", tc.base, tc.expOut) 316 want, err := os.ReadFile(file) 317 if err != nil { 318 t.Fatalf("test %d: could not read expected output: %v", i, err) 319 } 320 have := tt.Output() 321 ok, err := cmpJson(have, want) 322 switch { 323 case err != nil: 324 t.Fatalf("test %d, file %v: json parsing failed: %v", i, file, err) 325 case !ok: 326 t.Fatalf("test %d, file %v: output wrong, have \n%v\nwant\n%v\n", i, file, string(have), string(want)) 327 } 328 } 329 tt.WaitExit() 330 if have, want := tt.ExitStatus(), tc.expExitCode; have != want { 331 t.Fatalf("test %d: wrong exit code, have %d, want %d", i, have, want) 332 } 333 } 334 } 335 336 func lineIterator(path string) func() (string, error) { 337 data, err := os.ReadFile(path) 338 if err != nil { 339 return func() (string, error) { return err.Error(), err } 340 } 341 scanner := bufio.NewScanner(strings.NewReader(string(data))) 342 return func() (string, error) { 343 if scanner.Scan() { 344 return scanner.Text(), nil 345 } 346 if err := scanner.Err(); err != nil { 347 return "", err 348 } 349 return "", io.EOF // scanner gobbles io.EOF, but we want it 350 } 351 } 352 353 type t9nInput struct { 354 inTxs string 355 stFork string 356 } 357 358 func (args *t9nInput) get(base string) []string { 359 var out []string 360 if opt := args.inTxs; opt != "" { 361 out = append(out, "--input.txs") 362 out = append(out, fmt.Sprintf("%v/%v", base, opt)) 363 } 364 if opt := args.stFork; opt != "" { 365 out = append(out, "--state.fork", opt) 366 } 367 return out 368 } 369 370 func TestT9n(t *testing.T) { 371 t.Parallel() 372 tt := new(testT8n) 373 tt.TestCmd = cmdtest.NewTestCmd(t, tt) 374 for i, tc := range []struct { 375 base string 376 input t9nInput 377 expExitCode int 378 expOut string 379 }{ 380 { // London txs on homestead 381 base: "./testdata/15", 382 input: t9nInput{ 383 inTxs: "signed_txs.rlp", 384 stFork: "Homestead", 385 }, 386 expOut: "exp.json", 387 }, 388 { // London txs on London 389 base: "./testdata/15", 390 input: t9nInput{ 391 inTxs: "signed_txs.rlp", 392 stFork: "London", 393 }, 394 expOut: "exp2.json", 395 }, 396 { // An RLP list (a blockheader really) 397 base: "./testdata/15", 398 input: t9nInput{ 399 inTxs: "blockheader.rlp", 400 stFork: "London", 401 }, 402 expOut: "exp3.json", 403 }, 404 { // Transactions with too low gas 405 base: "./testdata/16", 406 input: t9nInput{ 407 inTxs: "signed_txs.rlp", 408 stFork: "London", 409 }, 410 expOut: "exp.json", 411 }, 412 { // Transactions with value exceeding 256 bits 413 base: "./testdata/17", 414 input: t9nInput{ 415 inTxs: "signed_txs.rlp", 416 stFork: "London", 417 }, 418 expOut: "exp.json", 419 }, 420 { // Invalid RLP 421 base: "./testdata/18", 422 input: t9nInput{ 423 inTxs: "invalid.rlp", 424 stFork: "London", 425 }, 426 expExitCode: t8ntool.ErrorIO, 427 }, 428 } { 429 args := []string{"t9n"} 430 args = append(args, tc.input.get(tc.base)...) 431 432 tt.Run("evm-test", args...) 433 tt.Logf("args:\n go run . %v\n", strings.Join(args, " ")) 434 // Compare the expected output, if provided 435 if tc.expOut != "" { 436 want, err := os.ReadFile(fmt.Sprintf("%v/%v", tc.base, tc.expOut)) 437 if err != nil { 438 t.Fatalf("test %d: could not read expected output: %v", i, err) 439 } 440 have := tt.Output() 441 ok, err := cmpJson(have, want) 442 switch { 443 case err != nil: 444 t.Log(string(have)) 445 t.Fatalf("test %d, json parsing failed: %v", i, err) 446 case !ok: 447 t.Fatalf("test %d: output wrong, have \n%v\nwant\n%v\n", i, string(have), string(want)) 448 } 449 } 450 tt.WaitExit() 451 if have, want := tt.ExitStatus(), tc.expExitCode; have != want { 452 t.Fatalf("test %d: wrong exit code, have %d, want %d", i, have, want) 453 } 454 } 455 } 456 457 type b11rInput struct { 458 inEnv string 459 inOmmersRlp string 460 inWithdrawals string 461 inTxsRlp string 462 inClique string 463 ethash bool 464 ethashMode string 465 ethashDir string 466 } 467 468 func (args *b11rInput) get(base string) []string { 469 var out []string 470 if opt := args.inEnv; opt != "" { 471 out = append(out, "--input.header") 472 out = append(out, fmt.Sprintf("%v/%v", base, opt)) 473 } 474 if opt := args.inOmmersRlp; opt != "" { 475 out = append(out, "--input.ommers") 476 out = append(out, fmt.Sprintf("%v/%v", base, opt)) 477 } 478 if opt := args.inWithdrawals; opt != "" { 479 out = append(out, "--input.withdrawals") 480 out = append(out, fmt.Sprintf("%v/%v", base, opt)) 481 } 482 if opt := args.inTxsRlp; opt != "" { 483 out = append(out, "--input.txs") 484 out = append(out, fmt.Sprintf("%v/%v", base, opt)) 485 } 486 if opt := args.inClique; opt != "" { 487 out = append(out, "--seal.clique") 488 out = append(out, fmt.Sprintf("%v/%v", base, opt)) 489 } 490 if args.ethash { 491 out = append(out, "--seal.ethash") 492 } 493 if opt := args.ethashMode; opt != "" { 494 out = append(out, "--seal.ethash.mode") 495 out = append(out, fmt.Sprintf("%v/%v", base, opt)) 496 } 497 if opt := args.ethashDir; opt != "" { 498 out = append(out, "--seal.ethash.dir") 499 out = append(out, fmt.Sprintf("%v/%v", base, opt)) 500 } 501 out = append(out, "--output.block") 502 out = append(out, "stdout") 503 return out 504 } 505 506 func TestB11r(t *testing.T) { 507 t.Parallel() 508 tt := new(testT8n) 509 tt.TestCmd = cmdtest.NewTestCmd(t, tt) 510 for i, tc := range []struct { 511 base string 512 input b11rInput 513 expExitCode int 514 expOut string 515 }{ 516 { // unsealed block 517 base: "./testdata/20", 518 input: b11rInput{ 519 inEnv: "header.json", 520 inOmmersRlp: "ommers.json", 521 inTxsRlp: "txs.rlp", 522 }, 523 expOut: "exp.json", 524 }, 525 { // ethash test seal 526 base: "./testdata/21", 527 input: b11rInput{ 528 inEnv: "header.json", 529 inOmmersRlp: "ommers.json", 530 inTxsRlp: "txs.rlp", 531 }, 532 expOut: "exp.json", 533 }, 534 { // clique test seal 535 base: "./testdata/21", 536 input: b11rInput{ 537 inEnv: "header.json", 538 inOmmersRlp: "ommers.json", 539 inTxsRlp: "txs.rlp", 540 inClique: "clique.json", 541 }, 542 expOut: "exp-clique.json", 543 }, 544 { // block with ommers 545 base: "./testdata/22", 546 input: b11rInput{ 547 inEnv: "header.json", 548 inOmmersRlp: "ommers.json", 549 inTxsRlp: "txs.rlp", 550 }, 551 expOut: "exp.json", 552 }, 553 { // block with withdrawals 554 base: "./testdata/27", 555 input: b11rInput{ 556 inEnv: "header.json", 557 inOmmersRlp: "ommers.json", 558 inWithdrawals: "withdrawals.json", 559 inTxsRlp: "txs.rlp", 560 }, 561 expOut: "exp.json", 562 }, 563 } { 564 args := []string{"b11r"} 565 args = append(args, tc.input.get(tc.base)...) 566 567 tt.Run("evm-test", args...) 568 tt.Logf("args:\n go run . %v\n", strings.Join(args, " ")) 569 // Compare the expected output, if provided 570 if tc.expOut != "" { 571 want, err := os.ReadFile(fmt.Sprintf("%v/%v", tc.base, tc.expOut)) 572 if err != nil { 573 t.Fatalf("test %d: could not read expected output: %v", i, err) 574 } 575 have := tt.Output() 576 ok, err := cmpJson(have, want) 577 switch { 578 case err != nil: 579 t.Log(string(have)) 580 t.Fatalf("test %d, json parsing failed: %v", i, err) 581 case !ok: 582 t.Fatalf("test %d: output wrong, have \n%v\nwant\n%v\n", i, string(have), string(want)) 583 } 584 } 585 tt.WaitExit() 586 if have, want := tt.ExitStatus(), tc.expExitCode; have != want { 587 t.Fatalf("test %d: wrong exit code, have %d, want %d", i, have, want) 588 } 589 } 590 } 591 592 func TestEvmRun(t *testing.T) { 593 t.Parallel() 594 tt := cmdtest.NewTestCmd(t, nil) 595 for i, tc := range []struct { 596 input []string 597 wantStdout string 598 wantStderr string 599 }{ 600 { // json tracing 601 input: []string{"run", "--trace", "--trace.format=json", "6040"}, 602 wantStdout: "./testdata/evmrun/1.out.1.txt", 603 wantStderr: "./testdata/evmrun/1.out.2.txt", 604 }, 605 { // Same as above, using the deprecated --json 606 input: []string{"run", "--json", "6040"}, 607 wantStdout: "./testdata/evmrun/1.out.1.txt", 608 wantStderr: "./testdata/evmrun/1.out.2.txt", 609 }, 610 { // Struct tracing 611 input: []string{"run", "--trace", "--trace.format=struct", "0x6040"}, 612 wantStdout: "./testdata/evmrun/2.out.1.txt", 613 wantStderr: "./testdata/evmrun/2.out.2.txt", 614 }, 615 { // struct-tracing, plus alloc-dump 616 input: []string{"run", "--trace", "--trace.format=struct", "--dump", "0x6040"}, 617 wantStdout: "./testdata/evmrun/3.out.1.txt", 618 //wantStderr: "./testdata/evmrun/3.out.2.txt", 619 }, 620 { // json-tracing (default), plus alloc-dump 621 input: []string{"run", "--trace", "--dump", "0x6040"}, 622 wantStdout: "./testdata/evmrun/4.out.1.txt", 623 //wantStderr: "./testdata/evmrun/4.out.2.txt", 624 }, 625 { // md-tracing 626 input: []string{"run", "--trace", "--trace.format=md", "0x6040"}, 627 wantStdout: "./testdata/evmrun/5.out.1.txt", 628 wantStderr: "./testdata/evmrun/5.out.2.txt", 629 }, 630 { // statetest subcommand 631 input: []string{"statetest", "./testdata/statetest.json"}, 632 wantStdout: "./testdata/evmrun/6.out.1.txt", 633 wantStderr: "./testdata/evmrun/6.out.2.txt", 634 }, 635 { // statetest subcommand with output 636 input: []string{"statetest", "--trace", "--trace.format=md", "./testdata/statetest.json"}, 637 wantStdout: "./testdata/evmrun/7.out.1.txt", 638 wantStderr: "./testdata/evmrun/7.out.2.txt", 639 }, 640 { // statetest subcommand with output 641 input: []string{"statetest", "--trace", "--trace.format=json", "./testdata/statetest.json"}, 642 wantStdout: "./testdata/evmrun/8.out.1.txt", 643 wantStderr: "./testdata/evmrun/8.out.2.txt", 644 }, 645 } { 646 tt.Logf("args: go run ./cmd/evm %v\n", strings.Join(tc.input, " ")) 647 tt.Run("evm-test", tc.input...) 648 649 haveStdOut := tt.Output() 650 tt.WaitExit() 651 haveStdErr := tt.StderrText() 652 653 if have, wantFile := haveStdOut, tc.wantStdout; wantFile != "" { 654 want, err := os.ReadFile(wantFile) 655 if err != nil { 656 t.Fatalf("test %d: could not read expected output: %v", i, err) 657 } 658 if string(haveStdOut) != string(want) { 659 t.Fatalf("test %d, output wrong, have \n%v\nwant\n%v\n", i, string(have), string(want)) 660 } 661 } 662 if have, wantFile := haveStdErr, tc.wantStderr; wantFile != "" { 663 want, err := os.ReadFile(wantFile) 664 if err != nil { 665 t.Fatalf("test %d: could not read expected output: %v", i, err) 666 } 667 if have != string(want) { 668 t.Fatalf("test %d, output wrong\nhave %q\nwant %q\n", i, have, string(want)) 669 } 670 } 671 } 672 } 673 674 func TestEvmRunRegEx(t *testing.T) { 675 t.Parallel() 676 tt := cmdtest.NewTestCmd(t, nil) 677 for i, tc := range []struct { 678 input []string 679 wantStdout string 680 wantStderr string 681 }{ 682 { // json tracing 683 input: []string{"run", "--bench", "6040"}, 684 wantStdout: "./testdata/evmrun/9.out.1.txt", 685 wantStderr: "./testdata/evmrun/9.out.2.txt", 686 }, 687 { // statetest subcommand 688 input: []string{"statetest", "--bench", "./testdata/statetest.json"}, 689 wantStdout: "./testdata/evmrun/10.out.1.txt", 690 wantStderr: "./testdata/evmrun/10.out.2.txt", 691 }, 692 } { 693 tt.Logf("args: go run ./cmd/evm %v\n", strings.Join(tc.input, " ")) 694 tt.Run("evm-test", tc.input...) 695 696 haveStdOut := tt.Output() 697 tt.WaitExit() 698 haveStdErr := tt.StderrText() 699 700 if have, wantFile := haveStdOut, tc.wantStdout; wantFile != "" { 701 want, err := os.ReadFile(wantFile) 702 if err != nil { 703 t.Fatalf("test %d: could not read expected output: %v", i, err) 704 } 705 re, err := regexp.Compile(string(want)) 706 if err != nil { 707 t.Fatalf("test %d: could not compile regular expression: %v", i, err) 708 } 709 if !re.Match(have) { 710 t.Fatalf("test %d, output wrong, have \n%v\nwant\n%v\n", i, string(have), re) 711 } 712 } 713 if have, wantFile := haveStdErr, tc.wantStderr; wantFile != "" { 714 want, err := os.ReadFile(wantFile) 715 if err != nil { 716 t.Fatalf("test %d: could not read expected output: %v", i, err) 717 } 718 re, err := regexp.Compile(string(want)) 719 if err != nil { 720 t.Fatalf("test %d: could not compile regular expression: %v", i, err) 721 } 722 if !re.MatchString(have) { 723 t.Fatalf("test %d, output wrong, have \n%v\nwant\n%v\n", i, have, re) 724 } 725 } 726 } 727 } 728 729 // cmpJson compares the JSON in two byte slices. 730 func cmpJson(a, b []byte) (bool, error) { 731 var j, j2 interface{} 732 if err := json.Unmarshal(a, &j); err != nil { 733 return false, err 734 } 735 if err := json.Unmarshal(b, &j2); err != nil { 736 return false, err 737 } 738 return reflect.DeepEqual(j2, j), nil 739 } 740 741 // TestEVMTracing is a test that checks the tracing-output from evm. 742 func TestEVMTracing(t *testing.T) { 743 t.Parallel() 744 tt := cmdtest.NewTestCmd(t, nil) 745 for i, tc := range []struct { 746 base string 747 input []string 748 expectedTraces []string 749 }{ 750 { 751 base: "./testdata/31", 752 input: []string{"t8n", 753 "--input.alloc=./testdata/31/alloc.json", "--input.txs=./testdata/31/txs.json", 754 "--input.env=./testdata/31/env.json", "--state.fork=Cancun", 755 "--trace", 756 }, 757 //expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.jsonl"}, 758 expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.jsonl", 759 "trace-1-0x03a7b0a91e61a170d64ea94b8263641ef5a8bbdb10ac69f466083a6789c77fb8.jsonl", 760 "trace-2-0xd96e0ce6418ee3360e11d3c7b6886f5a9a08f7ef183da72c23bb3b2374530128.jsonl"}, 761 }, 762 { 763 base: "./testdata/31", 764 input: []string{"t8n", 765 "--input.alloc=./testdata/31/alloc.json", "--input.txs=./testdata/31/txs.json", 766 "--input.env=./testdata/31/env.json", "--state.fork=Cancun", 767 "--trace.tracer", ` 768 { count: 0, 769 result: function(){ 770 this.count = this.count + 1; 771 return "hello world " + this.count 772 }, 773 fault: function(){} 774 }`, 775 }, 776 expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.json", 777 "trace-1-0x03a7b0a91e61a170d64ea94b8263641ef5a8bbdb10ac69f466083a6789c77fb8.json", 778 "trace-2-0xd96e0ce6418ee3360e11d3c7b6886f5a9a08f7ef183da72c23bb3b2374530128.json"}, 779 }, 780 { 781 base: "./testdata/32", 782 input: []string{"t8n", 783 "--input.alloc=./testdata/32/alloc.json", "--input.txs=./testdata/32/txs.json", 784 "--input.env=./testdata/32/env.json", "--state.fork=Paris", 785 "--trace", "--trace.callframes", 786 }, 787 expectedTraces: []string{"trace-0-0x47806361c0fa084be3caa18afe8c48156747c01dbdfc1ee11b5aecdbe4fcf23e.jsonl"}, 788 }, 789 // TODO, make it possible to run tracers on statetests, e.g: 790 //{ 791 // base: "./testdata/31", 792 // input: []string{"statetest", "--trace", "--trace.tracer", `{ 793 // result: function(){ 794 // return "hello world" 795 // }, 796 // fault: function(){} 797 //}`, "./testdata/statetest.json"}, 798 // expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.json"}, 799 // }, 800 } { 801 // Place the output somewhere we can find it 802 outdir := t.TempDir() 803 args := append(tc.input, "--output.basedir", outdir) 804 805 tt.Run("evm-test", args...) 806 tt.Logf("args: go run ./cmd/evm %v\n", args) 807 tt.WaitExit() 808 //t.Log(string(tt.Output())) 809 810 // Compare the expected traces 811 for _, traceFile := range tc.expectedTraces { 812 haveFn := lineIterator(filepath.Join(outdir, traceFile)) 813 wantFn := lineIterator(filepath.Join(tc.base, traceFile)) 814 815 for line := 0; ; line++ { 816 want, wErr := wantFn() 817 have, hErr := haveFn() 818 if want != have { 819 t.Fatalf("test %d, trace %v, line %d\nwant: %v\nhave: %v\n", 820 i, traceFile, line, want, have) 821 } 822 if wErr != nil && hErr != nil { 823 break 824 } 825 if wErr != nil { 826 t.Fatal(wErr) 827 } 828 if hErr != nil { 829 t.Fatal(hErr) 830 } 831 //t.Logf("%v\n", want) 832 } 833 } 834 } 835 }