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(ðTypes.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 }