github.com/ethereum/go-ethereum@v1.16.1/eth/tracers/internal/tracetest/supply_test.go (about)

     1  // Copyright 2024 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 tracetest
    18  
    19  import (
    20  	"bufio"
    21  	"bytes"
    22  	"encoding/json"
    23  	"fmt"
    24  	"math/big"
    25  	"os"
    26  	"path"
    27  	"path/filepath"
    28  	"testing"
    29  
    30  	"github.com/ethereum/go-ethereum/common"
    31  	"github.com/ethereum/go-ethereum/common/hexutil"
    32  	"github.com/ethereum/go-ethereum/consensus/beacon"
    33  	"github.com/ethereum/go-ethereum/consensus/ethash"
    34  	"github.com/ethereum/go-ethereum/core"
    35  	"github.com/ethereum/go-ethereum/core/rawdb"
    36  	"github.com/ethereum/go-ethereum/core/types"
    37  	"github.com/ethereum/go-ethereum/core/vm"
    38  	"github.com/ethereum/go-ethereum/crypto"
    39  	"github.com/ethereum/go-ethereum/eth/tracers"
    40  	"github.com/ethereum/go-ethereum/params"
    41  
    42  	// Force-load live packages, to trigger registration
    43  	_ "github.com/ethereum/go-ethereum/eth/tracers/live"
    44  )
    45  
    46  type supplyInfoIssuance struct {
    47  	GenesisAlloc *hexutil.Big `json:"genesisAlloc,omitempty"`
    48  	Reward       *hexutil.Big `json:"reward,omitempty"`
    49  	Withdrawals  *hexutil.Big `json:"withdrawals,omitempty"`
    50  }
    51  
    52  type supplyInfoBurn struct {
    53  	EIP1559 *hexutil.Big `json:"1559,omitempty"`
    54  	Blob    *hexutil.Big `json:"blob,omitempty"`
    55  	Misc    *hexutil.Big `json:"misc,omitempty"`
    56  }
    57  
    58  type supplyInfo struct {
    59  	Issuance *supplyInfoIssuance `json:"issuance,omitempty"`
    60  	Burn     *supplyInfoBurn     `json:"burn,omitempty"`
    61  
    62  	// Block info
    63  	Number     uint64      `json:"blockNumber"`
    64  	Hash       common.Hash `json:"hash"`
    65  	ParentHash common.Hash `json:"parentHash"`
    66  }
    67  
    68  func emptyBlockGenerationFunc(b *core.BlockGen) {}
    69  
    70  func TestSupplyOmittedFields(t *testing.T) {
    71  	var (
    72  		config = *params.MergedTestChainConfig
    73  		gspec  = &core.Genesis{
    74  			Config: &config,
    75  		}
    76  	)
    77  
    78  	out, _, err := testSupplyTracer(t, gspec, func(b *core.BlockGen) {
    79  		b.SetPoS()
    80  	})
    81  	if err != nil {
    82  		t.Fatalf("failed to test supply tracer: %v", err)
    83  	}
    84  
    85  	expected := supplyInfo{
    86  		Number:     0,
    87  		Hash:       common.HexToHash("0x3055fc27d6b4a08eb07033a0d1ee755a4b2988086f28a6189eac1b507525eeb1"),
    88  		ParentHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"),
    89  	}
    90  	actual := out[expected.Number]
    91  
    92  	compareAsJSON(t, expected, actual)
    93  }
    94  
    95  func TestSupplyGenesisAlloc(t *testing.T) {
    96  	var (
    97  		key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
    98  		key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
    99  		addr1   = crypto.PubkeyToAddress(key1.PublicKey)
   100  		addr2   = crypto.PubkeyToAddress(key2.PublicKey)
   101  		eth1    = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
   102  
   103  		config = *params.AllEthashProtocolChanges
   104  
   105  		gspec = &core.Genesis{
   106  			Config: &config,
   107  			Alloc: types.GenesisAlloc{
   108  				addr1: {Balance: eth1},
   109  				addr2: {Balance: eth1},
   110  			},
   111  		}
   112  	)
   113  
   114  	expected := supplyInfo{
   115  		Issuance: &supplyInfoIssuance{
   116  			GenesisAlloc: (*hexutil.Big)(new(big.Int).Mul(common.Big2, big.NewInt(params.Ether))),
   117  		},
   118  		Number:     0,
   119  		Hash:       common.HexToHash("0xbcc9466e9fc6a8b56f4b29ca353a421ff8b51a0c1a58ca4743b427605b08f2ca"),
   120  		ParentHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"),
   121  	}
   122  
   123  	out, _, err := testSupplyTracer(t, gspec, emptyBlockGenerationFunc)
   124  	if err != nil {
   125  		t.Fatalf("failed to test supply tracer: %v", err)
   126  	}
   127  
   128  	actual := out[expected.Number]
   129  
   130  	compareAsJSON(t, expected, actual)
   131  }
   132  
   133  func TestSupplyRewards(t *testing.T) {
   134  	var (
   135  		config = *params.AllEthashProtocolChanges
   136  
   137  		gspec = &core.Genesis{
   138  			Config: &config,
   139  		}
   140  	)
   141  
   142  	expected := supplyInfo{
   143  		Issuance: &supplyInfoIssuance{
   144  			Reward: (*hexutil.Big)(new(big.Int).Mul(common.Big2, big.NewInt(params.Ether))),
   145  		},
   146  		Number:     1,
   147  		Hash:       common.HexToHash("0xcbb08370505be503dafedc4e96d139ea27aba3cbc580148568b8a307b3f51052"),
   148  		ParentHash: common.HexToHash("0xadeda0a83e337b6c073e3f0e9a17531a04009b397a9588c093b628f21b8bc5a3"),
   149  	}
   150  
   151  	out, _, err := testSupplyTracer(t, gspec, emptyBlockGenerationFunc)
   152  	if err != nil {
   153  		t.Fatalf("failed to test supply tracer: %v", err)
   154  	}
   155  
   156  	actual := out[expected.Number]
   157  
   158  	compareAsJSON(t, expected, actual)
   159  }
   160  
   161  func TestSupplyEip1559Burn(t *testing.T) {
   162  	var (
   163  		config = *params.AllEthashProtocolChanges
   164  
   165  		aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
   166  		// A sender who makes transactions, has some eth1
   167  		key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
   168  		addr1   = crypto.PubkeyToAddress(key1.PublicKey)
   169  		gwei5   = new(big.Int).Mul(big.NewInt(5), big.NewInt(params.GWei))
   170  		eth1    = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
   171  
   172  		gspec = &core.Genesis{
   173  			Config:  &config,
   174  			BaseFee: big.NewInt(params.InitialBaseFee),
   175  			Alloc: types.GenesisAlloc{
   176  				addr1: {Balance: eth1},
   177  			},
   178  		}
   179  	)
   180  
   181  	signer := types.LatestSigner(gspec.Config)
   182  
   183  	eip1559BlockGenerationFunc := func(b *core.BlockGen) {
   184  		txdata := &types.DynamicFeeTx{
   185  			ChainID:   gspec.Config.ChainID,
   186  			Nonce:     0,
   187  			To:        &aa,
   188  			Gas:       21000,
   189  			GasFeeCap: gwei5,
   190  			GasTipCap: big.NewInt(2),
   191  		}
   192  		tx := types.NewTx(txdata)
   193  		tx, _ = types.SignTx(tx, signer, key1)
   194  
   195  		b.AddTx(tx)
   196  	}
   197  
   198  	out, chain, err := testSupplyTracer(t, gspec, eip1559BlockGenerationFunc)
   199  	if err != nil {
   200  		t.Fatalf("failed to test supply tracer: %v", err)
   201  	}
   202  	var (
   203  		head     = chain.CurrentBlock()
   204  		reward   = new(big.Int).Mul(common.Big2, big.NewInt(params.Ether))
   205  		burn     = new(big.Int).Mul(big.NewInt(21000), head.BaseFee)
   206  		expected = supplyInfo{
   207  			Issuance: &supplyInfoIssuance{
   208  				Reward: (*hexutil.Big)(reward),
   209  			},
   210  			Burn: &supplyInfoBurn{
   211  				EIP1559: (*hexutil.Big)(burn),
   212  			},
   213  			Number:     1,
   214  			Hash:       head.Hash(),
   215  			ParentHash: head.ParentHash,
   216  		}
   217  	)
   218  
   219  	actual := out[expected.Number]
   220  	compareAsJSON(t, expected, actual)
   221  }
   222  
   223  func TestSupplyWithdrawals(t *testing.T) {
   224  	var (
   225  		config = *params.MergedTestChainConfig
   226  		gspec  = &core.Genesis{
   227  			Config: &config,
   228  		}
   229  	)
   230  
   231  	withdrawalsBlockGenerationFunc := func(b *core.BlockGen) {
   232  		b.SetPoS()
   233  
   234  		b.AddWithdrawal(&types.Withdrawal{
   235  			Validator: 42,
   236  			Address:   common.Address{0xee},
   237  			Amount:    1337,
   238  		})
   239  	}
   240  
   241  	out, chain, err := testSupplyTracer(t, gspec, withdrawalsBlockGenerationFunc)
   242  	if err != nil {
   243  		t.Fatalf("failed to test supply tracer: %v", err)
   244  	}
   245  
   246  	var (
   247  		head     = chain.CurrentBlock()
   248  		expected = supplyInfo{
   249  			Issuance: &supplyInfoIssuance{
   250  				Withdrawals: (*hexutil.Big)(big.NewInt(1337000000000)),
   251  			},
   252  			Number:     1,
   253  			Hash:       head.Hash(),
   254  			ParentHash: head.ParentHash,
   255  		}
   256  		actual = out[expected.Number]
   257  	)
   258  
   259  	compareAsJSON(t, expected, actual)
   260  }
   261  
   262  // Tests fund retrieval after contract's selfdestruct.
   263  // Contract A calls contract B which selfdestructs, but B receives eth1
   264  // after the selfdestruct opcode executes from Contract A.
   265  // Because Contract B is removed only at the end of the transaction
   266  // the ether sent in between is burnt before Cancun hard fork.
   267  func TestSupplySelfdestruct(t *testing.T) {
   268  	var (
   269  		config = *params.TestChainConfig
   270  
   271  		aa      = common.HexToAddress("0x1111111111111111111111111111111111111111")
   272  		bb      = common.HexToAddress("0x2222222222222222222222222222222222222222")
   273  		dad     = common.HexToAddress("0x0000000000000000000000000000000000000dad")
   274  		key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
   275  		addr1   = crypto.PubkeyToAddress(key1.PublicKey)
   276  		gwei5   = new(big.Int).Mul(big.NewInt(5), big.NewInt(params.GWei))
   277  		eth1    = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
   278  
   279  		gspec = &core.Genesis{
   280  			Config:  &config,
   281  			BaseFee: big.NewInt(params.InitialBaseFee),
   282  			Alloc: types.GenesisAlloc{
   283  				addr1: {Balance: eth1},
   284  				aa: {
   285  					Code: common.FromHex("0x61face60f01b6000527322222222222222222222222222222222222222226000806002600080855af160008103603457600080fd5b60008060008034865af1905060008103604c57600080fd5b5050"),
   286  					// Nonce:   0,
   287  					Balance: big.NewInt(0),
   288  				},
   289  				bb: {
   290  					Code:    common.FromHex("0x6000357fface000000000000000000000000000000000000000000000000000000000000808203602f57610dad80ff5b5050"),
   291  					Nonce:   0,
   292  					Balance: eth1,
   293  				},
   294  			},
   295  		}
   296  	)
   297  
   298  	gspec.Config.TerminalTotalDifficulty = big.NewInt(0)
   299  
   300  	signer := types.LatestSigner(gspec.Config)
   301  
   302  	testBlockGenerationFunc := func(b *core.BlockGen) {
   303  		b.SetPoS()
   304  
   305  		txdata := &types.LegacyTx{
   306  			Nonce:    0,
   307  			To:       &aa,
   308  			Value:    gwei5,
   309  			Gas:      150000,
   310  			GasPrice: gwei5,
   311  			Data:     []byte{},
   312  		}
   313  
   314  		tx := types.NewTx(txdata)
   315  		tx, _ = types.SignTx(tx, signer, key1)
   316  
   317  		b.AddTx(tx)
   318  	}
   319  
   320  	// 1. Test pre Cancun
   321  	preCancunOutput, preCancunChain, err := testSupplyTracer(t, gspec, testBlockGenerationFunc)
   322  	if err != nil {
   323  		t.Fatalf("Pre-cancun failed to test supply tracer: %v", err)
   324  	}
   325  
   326  	// Check balance at state:
   327  	// 1. 0x0000...000dad has 1 ether
   328  	// 2. A has 0 ether
   329  	// 3. B has 0 ether
   330  	statedb, _ := preCancunChain.State()
   331  	if got, exp := statedb.GetBalance(dad), eth1; got.CmpBig(exp) != 0 {
   332  		t.Fatalf("Pre-cancun address \"%v\" balance, got %v exp %v\n", dad, got, exp)
   333  	}
   334  	if got, exp := statedb.GetBalance(aa), big.NewInt(0); got.CmpBig(exp) != 0 {
   335  		t.Fatalf("Pre-cancun address \"%v\" balance, got %v exp %v\n", aa, got, exp)
   336  	}
   337  	if got, exp := statedb.GetBalance(bb), big.NewInt(0); got.CmpBig(exp) != 0 {
   338  		t.Fatalf("Pre-cancun address \"%v\" balance, got %v exp %v\n", bb, got, exp)
   339  	}
   340  
   341  	head := preCancunChain.CurrentBlock()
   342  	// Check live trace output
   343  	expected := supplyInfo{
   344  		Burn: &supplyInfoBurn{
   345  			EIP1559: (*hexutil.Big)(big.NewInt(55289500000000)),
   346  			Misc:    (*hexutil.Big)(big.NewInt(5000000000)),
   347  		},
   348  		Number:     1,
   349  		Hash:       head.Hash(),
   350  		ParentHash: head.ParentHash,
   351  	}
   352  
   353  	actual := preCancunOutput[expected.Number]
   354  
   355  	compareAsJSON(t, expected, actual)
   356  
   357  	// 2. Test post Cancun
   358  	cancunTime := uint64(0)
   359  	gspec.Config.ShanghaiTime = &cancunTime
   360  	gspec.Config.CancunTime = &cancunTime
   361  	gspec.Config.BlobScheduleConfig = params.DefaultBlobSchedule
   362  
   363  	postCancunOutput, postCancunChain, err := testSupplyTracer(t, gspec, testBlockGenerationFunc)
   364  	if err != nil {
   365  		t.Fatalf("Post-cancun failed to test supply tracer: %v", err)
   366  	}
   367  
   368  	// Check balance at state:
   369  	// 1. 0x0000...000dad has 1 ether
   370  	// 3. A has 0 ether
   371  	// 3. B has 5 gwei
   372  	statedb, _ = postCancunChain.State()
   373  	if got, exp := statedb.GetBalance(dad), eth1; got.CmpBig(exp) != 0 {
   374  		t.Fatalf("Post-shanghai address \"%v\" balance, got %v exp %v\n", dad, got, exp)
   375  	}
   376  	if got, exp := statedb.GetBalance(aa), big.NewInt(0); got.CmpBig(exp) != 0 {
   377  		t.Fatalf("Post-shanghai address \"%v\" balance, got %v exp %v\n", aa, got, exp)
   378  	}
   379  	if got, exp := statedb.GetBalance(bb), gwei5; got.CmpBig(exp) != 0 {
   380  		t.Fatalf("Post-shanghai address \"%v\" balance, got %v exp %v\n", bb, got, exp)
   381  	}
   382  
   383  	// Check live trace output
   384  	head = postCancunChain.CurrentBlock()
   385  	expected = supplyInfo{
   386  		Burn: &supplyInfoBurn{
   387  			EIP1559: (*hexutil.Big)(big.NewInt(55289500000000)),
   388  		},
   389  		Number:     1,
   390  		Hash:       head.Hash(),
   391  		ParentHash: head.ParentHash,
   392  	}
   393  
   394  	actual = postCancunOutput[expected.Number]
   395  
   396  	compareAsJSON(t, expected, actual)
   397  }
   398  
   399  // Tests selfdestructing contract to send its balance to itself (burn).
   400  // It tests both cases of selfdestructing succeeding and being reverted.
   401  //   - Contract A calls B and D.
   402  //   - Contract B selfdestructs and sends the eth1 to itself (Burn amount to be counted).
   403  //   - Contract C selfdestructs and sends the eth1 to itself.
   404  //   - Contract D calls C and reverts (Burn amount of C
   405  //     has to be reverted as well).
   406  func TestSupplySelfdestructItselfAndRevert(t *testing.T) {
   407  	var (
   408  		config = *params.TestChainConfig
   409  
   410  		aa      = common.HexToAddress("0x1111111111111111111111111111111111111111")
   411  		bb      = common.HexToAddress("0x2222222222222222222222222222222222222222")
   412  		cc      = common.HexToAddress("0x3333333333333333333333333333333333333333")
   413  		dd      = common.HexToAddress("0x4444444444444444444444444444444444444444")
   414  		key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
   415  		addr1   = crypto.PubkeyToAddress(key1.PublicKey)
   416  		gwei5   = new(big.Int).Mul(big.NewInt(5), big.NewInt(params.GWei))
   417  		eth1    = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
   418  		eth2    = new(big.Int).Mul(common.Big2, big.NewInt(params.Ether))
   419  		eth5    = new(big.Int).Mul(big.NewInt(5), big.NewInt(params.Ether))
   420  
   421  		gspec = &core.Genesis{
   422  			Config: &config,
   423  			// BaseFee: big.NewInt(params.InitialBaseFee),
   424  			Alloc: types.GenesisAlloc{
   425  				addr1: {Balance: eth1},
   426  				aa: {
   427  					// Contract code in YUL:
   428  					//
   429  					// object "ContractA" {
   430  					// 	code {
   431  					// 			let B := 0x2222222222222222222222222222222222222222
   432  					// 			let D := 0x4444444444444444444444444444444444444444
   433  
   434  					// 			// Call to Contract B
   435  					// 			let resB:= call(gas(), B, 0, 0x0, 0x0, 0, 0)
   436  
   437  					// 			// Call to Contract D
   438  					// 			let resD := call(gas(), D, 0, 0x0, 0x0, 0, 0)
   439  					// 	}
   440  					// }
   441  					Code:    common.FromHex("0x73222222222222222222222222222222222222222273444444444444444444444444444444444444444460006000600060006000865af160006000600060006000865af150505050"),
   442  					Balance: common.Big0,
   443  				},
   444  				bb: {
   445  					// Contract code in YUL:
   446  					//
   447  					// object "ContractB" {
   448  					// 	code {
   449  					// 			let self := address()
   450  					// 			selfdestruct(self)
   451  					// 	}
   452  					// }
   453  					Code:    common.FromHex("0x3080ff50"),
   454  					Balance: eth5,
   455  				},
   456  				cc: {
   457  					Code:    common.FromHex("0x3080ff50"),
   458  					Balance: eth1,
   459  				},
   460  				dd: {
   461  					// Contract code in YUL:
   462  					//
   463  					// object "ContractD" {
   464  					// 	code {
   465  					// 			let C := 0x3333333333333333333333333333333333333333
   466  
   467  					// 			// Call to Contract C
   468  					// 			let resC := call(gas(), C, 0, 0x0, 0x0, 0, 0)
   469  
   470  					// 			// Revert
   471  					// 			revert(0, 0)
   472  					// 	}
   473  					// }
   474  					Code:    common.FromHex("0x73333333333333333333333333333333333333333360006000600060006000855af160006000fd5050"),
   475  					Balance: eth2,
   476  				},
   477  			},
   478  		}
   479  	)
   480  
   481  	gspec.Config.TerminalTotalDifficulty = big.NewInt(0)
   482  
   483  	signer := types.LatestSigner(gspec.Config)
   484  
   485  	testBlockGenerationFunc := func(b *core.BlockGen) {
   486  		b.SetPoS()
   487  
   488  		txdata := &types.LegacyTx{
   489  			Nonce:    0,
   490  			To:       &aa,
   491  			Value:    common.Big0,
   492  			Gas:      150000,
   493  			GasPrice: gwei5,
   494  			Data:     []byte{},
   495  		}
   496  
   497  		tx := types.NewTx(txdata)
   498  		tx, _ = types.SignTx(tx, signer, key1)
   499  
   500  		b.AddTx(tx)
   501  	}
   502  
   503  	output, chain, err := testSupplyTracer(t, gspec, testBlockGenerationFunc)
   504  	if err != nil {
   505  		t.Fatalf("failed to test supply tracer: %v", err)
   506  	}
   507  
   508  	// Check balance at state:
   509  	// 1. A has 0 ether
   510  	// 2. B has 0 ether, burned
   511  	// 3. C has 2 ether, selfdestructed but parent D reverted
   512  	// 4. D has 1 ether, reverted
   513  	statedb, _ := chain.State()
   514  	if got, exp := statedb.GetBalance(aa), common.Big0; got.CmpBig(exp) != 0 {
   515  		t.Fatalf("address \"%v\" balance, got %v exp %v\n", aa, got, exp)
   516  	}
   517  	if got, exp := statedb.GetBalance(bb), common.Big0; got.CmpBig(exp) != 0 {
   518  		t.Fatalf("address \"%v\" balance, got %v exp %v\n", bb, got, exp)
   519  	}
   520  	if got, exp := statedb.GetBalance(cc), eth1; got.CmpBig(exp) != 0 {
   521  		t.Fatalf("address \"%v\" balance, got %v exp %v\n", bb, got, exp)
   522  	}
   523  	if got, exp := statedb.GetBalance(dd), eth2; got.CmpBig(exp) != 0 {
   524  		t.Fatalf("address \"%v\" balance, got %v exp %v\n", bb, got, exp)
   525  	}
   526  
   527  	// Check live trace output
   528  	block := chain.GetBlockByNumber(1)
   529  
   530  	expected := supplyInfo{
   531  		Burn: &supplyInfoBurn{
   532  			EIP1559: (*hexutil.Big)(new(big.Int).Mul(block.BaseFee(), big.NewInt(int64(block.GasUsed())))),
   533  			Misc:    (*hexutil.Big)(eth5), // 5ETH burned from contract B
   534  		},
   535  		Number:     1,
   536  		Hash:       block.Hash(),
   537  		ParentHash: block.ParentHash(),
   538  	}
   539  
   540  	actual := output[expected.Number]
   541  
   542  	compareAsJSON(t, expected, actual)
   543  }
   544  
   545  func testSupplyTracer(t *testing.T, genesis *core.Genesis, gen func(*core.BlockGen)) ([]supplyInfo, *core.BlockChain, error) {
   546  	engine := beacon.New(ethash.NewFaker())
   547  
   548  	traceOutputPath := filepath.ToSlash(t.TempDir())
   549  	traceOutputFilename := path.Join(traceOutputPath, "supply.jsonl")
   550  
   551  	// Load supply tracer
   552  	tracer, err := tracers.LiveDirectory.New("supply", json.RawMessage(fmt.Sprintf(`{"path":"%s"}`, traceOutputPath)))
   553  	if err != nil {
   554  		return nil, nil, fmt.Errorf("failed to create call tracer: %v", err)
   555  	}
   556  
   557  	options := core.DefaultConfig().WithStateScheme(rawdb.PathScheme)
   558  	options.VmConfig = vm.Config{Tracer: tracer}
   559  	chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), genesis, engine, options)
   560  	if err != nil {
   561  		return nil, nil, fmt.Errorf("failed to create tester chain: %v", err)
   562  	}
   563  	defer chain.Stop()
   564  
   565  	_, blocks, _ := core.GenerateChainWithGenesis(genesis, engine, 1, func(i int, b *core.BlockGen) {
   566  		b.SetCoinbase(common.Address{1})
   567  		gen(b)
   568  	})
   569  
   570  	if n, err := chain.InsertChain(blocks); err != nil {
   571  		return nil, chain, fmt.Errorf("block %d: failed to insert into chain: %v", n, err)
   572  	}
   573  
   574  	// Check and compare the results
   575  	file, err := os.OpenFile(traceOutputFilename, os.O_RDONLY, 0666)
   576  	if err != nil {
   577  		return nil, chain, fmt.Errorf("failed to open output file: %v", err)
   578  	}
   579  	defer file.Close()
   580  
   581  	var output []supplyInfo
   582  	scanner := bufio.NewScanner(file)
   583  
   584  	for scanner.Scan() {
   585  		blockBytes := scanner.Bytes()
   586  
   587  		var info supplyInfo
   588  		if err := json.Unmarshal(blockBytes, &info); err != nil {
   589  			return nil, chain, fmt.Errorf("failed to unmarshal result: %v", err)
   590  		}
   591  
   592  		output = append(output, info)
   593  	}
   594  
   595  	return output, chain, nil
   596  }
   597  
   598  func compareAsJSON(t *testing.T, expected interface{}, actual interface{}) {
   599  	t.Helper()
   600  	want, err := json.Marshal(expected)
   601  	if err != nil {
   602  		t.Fatalf("failed to marshal expected value to JSON: %v", err)
   603  	}
   604  
   605  	have, err := json.Marshal(actual)
   606  	if err != nil {
   607  		t.Fatalf("failed to marshal actual value to JSON: %v", err)
   608  	}
   609  
   610  	if !bytes.Equal(want, have) {
   611  		t.Fatalf("incorrect supply info:\nwant %s\nhave %s", string(want), string(have))
   612  	}
   613  }