github.com/0xPolygon/supernets2-node@v0.0.0-20230711153321-2fe574524eaa/test/e2e/debug_test.go (about)

     1  package e2e
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"math/big"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/0xPolygon/supernets2-node/hex"
    12  	"github.com/0xPolygon/supernets2-node/jsonrpc/client"
    13  	"github.com/0xPolygon/supernets2-node/jsonrpc/types"
    14  	"github.com/0xPolygon/supernets2-node/log"
    15  	"github.com/0xPolygon/supernets2-node/test/operations"
    16  	"github.com/ethereum/go-ethereum"
    17  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    18  	"github.com/ethereum/go-ethereum/common"
    19  	ethTypes "github.com/ethereum/go-ethereum/core/types"
    20  	"github.com/ethereum/go-ethereum/crypto"
    21  	"github.com/ethereum/go-ethereum/ethclient"
    22  	"github.com/stretchr/testify/require"
    23  )
    24  
    25  func TestDebugTraceTransactionNotFoundTx(t *testing.T) {
    26  	if testing.Short() {
    27  		t.Skip()
    28  	}
    29  
    30  	const l2NetworkURL = "http://localhost:8124"
    31  	const l2ExplorerRPCComponentName = "l2-explorer-json-rpc"
    32  
    33  	var err error
    34  	err = operations.Teardown()
    35  	require.NoError(t, err)
    36  
    37  	defer func() {
    38  		require.NoError(t, operations.Teardown())
    39  		require.NoError(t, operations.StopComponent(l2ExplorerRPCComponentName))
    40  	}()
    41  
    42  	ctx := context.Background()
    43  	opsCfg := operations.GetDefaultOperationsConfig()
    44  	opsMan, err := operations.NewManager(ctx, opsCfg)
    45  	require.NoError(t, err)
    46  	err = opsMan.Setup()
    47  	require.NoError(t, err)
    48  
    49  	err = operations.StartComponent(l2ExplorerRPCComponentName, func() (bool, error) { return operations.NodeUpCondition(l2NetworkURL) })
    50  	require.NoError(t, err)
    51  
    52  	const l1NetworkName, l2NetworkName = "Local L1", "Local L2"
    53  
    54  	networks := []struct {
    55  		Name         string
    56  		URL          string
    57  		WebSocketURL string
    58  		ChainID      uint64
    59  		PrivateKey   string
    60  	}{
    61  		{
    62  			Name:       l1NetworkName,
    63  			URL:        operations.DefaultL1NetworkURL,
    64  			ChainID:    operations.DefaultL1ChainID,
    65  			PrivateKey: operations.DefaultSequencerPrivateKey,
    66  		},
    67  		{
    68  			Name:       l2NetworkName,
    69  			URL:        l2NetworkURL,
    70  			ChainID:    operations.DefaultL2ChainID,
    71  			PrivateKey: operations.DefaultSequencerPrivateKey,
    72  		},
    73  	}
    74  
    75  	for _, network := range networks {
    76  		log.Debugf(network.Name)
    77  		tx := ethTypes.NewTx(&ethTypes.LegacyTx{
    78  			Nonce: 10,
    79  		})
    80  
    81  		response, err := client.JSONRPCCall(network.URL, "debug_traceTransaction", tx.Hash().String())
    82  		require.NoError(t, err)
    83  		require.Nil(t, response.Result)
    84  		require.NotNil(t, response.Error)
    85  
    86  		require.Equal(t, -32000, response.Error.Code)
    87  		require.Equal(t, "transaction not found", response.Error.Message)
    88  		require.Nil(t, response.Error.Data)
    89  	}
    90  }
    91  
    92  func TestDebugTraceBlockByNumberNotFoundTx(t *testing.T) {
    93  	if testing.Short() {
    94  		t.Skip()
    95  	}
    96  
    97  	const l2NetworkURL = "http://localhost:8124"
    98  	const l2ExplorerRPCComponentName = "l2-explorer-json-rpc"
    99  
   100  	var err error
   101  	err = operations.Teardown()
   102  	require.NoError(t, err)
   103  
   104  	defer func() {
   105  		require.NoError(t, operations.Teardown())
   106  		require.NoError(t, operations.StopComponent(l2ExplorerRPCComponentName))
   107  	}()
   108  
   109  	ctx := context.Background()
   110  	opsCfg := operations.GetDefaultOperationsConfig()
   111  	opsMan, err := operations.NewManager(ctx, opsCfg)
   112  	require.NoError(t, err)
   113  	err = opsMan.Setup()
   114  	require.NoError(t, err)
   115  
   116  	err = operations.StartComponent(l2ExplorerRPCComponentName, func() (bool, error) { return operations.NodeUpCondition(l2NetworkURL) })
   117  	require.NoError(t, err)
   118  
   119  	const l1NetworkName, l2NetworkName = "Local L1", "Local L2"
   120  
   121  	networks := []struct {
   122  		Name         string
   123  		URL          string
   124  		WebSocketURL string
   125  		ChainID      uint64
   126  		PrivateKey   string
   127  	}{
   128  		{
   129  			Name:       l1NetworkName,
   130  			URL:        operations.DefaultL1NetworkURL,
   131  			ChainID:    operations.DefaultL1ChainID,
   132  			PrivateKey: operations.DefaultSequencerPrivateKey,
   133  		},
   134  		{
   135  			Name:       l2NetworkName,
   136  			URL:        l2NetworkURL,
   137  			ChainID:    operations.DefaultL2ChainID,
   138  			PrivateKey: operations.DefaultSequencerPrivateKey,
   139  		},
   140  	}
   141  
   142  	for _, network := range networks {
   143  		log.Debugf(network.Name)
   144  
   145  		response, err := client.JSONRPCCall(network.URL, "debug_traceBlockByNumber", hex.EncodeBig(big.NewInt(999999999999)))
   146  		require.NoError(t, err)
   147  		require.Nil(t, response.Result)
   148  		require.NotNil(t, response.Error)
   149  
   150  		require.Equal(t, -32000, response.Error.Code)
   151  		require.Equal(t, "block #999999999999 not found", response.Error.Message)
   152  		require.Nil(t, response.Error.Data)
   153  	}
   154  }
   155  
   156  func TestDebugTraceBlockByHashNotFoundTx(t *testing.T) {
   157  	if testing.Short() {
   158  		t.Skip()
   159  	}
   160  
   161  	const l2NetworkURL = "http://localhost:8124"
   162  	const l2ExplorerRPCComponentName = "l2-explorer-json-rpc"
   163  
   164  	var err error
   165  	err = operations.Teardown()
   166  	require.NoError(t, err)
   167  
   168  	defer func() {
   169  		require.NoError(t, operations.Teardown())
   170  		require.NoError(t, operations.StopComponent(l2ExplorerRPCComponentName))
   171  	}()
   172  
   173  	ctx := context.Background()
   174  	opsCfg := operations.GetDefaultOperationsConfig()
   175  	opsMan, err := operations.NewManager(ctx, opsCfg)
   176  	require.NoError(t, err)
   177  	err = opsMan.Setup()
   178  	require.NoError(t, err)
   179  
   180  	err = operations.StartComponent(l2ExplorerRPCComponentName, func() (bool, error) { return operations.NodeUpCondition(l2NetworkURL) })
   181  	require.NoError(t, err)
   182  
   183  	const l1NetworkName, l2NetworkName = "Local L1", "Local L2"
   184  
   185  	networks := []struct {
   186  		Name         string
   187  		URL          string
   188  		WebSocketURL string
   189  		ChainID      uint64
   190  		PrivateKey   string
   191  	}{
   192  		{
   193  			Name:       l1NetworkName,
   194  			URL:        operations.DefaultL1NetworkURL,
   195  			ChainID:    operations.DefaultL1ChainID,
   196  			PrivateKey: operations.DefaultSequencerPrivateKey,
   197  		},
   198  		{
   199  			Name:       l2NetworkName,
   200  			URL:        l2NetworkURL,
   201  			ChainID:    operations.DefaultL2ChainID,
   202  			PrivateKey: operations.DefaultSequencerPrivateKey,
   203  		},
   204  	}
   205  
   206  	for _, network := range networks {
   207  		log.Debugf(network.Name)
   208  
   209  		response, err := client.JSONRPCCall(network.URL, "debug_traceBlockByHash", common.Hash{}.String())
   210  		require.NoError(t, err)
   211  		require.Nil(t, response.Result)
   212  		require.NotNil(t, response.Error)
   213  
   214  		require.Equal(t, -32000, response.Error.Code)
   215  		require.Equal(t, "block 0x0000000000000000000000000000000000000000000000000000000000000000 not found", response.Error.Message)
   216  		require.Nil(t, response.Error.Data)
   217  	}
   218  }
   219  
   220  func TestDebugTraceTransaction(t *testing.T) {
   221  	if testing.Short() {
   222  		t.Skip()
   223  	}
   224  
   225  	const l2NetworkURL = "http://localhost:8124"
   226  	const l2ExplorerRPCComponentName = "l2-explorer-json-rpc"
   227  
   228  	var err error
   229  	err = operations.Teardown()
   230  	require.NoError(t, err)
   231  
   232  	defer func() {
   233  		require.NoError(t, operations.Teardown())
   234  		require.NoError(t, operations.StopComponent(l2ExplorerRPCComponentName))
   235  	}()
   236  
   237  	ctx := context.Background()
   238  	opsCfg := operations.GetDefaultOperationsConfig()
   239  	opsMan, err := operations.NewManager(ctx, opsCfg)
   240  	require.NoError(t, err)
   241  	err = opsMan.Setup()
   242  	require.NoError(t, err)
   243  
   244  	err = operations.StartComponent(l2ExplorerRPCComponentName, func() (bool, error) { return operations.NodeUpCondition(l2NetworkURL) })
   245  	require.NoError(t, err)
   246  
   247  	const l1NetworkName, l2NetworkName = "Local L1", "Local L2"
   248  
   249  	networks := []struct {
   250  		Name         string
   251  		URL          string
   252  		WebSocketURL string
   253  		ChainID      uint64
   254  		PrivateKey   string
   255  	}{
   256  		{
   257  			Name:       l1NetworkName,
   258  			URL:        operations.DefaultL1NetworkURL,
   259  			ChainID:    operations.DefaultL1ChainID,
   260  			PrivateKey: operations.DefaultSequencerPrivateKey,
   261  		},
   262  		{
   263  			Name:       l2NetworkName,
   264  			URL:        l2NetworkURL,
   265  			ChainID:    operations.DefaultL2ChainID,
   266  			PrivateKey: operations.DefaultSequencerPrivateKey,
   267  		},
   268  	}
   269  
   270  	results := map[string]json.RawMessage{}
   271  
   272  	type testCase struct {
   273  		name           string
   274  		prepare        func(t *testing.T, ctx context.Context, auth *bind.TransactOpts, client *ethclient.Client) (map[string]interface{}, error)
   275  		createSignedTx func(t *testing.T, ctx context.Context, auth *bind.TransactOpts, client *ethclient.Client, customData map[string]interface{}) (*ethTypes.Transaction, error)
   276  	}
   277  	testCases := []testCase{
   278  		// successful transactions
   279  		{name: "eth transfer", createSignedTx: createEthTransferSignedTx},
   280  		{name: "sc deployment", createSignedTx: createScDeploySignedTx},
   281  		{name: "sc call", prepare: prepareScCall, createSignedTx: createScCallSignedTx},
   282  		{name: "erc20 transfer", prepare: prepareERC20Transfer, createSignedTx: createERC20TransferSignedTx},
   283  		{name: "create", prepare: prepareCreate, createSignedTx: createCreateSignedTx},
   284  		{name: "create2", prepare: prepareCreate, createSignedTx: createCreate2SignedTx},
   285  		{name: "call", prepare: prepareCalls, createSignedTx: createCallSignedTx},
   286  		{name: "delegate call", prepare: prepareCalls, createSignedTx: createDelegateCallSignedTx},
   287  		{name: "multi call", prepare: prepareCalls, createSignedTx: createMultiCallSignedTx},
   288  		{name: "pre ecrecover 0", prepare: prepareCalls, createSignedTx: createPreEcrecover0SignedTx},
   289  		{name: "chain call", prepare: prepareChainCalls, createSignedTx: createChainCallSignedTx},
   290  
   291  		// failed transactions
   292  		{name: "sc deployment reverted", createSignedTx: createScDeployRevertedSignedTx},
   293  		{name: "sc call reverted", prepare: prepareScCallReverted, createSignedTx: createScCallRevertedSignedTx},
   294  		{name: "erc20 transfer reverted", prepare: prepareERC20TransferReverted, createSignedTx: createERC20TransferRevertedSignedTx},
   295  	}
   296  
   297  	privateKey, err := crypto.GenerateKey()
   298  	require.NoError(t, err)
   299  	for _, network := range networks {
   300  		auth, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(0).SetUint64(network.ChainID))
   301  		require.NoError(t, err)
   302  
   303  		ethereumClient := operations.MustGetClient(network.URL)
   304  		sourceAuth := operations.MustGetAuth(network.PrivateKey, network.ChainID)
   305  
   306  		nonce, err := ethereumClient.NonceAt(ctx, sourceAuth.From, nil)
   307  		require.NoError(t, err)
   308  
   309  		balance, err := ethereumClient.BalanceAt(ctx, sourceAuth.From, nil)
   310  		require.NoError(t, err)
   311  
   312  		gasPrice, err := ethereumClient.SuggestGasPrice(ctx)
   313  		require.NoError(t, err)
   314  
   315  		value := big.NewInt(0).Quo(balance, big.NewInt(2))
   316  
   317  		gas, err := ethereumClient.EstimateGas(ctx, ethereum.CallMsg{
   318  			From:     sourceAuth.From,
   319  			To:       &auth.From,
   320  			GasPrice: gasPrice,
   321  			Value:    value,
   322  		})
   323  		require.NoError(t, err)
   324  
   325  		tx := ethTypes.NewTx(&ethTypes.LegacyTx{
   326  			To:       &auth.From,
   327  			Nonce:    nonce,
   328  			GasPrice: gasPrice,
   329  			Value:    value,
   330  			Gas:      gas,
   331  		})
   332  
   333  		signedTx, err := sourceAuth.Signer(sourceAuth.From, tx)
   334  		require.NoError(t, err)
   335  
   336  		err = ethereumClient.SendTransaction(ctx, signedTx)
   337  		require.NoError(t, err)
   338  
   339  		err = operations.WaitTxToBeMined(ctx, ethereumClient, signedTx, operations.DefaultTimeoutTxToBeMined)
   340  		require.NoError(t, err)
   341  	}
   342  
   343  	for _, tc := range testCases {
   344  		t.Run(tc.name, func(t *testing.T) {
   345  			log.Debug("************************ ", tc.name, " ************************")
   346  
   347  			for _, network := range networks {
   348  				log.Debug("------------------------ ", network.Name, " ------------------------")
   349  				ethereumClient := operations.MustGetClient(network.URL)
   350  				auth, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(0).SetUint64(network.ChainID))
   351  				require.NoError(t, err)
   352  
   353  				var customData map[string]interface{}
   354  				if tc.prepare != nil {
   355  					customData, err = tc.prepare(t, ctx, auth, ethereumClient)
   356  					require.NoError(t, err)
   357  				}
   358  
   359  				signedTx, err := tc.createSignedTx(t, ctx, auth, ethereumClient, customData)
   360  				require.NoError(t, err)
   361  
   362  				balance, err := ethereumClient.BalanceAt(ctx, auth.From, nil)
   363  				require.NoError(t, err)
   364  
   365  				log.Debugf("balance of %v: %v", auth.From, balance.String())
   366  
   367  				err = ethereumClient.SendTransaction(ctx, signedTx)
   368  				require.NoError(t, err)
   369  
   370  				log.Debugf("tx sent: %v", signedTx.Hash().String())
   371  
   372  				err = operations.WaitTxToBeMined(ctx, ethereumClient, signedTx, operations.DefaultTimeoutTxToBeMined)
   373  				if err != nil && !strings.HasPrefix(err.Error(), "transaction has failed, reason:") {
   374  					require.NoError(t, err)
   375  				}
   376  
   377  				debugOptions := map[string]interface{}{
   378  					"disableStorage":   false,
   379  					"disableStack":     false,
   380  					"enableMemory":     true,
   381  					"enableReturnData": true,
   382  				}
   383  
   384  				response, err := client.JSONRPCCall(network.URL, "debug_traceTransaction", signedTx.Hash().String(), debugOptions)
   385  				require.NoError(t, err)
   386  				require.Nil(t, response.Error)
   387  				require.NotNil(t, response.Result)
   388  
   389  				results[network.Name] = response.Result
   390  
   391  				saveTraceResultToFile(t, tc.name, network.Name, signedTx, response.Result, true)
   392  			}
   393  
   394  			referenceValueMap := map[string]interface{}{}
   395  			err = json.Unmarshal(results[l1NetworkName], &referenceValueMap)
   396  			require.NoError(t, err)
   397  
   398  			referenceStructLogsMap := referenceValueMap["structLogs"].([]interface{})
   399  
   400  			for networkName, result := range results {
   401  				if networkName == l1NetworkName {
   402  					continue
   403  				}
   404  
   405  				resultMap := map[string]interface{}{}
   406  				err = json.Unmarshal(result, &resultMap)
   407  				require.NoError(t, err)
   408  
   409  				require.Equal(t, referenceValueMap["failed"], resultMap["failed"], fmt.Sprintf("invalid `failed` for network %s", networkName))
   410  
   411  				resultStructLogsMap := resultMap["structLogs"].([]interface{})
   412  				require.Equal(t, len(referenceStructLogsMap), len(resultStructLogsMap))
   413  
   414  				for structLogIndex := range referenceStructLogsMap {
   415  					referenceStructLogMap := referenceStructLogsMap[structLogIndex].(map[string]interface{})
   416  					resultStructLogMap := resultStructLogsMap[structLogIndex].(map[string]interface{})
   417  
   418  					require.Equal(t, referenceStructLogMap["pc"], resultStructLogMap["pc"], fmt.Sprintf("invalid struct log pc for network %s", networkName))
   419  					require.Equal(t, referenceStructLogMap["op"], resultStructLogMap["op"], fmt.Sprintf("invalid struct log op for network %s", networkName))
   420  					require.Equal(t, referenceStructLogMap["depth"], resultStructLogMap["depth"], fmt.Sprintf("invalid struct log depth for network %s", networkName))
   421  
   422  					pc := referenceStructLogMap["pc"]
   423  					op := referenceStructLogMap["op"]
   424  
   425  					referenceStack, found := referenceStructLogMap["stack"].([]interface{})
   426  					if found {
   427  						resultStack := resultStructLogMap["stack"].([]interface{})
   428  
   429  						require.Equal(t, len(referenceStack), len(resultStack), fmt.Sprintf("stack size doesn't match for pc %v op %v", pc, op))
   430  						for stackIndex := range referenceStack {
   431  							require.Equal(t, referenceStack[stackIndex], resultStack[stackIndex], fmt.Sprintf("stack index %v doesn't match for pc %v op %v", stackIndex, pc, op))
   432  						}
   433  					}
   434  
   435  					referenceMemory, found := referenceStructLogMap["memory"].([]interface{})
   436  					if found {
   437  						resultMemory := resultStructLogMap["memory"].([]interface{})
   438  
   439  						require.Equal(t, len(referenceMemory), len(resultMemory), fmt.Sprintf("memory size doesn't match for pc %v op %v", pc, op))
   440  						for memoryIndex := range referenceMemory {
   441  							require.Equal(t, referenceMemory[memoryIndex], resultMemory[memoryIndex], fmt.Sprintf("memory index %v doesn't match for pc %v op %v", memoryIndex, pc, op))
   442  						}
   443  					}
   444  
   445  					referenceStorage, found := referenceStructLogMap["storage"].(map[string]interface{})
   446  					if found {
   447  						resultStorage := resultStructLogMap["storage"].(map[string]interface{})
   448  
   449  						require.Equal(t, len(referenceStorage), len(resultStorage), fmt.Sprintf("storage size doesn't match for pc %v op %v", pc, op))
   450  						for storageKey, referenceStorageValue := range referenceStorage {
   451  							resultStorageValue, found := resultStorage[storageKey]
   452  							require.True(t, found, "storage address not found")
   453  							require.Equal(t, referenceStorageValue, resultStorageValue, fmt.Sprintf("storage value doesn't match for address %v for pc %v op %v", storageKey, pc, op))
   454  						}
   455  					}
   456  				}
   457  			}
   458  		})
   459  	}
   460  }
   461  
   462  func TestDebugTraceBlock(t *testing.T) {
   463  	if testing.Short() {
   464  		t.Skip()
   465  	}
   466  
   467  	const l2NetworkURL = "http://localhost:8124"
   468  	const l2ExplorerRPCComponentName = "l2-explorer-json-rpc"
   469  
   470  	var err error
   471  	err = operations.Teardown()
   472  	require.NoError(t, err)
   473  
   474  	defer func() {
   475  		require.NoError(t, operations.Teardown())
   476  		require.NoError(t, operations.StopComponent(l2ExplorerRPCComponentName))
   477  	}()
   478  
   479  	ctx := context.Background()
   480  	opsCfg := operations.GetDefaultOperationsConfig()
   481  	opsMan, err := operations.NewManager(ctx, opsCfg)
   482  	require.NoError(t, err)
   483  	err = opsMan.Setup()
   484  	require.NoError(t, err)
   485  
   486  	err = operations.StartComponent(l2ExplorerRPCComponentName, func() (bool, error) { return operations.NodeUpCondition(l2NetworkURL) })
   487  	require.NoError(t, err)
   488  
   489  	const l1NetworkName, l2NetworkName = "Local L1", "Local L2"
   490  
   491  	networks := []struct {
   492  		Name         string
   493  		URL          string
   494  		WebSocketURL string
   495  		ChainID      uint64
   496  		PrivateKey   string
   497  	}{
   498  		{
   499  			Name:       l1NetworkName,
   500  			URL:        operations.DefaultL1NetworkURL,
   501  			ChainID:    operations.DefaultL1ChainID,
   502  			PrivateKey: operations.DefaultSequencerPrivateKey,
   503  		},
   504  		{
   505  			Name:       l2NetworkName,
   506  			URL:        l2NetworkURL,
   507  			ChainID:    operations.DefaultL2ChainID,
   508  			PrivateKey: operations.DefaultSequencerPrivateKey,
   509  		},
   510  	}
   511  
   512  	results := map[string]json.RawMessage{}
   513  
   514  	type testCase struct {
   515  		name              string
   516  		blockNumberOrHash string
   517  		prepare           func(t *testing.T, ctx context.Context, auth *bind.TransactOpts, client *ethclient.Client) (map[string]interface{}, error)
   518  		createSignedTx    func(t *testing.T, ctx context.Context, auth *bind.TransactOpts, client *ethclient.Client, customData map[string]interface{}) (*ethTypes.Transaction, error)
   519  	}
   520  	testCases := []testCase{
   521  		// successful transactions
   522  		// by block number
   523  		{name: "eth transfer by number", blockNumberOrHash: "number", createSignedTx: createEthTransferSignedTx},
   524  		{name: "sc deployment by number", blockNumberOrHash: "number", createSignedTx: createScDeploySignedTx},
   525  		{name: "sc call by number", blockNumberOrHash: "number", prepare: prepareScCall, createSignedTx: createScCallSignedTx},
   526  		{name: "erc20 transfer by number", blockNumberOrHash: "number", prepare: prepareERC20Transfer, createSignedTx: createERC20TransferSignedTx},
   527  		// by block hash
   528  		{name: "eth transfer by hash", blockNumberOrHash: "hash", createSignedTx: createEthTransferSignedTx},
   529  		{name: "sc deployment by hash", blockNumberOrHash: "hash", createSignedTx: createScDeploySignedTx},
   530  		{name: "sc call by hash", blockNumberOrHash: "hash", prepare: prepareScCall, createSignedTx: createScCallSignedTx},
   531  		{name: "erc20 transfer by hash", blockNumberOrHash: "hash", prepare: prepareERC20Transfer, createSignedTx: createERC20TransferSignedTx},
   532  
   533  		// failed transactions
   534  		// by block number
   535  		{name: "sc deployment reverted by number", blockNumberOrHash: "number", createSignedTx: createScDeployRevertedSignedTx},
   536  		{name: "sc call reverted by number", blockNumberOrHash: "number", prepare: prepareScCallReverted, createSignedTx: createScCallRevertedSignedTx},
   537  		{name: "erc20 transfer reverted by number", blockNumberOrHash: "number", prepare: prepareERC20TransferReverted, createSignedTx: createERC20TransferRevertedSignedTx},
   538  		// by block hash
   539  		{name: "sc deployment reverted by hash", blockNumberOrHash: "hash", createSignedTx: createScDeployRevertedSignedTx},
   540  		{name: "sc call reverted by hash", blockNumberOrHash: "hash", prepare: prepareScCallReverted, createSignedTx: createScCallRevertedSignedTx},
   541  		{name: "erc20 transfer reverted by hash", blockNumberOrHash: "hash", prepare: prepareERC20TransferReverted, createSignedTx: createERC20TransferRevertedSignedTx},
   542  	}
   543  
   544  	for _, tc := range testCases {
   545  		t.Run(tc.name, func(t *testing.T) {
   546  			log.Debug("************************ ", tc.name, " ************************")
   547  
   548  			for _, network := range networks {
   549  				log.Debug("------------------------ ", network.Name, " ------------------------")
   550  				ethereumClient := operations.MustGetClient(network.URL)
   551  				auth := operations.MustGetAuth(network.PrivateKey, network.ChainID)
   552  
   553  				var customData map[string]interface{}
   554  				if tc.prepare != nil {
   555  					customData, err = tc.prepare(t, ctx, auth, ethereumClient)
   556  					require.NoError(t, err)
   557  				}
   558  
   559  				signedTx, err := tc.createSignedTx(t, ctx, auth, ethereumClient, customData)
   560  				require.NoError(t, err)
   561  
   562  				err = ethereumClient.SendTransaction(ctx, signedTx)
   563  				require.NoError(t, err)
   564  
   565  				log.Debugf("tx sent: %v", signedTx.Hash().String())
   566  
   567  				err = operations.WaitTxToBeMined(ctx, ethereumClient, signedTx, operations.DefaultTimeoutTxToBeMined)
   568  				if err != nil && !strings.HasPrefix(err.Error(), "transaction has failed, reason:") {
   569  					require.NoError(t, err)
   570  				}
   571  
   572  				receipt, err := ethereumClient.TransactionReceipt(ctx, signedTx.Hash())
   573  				require.NoError(t, err)
   574  
   575  				debugOptions := map[string]interface{}{
   576  					"disableStorage":   false,
   577  					"disableStack":     false,
   578  					"enableMemory":     true,
   579  					"enableReturnData": true,
   580  				}
   581  
   582  				var response types.Response
   583  				if tc.blockNumberOrHash == "number" {
   584  					response, err = client.JSONRPCCall(network.URL, "debug_traceBlockByNumber", hex.EncodeBig(receipt.BlockNumber), debugOptions)
   585  				} else {
   586  					response, err = client.JSONRPCCall(network.URL, "debug_traceBlockByHash", receipt.BlockHash.String(), debugOptions)
   587  				}
   588  				require.NoError(t, err)
   589  				require.Nil(t, response.Error)
   590  				require.NotNil(t, response.Result)
   591  
   592  				results[network.Name] = response.Result
   593  			}
   594  
   595  			referenceTransactions := []interface{}{}
   596  			err = json.Unmarshal(results[l1NetworkName], &referenceTransactions)
   597  			require.NoError(t, err)
   598  
   599  			for networkName, result := range results {
   600  				if networkName == l1NetworkName {
   601  					continue
   602  				}
   603  
   604  				resultTransactions := []interface{}{}
   605  				err = json.Unmarshal(result, &resultTransactions)
   606  				require.NoError(t, err)
   607  
   608  				for transactionIndex := range referenceTransactions {
   609  					referenceTransactionMap := referenceTransactions[transactionIndex].(map[string]interface{})
   610  					referenceResultMap := referenceTransactionMap["result"].(map[string]interface{})
   611  					referenceStructLogsMap := referenceResultMap["structLogs"].([]interface{})
   612  
   613  					resultTransactionMap := resultTransactions[transactionIndex].(map[string]interface{})
   614  					resultResultMap := resultTransactionMap["result"].(map[string]interface{})
   615  					resultStructLogsMap := resultResultMap["structLogs"].([]interface{})
   616  
   617  					require.Equal(t, len(referenceStructLogsMap), len(resultStructLogsMap))
   618  
   619  					for structLogIndex := range referenceStructLogsMap {
   620  						referenceStructLogMap := referenceStructLogsMap[structLogIndex].(map[string]interface{})
   621  						resultStructLogMap := resultStructLogsMap[structLogIndex].(map[string]interface{})
   622  
   623  						require.Equal(t, referenceStructLogMap["pc"], resultStructLogMap["pc"], fmt.Sprintf("invalid struct log pc for network %s", networkName))
   624  						require.Equal(t, referenceStructLogMap["op"], resultStructLogMap["op"], fmt.Sprintf("invalid struct log op for network %s", networkName))
   625  						require.Equal(t, referenceStructLogMap["depth"], resultStructLogMap["depth"], fmt.Sprintf("invalid struct log depth for network %s", networkName))
   626  
   627  						pc := referenceStructLogMap["pc"]
   628  						op := referenceStructLogMap["op"]
   629  
   630  						referenceStack, found := referenceStructLogMap["stack"].([]interface{})
   631  						if found {
   632  							resultStack := resultStructLogMap["stack"].([]interface{})
   633  
   634  							require.Equal(t, len(referenceStack), len(resultStack), fmt.Sprintf("stack size doesn't match for pc %v op %v", pc, op))
   635  							for stackIndex := range referenceStack {
   636  								require.Equal(t, referenceStack[stackIndex], resultStack[stackIndex], fmt.Sprintf("stack index %v doesn't match for pc %v op %v", stackIndex, pc, op))
   637  							}
   638  						}
   639  
   640  						referenceMemory, found := referenceStructLogMap["memory"].([]interface{})
   641  						if found {
   642  							resultMemory := resultStructLogMap["memory"].([]interface{})
   643  
   644  							require.Equal(t, len(referenceMemory), len(resultMemory), fmt.Sprintf("memory size doesn't match for pc %v op %v", pc, op))
   645  							for memoryIndex := range referenceMemory {
   646  								require.Equal(t, referenceMemory[memoryIndex], resultMemory[memoryIndex], fmt.Sprintf("memory index %v doesn't match for pc %v op %v", memoryIndex, pc, op))
   647  							}
   648  						}
   649  
   650  						referenceStorage, found := referenceStructLogMap["storage"].(map[string]interface{})
   651  						if found {
   652  							resultStorage := resultStructLogMap["storage"].(map[string]interface{})
   653  
   654  							require.Equal(t, len(referenceStorage), len(resultStorage), fmt.Sprintf("storage size doesn't match for pc %v op %v", pc, op))
   655  							for storageKey, referenceStorageValue := range referenceStorage {
   656  								resultStorageValue, found := resultStorage[storageKey]
   657  								require.True(t, found, "storage address not found")
   658  								require.Equal(t, referenceStorageValue, resultStorageValue, fmt.Sprintf("storage value doesn't match for address %v for pc %v op %v", storageKey, pc, op))
   659  							}
   660  						}
   661  					}
   662  				}
   663  			}
   664  		})
   665  	}
   666  }