github.com/0xPolygon/supernets2-node@v0.0.0-20230711153321-2fe574524eaa/test/e2e/debug_calltracer_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  	ethTypes "github.com/ethereum/go-ethereum/core/types"
    19  	"github.com/ethereum/go-ethereum/crypto"
    20  	"github.com/ethereum/go-ethereum/ethclient"
    21  	"github.com/stretchr/testify/require"
    22  )
    23  
    24  func TestDebugTraceTransactionCallTracer(t *testing.T) {
    25  	if testing.Short() {
    26  		t.Skip()
    27  	}
    28  
    29  	const l2NetworkURL = "http://localhost:8124"
    30  	const l2ExplorerRPCComponentName = "l2-explorer-json-rpc"
    31  
    32  	var err error
    33  	err = operations.Teardown()
    34  	require.NoError(t, err)
    35  
    36  	defer func() {
    37  		require.NoError(t, operations.Teardown())
    38  		require.NoError(t, operations.StopComponent(l2ExplorerRPCComponentName))
    39  	}()
    40  
    41  	ctx := context.Background()
    42  	opsCfg := operations.GetDefaultOperationsConfig()
    43  	opsMan, err := operations.NewManager(ctx, opsCfg)
    44  	require.NoError(t, err)
    45  	err = opsMan.Setup()
    46  	require.NoError(t, err)
    47  
    48  	err = operations.StartComponent(l2ExplorerRPCComponentName, func() (bool, error) { return operations.NodeUpCondition(l2NetworkURL) })
    49  	require.NoError(t, err)
    50  
    51  	const l1NetworkName, l2NetworkName = "Local L1", "Local L2"
    52  
    53  	networks := []struct {
    54  		Name         string
    55  		URL          string
    56  		WebSocketURL string
    57  		ChainID      uint64
    58  		PrivateKey   string
    59  	}{
    60  		{
    61  			Name:       l1NetworkName,
    62  			URL:        operations.DefaultL1NetworkURL,
    63  			ChainID:    operations.DefaultL1ChainID,
    64  			PrivateKey: operations.DefaultSequencerPrivateKey,
    65  		},
    66  		{
    67  			Name:       l2NetworkName,
    68  			URL:        l2NetworkURL,
    69  			ChainID:    operations.DefaultL2ChainID,
    70  			PrivateKey: operations.DefaultSequencerPrivateKey,
    71  		},
    72  	}
    73  
    74  	results := map[string]json.RawMessage{}
    75  
    76  	type testCase struct {
    77  		name           string
    78  		prepare        func(t *testing.T, ctx context.Context, auth *bind.TransactOpts, client *ethclient.Client) (map[string]interface{}, error)
    79  		createSignedTx func(t *testing.T, ctx context.Context, auth *bind.TransactOpts, client *ethclient.Client, customData map[string]interface{}) (*ethTypes.Transaction, error)
    80  	}
    81  	testCases := []testCase{
    82  		// successful transactions
    83  		{name: "eth transfer", createSignedTx: createEthTransferSignedTx},
    84  		{name: "sc deployment", createSignedTx: createScDeploySignedTx},
    85  		{name: "sc call", prepare: prepareScCall, createSignedTx: createScCallSignedTx},
    86  		{name: "erc20 transfer", prepare: prepareERC20Transfer, createSignedTx: createERC20TransferSignedTx},
    87  		{name: "create", prepare: prepareCreate, createSignedTx: createCreateSignedTx},
    88  		{name: "create2", prepare: prepareCreate, createSignedTx: createCreate2SignedTx},
    89  		{name: "call", prepare: prepareCalls, createSignedTx: createCallSignedTx},
    90  		{name: "delegate call", prepare: prepareCalls, createSignedTx: createDelegateCallSignedTx},
    91  		{name: "multi call", prepare: prepareCalls, createSignedTx: createMultiCallSignedTx},
    92  		{name: "pre ecrecover 0", prepare: prepareCalls, createSignedTx: createPreEcrecover0SignedTx},
    93  		{name: "chain call", prepare: prepareChainCalls, createSignedTx: createChainCallSignedTx},
    94  
    95  		// failed transactions
    96  		{name: "sc deployment reverted", createSignedTx: createScDeployRevertedSignedTx},
    97  		{name: "sc call reverted", prepare: prepareScCallReverted, createSignedTx: createScCallRevertedSignedTx},
    98  		{name: "erc20 transfer reverted", prepare: prepareERC20TransferReverted, createSignedTx: createERC20TransferRevertedSignedTx},
    99  		{name: "invalid static call less parameters", prepare: prepareCalls, createSignedTx: createInvalidStaticCallLessParametersSignedTx},
   100  		{name: "invalid static call more parameters", prepare: prepareCalls, createSignedTx: createInvalidStaticCallMoreParametersSignedTx},
   101  		{name: "invalid static call with inner call", prepare: prepareCalls, createSignedTx: createInvalidStaticCallWithInnerCallSignedTx},
   102  	}
   103  	privateKey, err := crypto.GenerateKey()
   104  	require.NoError(t, err)
   105  	for _, network := range networks {
   106  		auth, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(0).SetUint64(network.ChainID))
   107  		require.NoError(t, err)
   108  
   109  		ethereumClient := operations.MustGetClient(network.URL)
   110  		sourceAuth := operations.MustGetAuth(network.PrivateKey, network.ChainID)
   111  
   112  		nonce, err := ethereumClient.NonceAt(ctx, sourceAuth.From, nil)
   113  		require.NoError(t, err)
   114  
   115  		balance, err := ethereumClient.BalanceAt(ctx, sourceAuth.From, nil)
   116  		require.NoError(t, err)
   117  
   118  		gasPrice, err := ethereumClient.SuggestGasPrice(ctx)
   119  		require.NoError(t, err)
   120  
   121  		value := big.NewInt(0).Quo(balance, big.NewInt(2))
   122  
   123  		gas, err := ethereumClient.EstimateGas(ctx, ethereum.CallMsg{
   124  			From:     sourceAuth.From,
   125  			To:       &auth.From,
   126  			GasPrice: gasPrice,
   127  			Value:    value,
   128  		})
   129  		require.NoError(t, err)
   130  
   131  		tx := ethTypes.NewTx(&ethTypes.LegacyTx{
   132  			To:       &auth.From,
   133  			Nonce:    nonce,
   134  			GasPrice: gasPrice,
   135  			Value:    value,
   136  			Gas:      gas,
   137  		})
   138  
   139  		signedTx, err := sourceAuth.Signer(sourceAuth.From, tx)
   140  		require.NoError(t, err)
   141  
   142  		err = ethereumClient.SendTransaction(ctx, signedTx)
   143  		require.NoError(t, err)
   144  
   145  		err = operations.WaitTxToBeMined(ctx, ethereumClient, signedTx, operations.DefaultTimeoutTxToBeMined)
   146  		require.NoError(t, err)
   147  	}
   148  
   149  	for _, tc := range testCases {
   150  		t.Run(tc.name, func(t *testing.T) {
   151  			log.Debug("************************ ", tc.name, " ************************")
   152  
   153  			for _, network := range networks {
   154  				log.Debug("------------------------ ", network.Name, " ------------------------")
   155  				ethereumClient := operations.MustGetClient(network.URL)
   156  				auth, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(0).SetUint64(network.ChainID))
   157  				require.NoError(t, err)
   158  
   159  				var customData map[string]interface{}
   160  				if tc.prepare != nil {
   161  					customData, err = tc.prepare(t, ctx, auth, ethereumClient)
   162  					require.NoError(t, err)
   163  				}
   164  
   165  				signedTx, err := tc.createSignedTx(t, ctx, auth, ethereumClient, customData)
   166  				require.NoError(t, err)
   167  
   168  				err = ethereumClient.SendTransaction(ctx, signedTx)
   169  				require.NoError(t, err)
   170  
   171  				log.Debugf("tx sent: %v", signedTx.Hash().String())
   172  
   173  				err = operations.WaitTxToBeMined(ctx, ethereumClient, signedTx, operations.DefaultTimeoutTxToBeMined)
   174  				if err != nil && !strings.HasPrefix(err.Error(), "transaction has failed, reason:") {
   175  					require.NoError(t, err)
   176  				}
   177  
   178  				debugOptions := map[string]interface{}{
   179  					"tracer": "callTracer",
   180  					"tracerConfig": map[string]interface{}{
   181  						"onlyTopCall": false,
   182  						"withLog":     true,
   183  					},
   184  				}
   185  
   186  				response, err := client.JSONRPCCall(network.URL, "debug_traceTransaction", signedTx.Hash().String(), debugOptions)
   187  				require.NoError(t, err)
   188  				require.Nil(t, response.Error)
   189  				require.NotNil(t, response.Result)
   190  
   191  				results[network.Name] = response.Result
   192  				log.Debug(string(response.Result))
   193  
   194  				saveTraceResultToFile(t, tc.name, network.Name, signedTx, response.Result, true)
   195  			}
   196  
   197  			referenceValueMap := map[string]interface{}{}
   198  			err = json.Unmarshal(results[l1NetworkName], &referenceValueMap)
   199  			require.NoError(t, err)
   200  
   201  			for networkName, result := range results {
   202  				if networkName == l1NetworkName {
   203  					continue
   204  				}
   205  
   206  				resultMap := map[string]interface{}{}
   207  				err = json.Unmarshal(result, &resultMap)
   208  				require.NoError(t, err)
   209  
   210  				compareCallFrame(t, referenceValueMap, resultMap, networkName)
   211  			}
   212  		})
   213  	}
   214  }
   215  
   216  func compareCallFrame(t *testing.T, referenceValueMap, resultMap map[string]interface{}, networkName string) {
   217  	require.Equal(t, referenceValueMap["from"], resultMap["from"], fmt.Sprintf("invalid `from` for network %s", networkName))
   218  	require.Equal(t, referenceValueMap["input"], resultMap["input"], fmt.Sprintf("invalid `input` for network %s", networkName))
   219  	require.Equal(t, referenceValueMap["output"], resultMap["output"], fmt.Sprintf("invalid `output` for network %s", networkName))
   220  	require.Equal(t, referenceValueMap["value"], resultMap["value"], fmt.Sprintf("invalid `value` for network %s", networkName))
   221  	require.Equal(t, referenceValueMap["type"], resultMap["type"], fmt.Sprintf("invalid `type` for network %s", networkName))
   222  	require.Equal(t, referenceValueMap["error"], resultMap["error"], fmt.Sprintf("invalid `error` for network %s", networkName))
   223  	require.Equal(t, referenceValueMap["revertReason"], resultMap["revertReason"], fmt.Sprintf("invalid `revertReason` for network %s", networkName))
   224  
   225  	referenceLogs, found := referenceValueMap["logs"].([]interface{})
   226  	if found {
   227  		resultLogs := resultMap["logs"].([]interface{})
   228  		require.Equal(t, len(referenceLogs), len(resultLogs), "logs size doesn't match")
   229  		for logIndex := range referenceLogs {
   230  			referenceLog := referenceLogs[logIndex].(map[string]interface{})
   231  			resultLog := resultLogs[logIndex].(map[string]interface{})
   232  
   233  			require.Equal(t, referenceLog["data"], resultLog["data"], fmt.Sprintf("log index %v data doesn't match", logIndex))
   234  			referenceTopics, found := referenceLog["topics"].([]interface{})
   235  			if found {
   236  				resultTopics := resultLog["topics"].([]interface{})
   237  				require.Equal(t, len(referenceTopics), len(resultTopics), "log index %v topics size doesn't match", logIndex)
   238  				for topicIndex := range referenceTopics {
   239  					require.Equal(t, referenceTopics[topicIndex], resultTopics[topicIndex], fmt.Sprintf("log index %v topic index %v doesn't match", logIndex, topicIndex))
   240  				}
   241  			}
   242  		}
   243  	}
   244  
   245  	referenceCalls, found := referenceValueMap["calls"].([]interface{})
   246  	if found {
   247  		resultCalls := resultMap["calls"].([]interface{})
   248  		require.Equal(t, len(referenceCalls), len(resultCalls), "logs size doesn't match")
   249  		for callIndex := range referenceCalls {
   250  			referenceCall := referenceCalls[callIndex].(map[string]interface{})
   251  			resultCall := resultCalls[callIndex].(map[string]interface{})
   252  
   253  			compareCallFrame(t, referenceCall, resultCall, networkName)
   254  		}
   255  	}
   256  }
   257  
   258  func TestDebugTraceBlockCallTracer(t *testing.T) {
   259  	if testing.Short() {
   260  		t.Skip()
   261  	}
   262  
   263  	const l2NetworkURL = "http://localhost:8124"
   264  	const l2ExplorerRPCComponentName = "l2-explorer-json-rpc"
   265  
   266  	var err error
   267  	err = operations.Teardown()
   268  	require.NoError(t, err)
   269  
   270  	defer func() {
   271  		require.NoError(t, operations.Teardown())
   272  		require.NoError(t, operations.StopComponent(l2ExplorerRPCComponentName))
   273  	}()
   274  
   275  	ctx := context.Background()
   276  	opsCfg := operations.GetDefaultOperationsConfig()
   277  	opsMan, err := operations.NewManager(ctx, opsCfg)
   278  	require.NoError(t, err)
   279  	err = opsMan.Setup()
   280  	require.NoError(t, err)
   281  
   282  	err = operations.StartComponent(l2ExplorerRPCComponentName, func() (bool, error) { return operations.NodeUpCondition(l2NetworkURL) })
   283  	require.NoError(t, err)
   284  
   285  	const l1NetworkName, l2NetworkName = "Local L1", "Local L2"
   286  
   287  	networks := []struct {
   288  		Name         string
   289  		URL          string
   290  		WebSocketURL string
   291  		ChainID      uint64
   292  		PrivateKey   string
   293  	}{
   294  		{
   295  			Name:       l1NetworkName,
   296  			URL:        operations.DefaultL1NetworkURL,
   297  			ChainID:    operations.DefaultL1ChainID,
   298  			PrivateKey: operations.DefaultSequencerPrivateKey,
   299  		},
   300  		{
   301  			Name:       l2NetworkName,
   302  			URL:        l2NetworkURL,
   303  			ChainID:    operations.DefaultL2ChainID,
   304  			PrivateKey: operations.DefaultSequencerPrivateKey,
   305  		},
   306  	}
   307  
   308  	results := map[string]json.RawMessage{}
   309  
   310  	type testCase struct {
   311  		name              string
   312  		blockNumberOrHash string
   313  		prepare           func(t *testing.T, ctx context.Context, auth *bind.TransactOpts, client *ethclient.Client) (map[string]interface{}, error)
   314  		createSignedTx    func(t *testing.T, ctx context.Context, auth *bind.TransactOpts, client *ethclient.Client, customData map[string]interface{}) (*ethTypes.Transaction, error)
   315  	}
   316  	testCases := []testCase{
   317  		// successful transactions
   318  		// by block number
   319  		{name: "eth transfer by number", blockNumberOrHash: "number", createSignedTx: createEthTransferSignedTx},
   320  		{name: "sc deployment by number", blockNumberOrHash: "number", createSignedTx: createScDeploySignedTx},
   321  		{name: "sc call by number", blockNumberOrHash: "number", prepare: prepareScCall, createSignedTx: createScCallSignedTx},
   322  		{name: "erc20 transfer by number", blockNumberOrHash: "number", prepare: prepareERC20Transfer, createSignedTx: createERC20TransferSignedTx},
   323  		// by block hash
   324  		{name: "eth transfer by hash", blockNumberOrHash: "hash", createSignedTx: createEthTransferSignedTx},
   325  		{name: "sc deployment by hash", blockNumberOrHash: "hash", createSignedTx: createScDeploySignedTx},
   326  		{name: "sc call by hash", blockNumberOrHash: "hash", prepare: prepareScCall, createSignedTx: createScCallSignedTx},
   327  		{name: "erc20 transfer by hash", blockNumberOrHash: "hash", prepare: prepareERC20Transfer, createSignedTx: createERC20TransferSignedTx},
   328  
   329  		// failed transactions
   330  		// by block number
   331  		{name: "sc deployment reverted by number", blockNumberOrHash: "number", createSignedTx: createScDeployRevertedSignedTx},
   332  		{name: "sc call reverted by number", blockNumberOrHash: "number", prepare: prepareScCallReverted, createSignedTx: createScCallRevertedSignedTx},
   333  		{name: "erc20 transfer reverted by number", blockNumberOrHash: "number", prepare: prepareERC20TransferReverted, createSignedTx: createERC20TransferRevertedSignedTx},
   334  		// by block hash
   335  		{name: "sc deployment reverted by hash", blockNumberOrHash: "hash", createSignedTx: createScDeployRevertedSignedTx},
   336  		{name: "sc call reverted by hash", blockNumberOrHash: "hash", prepare: prepareScCallReverted, createSignedTx: createScCallRevertedSignedTx},
   337  		{name: "erc20 transfer reverted by hash", blockNumberOrHash: "hash", prepare: prepareERC20TransferReverted, createSignedTx: createERC20TransferRevertedSignedTx},
   338  	}
   339  
   340  	for _, tc := range testCases {
   341  		t.Run(tc.name, func(t *testing.T) {
   342  			log.Debug("************************ ", tc.name, " ************************")
   343  
   344  			for _, network := range networks {
   345  				log.Debug("------------------------ ", network.Name, " ------------------------")
   346  				ethereumClient := operations.MustGetClient(network.URL)
   347  				auth := operations.MustGetAuth(network.PrivateKey, network.ChainID)
   348  
   349  				var customData map[string]interface{}
   350  				if tc.prepare != nil {
   351  					customData, err = tc.prepare(t, ctx, auth, ethereumClient)
   352  					require.NoError(t, err)
   353  				}
   354  
   355  				signedTx, err := tc.createSignedTx(t, ctx, auth, ethereumClient, customData)
   356  				require.NoError(t, err)
   357  
   358  				err = ethereumClient.SendTransaction(ctx, signedTx)
   359  				require.NoError(t, err)
   360  
   361  				log.Debugf("tx sent: %v", signedTx.Hash().String())
   362  
   363  				err = operations.WaitTxToBeMined(ctx, ethereumClient, signedTx, operations.DefaultTimeoutTxToBeMined)
   364  				if err != nil && !strings.HasPrefix(err.Error(), "transaction has failed, reason:") {
   365  					require.NoError(t, err)
   366  				}
   367  
   368  				receipt, err := ethereumClient.TransactionReceipt(ctx, signedTx.Hash())
   369  				require.NoError(t, err)
   370  
   371  				debugOptions := map[string]interface{}{
   372  					"tracer": "callTracer",
   373  					"tracerConfig": map[string]interface{}{
   374  						"onlyTopCall": false,
   375  						"withLog":     true,
   376  					},
   377  				}
   378  
   379  				var response types.Response
   380  				if tc.blockNumberOrHash == "number" {
   381  					response, err = client.JSONRPCCall(network.URL, "debug_traceBlockByNumber", hex.EncodeBig(receipt.BlockNumber), debugOptions)
   382  				} else {
   383  					response, err = client.JSONRPCCall(network.URL, "debug_traceBlockByHash", receipt.BlockHash.String(), debugOptions)
   384  				}
   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  
   392  			referenceTransactions := []interface{}{}
   393  			err = json.Unmarshal(results[l1NetworkName], &referenceTransactions)
   394  			require.NoError(t, err)
   395  
   396  			for networkName, result := range results {
   397  				if networkName == l1NetworkName {
   398  					continue
   399  				}
   400  
   401  				resultTransactions := []interface{}{}
   402  				err = json.Unmarshal(result, &resultTransactions)
   403  				require.NoError(t, err)
   404  
   405  				for transactionIndex := range referenceTransactions {
   406  					referenceTransactionMap := referenceTransactions[transactionIndex].(map[string]interface{})
   407  					resultTransactionMap := resultTransactions[transactionIndex].(map[string]interface{})
   408  
   409  					compareCallFrame(t, referenceTransactionMap, resultTransactionMap, networkName)
   410  				}
   411  			}
   412  		})
   413  	}
   414  }