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  }