From 4a8fb8190b915925b779208174379e77ffad1c16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Rodr=C3=ADguez?= Date: Mon, 11 Mar 2024 11:12:17 -0300 Subject: [PATCH] Fix tracing for multiple xcm transactions in the same block (#2701) --- .../src/impl_moonbeam_xcm_call_tracing.rs | 10 +- .../test-trace-ethereum-xcm-3.ts | 139 ++++++++++++++++++ .../tracing-tests/test-trace-filter-reorg.ts | 2 +- .../suites/tracing-tests/test-trace-filter.ts | 2 +- test/suites/tracing-tests/test-trace-gas.ts | 2 +- 5 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 test/suites/tracing-tests/test-trace-ethereum-xcm-3.ts diff --git a/runtime/common/src/impl_moonbeam_xcm_call_tracing.rs b/runtime/common/src/impl_moonbeam_xcm_call_tracing.rs index 87b1c94e58..08d2b06f13 100644 --- a/runtime/common/src/impl_moonbeam_xcm_call_tracing.rs +++ b/runtime/common/src/impl_moonbeam_xcm_call_tracing.rs @@ -62,7 +62,7 @@ macro_rules! impl_moonbeam_xcm_call_tracing { ETHEREUM_XCM_TRACING_STORAGE_KEY ) { // This runtime instance is used for tracing. - Some(transaction) => match transaction { + Some(tracing_status) => match tracing_status { // Tracing a block, all calls are done using environmental. EthereumXcmTracingStatus::Block => { // Each known extrinsic is a new call stack. @@ -96,7 +96,13 @@ macro_rules! impl_moonbeam_xcm_call_tracing { } dispatch_call() }, - _ => unreachable!() + // Tracing a transaction that has already been found and + // executed. There's no need to dispatch the rest of the + // calls. + EthereumXcmTracingStatus::TransactionExited => Ok(crate::PostDispatchInfo { + actual_weight: None, + pays_fee: frame_support::pallet_prelude::Pays::No, + }), }, // This runtime instance is importing a block. None => dispatch_call() diff --git a/test/suites/tracing-tests/test-trace-ethereum-xcm-3.ts b/test/suites/tracing-tests/test-trace-ethereum-xcm-3.ts new file mode 100644 index 0000000000..0bf0eebf63 --- /dev/null +++ b/test/suites/tracing-tests/test-trace-ethereum-xcm-3.ts @@ -0,0 +1,139 @@ +import { beforeAll, customDevRpcRequest, describeSuite, expect } from "@moonwall/cli"; +import { + XcmFragment, + injectHrmpMessage, + descendOriginFromAddress20, + RawXcmMessage, +} from "../../helpers"; +import { hexToNumber, Abi, encodeFunctionData } from "viem"; + +describeSuite({ + id: "T12", + title: "Trace ethereum xcm #3: Multiple xcms in a block", + foundationMethods: "dev", + testCases: ({ context, it }) => { + let incremetorAddress: `0x${string}`; + let incremetorABI: Abi; + const transactionHashes: `0x${string}`[] = []; + + beforeAll(async () => { + const { contractAddress, abi } = await context.deployContract!("Incrementor"); + incremetorAddress = contractAddress; + incremetorABI = abi; + + const { originAddress: originAddress1, descendOriginAddress: descendOriginAddress1 } = + descendOriginFromAddress20(context); + const sendingAddress1 = originAddress1; + const { originAddress: originAddress2, descendOriginAddress: descendOriginAddress2 } = + descendOriginFromAddress20(context, "0x0101010101010101010101010101010101010101", 2); + const sendingAddress2 = originAddress2; + const transferredBalance = 10_000_000_000_000_000_000n; + + // We first fund parachain sovreign accounts + await context.createBlock( + context + .polkadotJs() + .tx.balances.transferAllowDeath(descendOriginAddress1, transferredBalance), + { allowFailures: false } + ); + await context.createBlock( + context + .polkadotJs() + .tx.balances.transferAllowDeath(descendOriginAddress2, transferredBalance), + { allowFailures: false } + ); + + // Get Pallet balances index + const metadata = await context.polkadotJs().rpc.state.getMetadata(); + const balancesPalletIndex = metadata.asLatest.pallets + .find(({ name }) => name.toString() == "Balances")! + .index.toNumber(); + + const xcmTransaction = { + V2: { + gas_limit: 100000, + action: { + Call: incremetorAddress, + }, + value: 0n, + input: encodeFunctionData({ + abi: incremetorABI, + functionName: "incr", + args: [], + }), + access_list: null, + }, + }; + + const transferCall = context.polkadotJs().tx.ethereumXcm.transact(xcmTransaction); + const transferCallEncoded = transferCall?.method.toHex(); + for (const [paraId, sendingAddress] of [ + [1, sendingAddress1], + [2, sendingAddress2], + ]) { + const xcmMessage = new XcmFragment({ + assets: [ + { + multilocation: { + parents: 0, + interior: { + X1: { PalletInstance: balancesPalletIndex }, + }, + }, + fungible: transferredBalance, + }, + ], + weight_limit: { + refTime: 4000000000n, + proofSize: 60000n, + } as any, + descend_origin: sendingAddress, + }) + .descend_origin() + .withdraw_asset() + .buy_execution() + .push_any({ + Transact: { + originKind: "SovereignAccount", + requireWeightAtMost: { + refTime: 3000000000n, + proofSize: 30000n, + }, + call: { + encoded: transferCallEncoded, + }, + }, + }) + .as_v3(); + + // Send an XCM and create block to execute it + await injectHrmpMessage(context, paraId, { + type: "XcmVersionedXcm", + payload: xcmMessage, + } as RawXcmMessage); + } + await context.createBlock(); + + const txHashes = (await context.viem().getBlock({ blockTag: "latest" })).transactions; + expect(txHashes.length).toBe(2); + transactionHashes.push(...txHashes); + }); + + it({ + id: "T01", + title: "should trace ethereum xcm transactions with debug_traceTransaction", + test: async function () { + for (const hash of transactionHashes) { + const receipt = await context.viem().getTransactionReceipt({ hash }); + const trace = await customDevRpcRequest("debug_traceTransaction", [ + hash, + { tracer: "callTracer" }, + ]); + // We traced the transaction, and the traced gas used matches the one recorded + // in the ethereum transaction receipt. + expect(hexToNumber(trace.gasUsed)).to.eq(Number(receipt.gasUsed)); + } + }, + }); + }, +}); diff --git a/test/suites/tracing-tests/test-trace-filter-reorg.ts b/test/suites/tracing-tests/test-trace-filter-reorg.ts index 8dfc49121c..b246934c52 100644 --- a/test/suites/tracing-tests/test-trace-filter-reorg.ts +++ b/test/suites/tracing-tests/test-trace-filter-reorg.ts @@ -3,7 +3,7 @@ import { describeSuite, customDevRpcRequest } from "@moonwall/cli"; import { createEthersTransaction, generateKeyringPair } from "@moonwall/util"; describeSuite({ - id: "T12", + id: "T13", title: "Trace filter reorg", foundationMethods: "dev", testCases: ({ context, it }) => { diff --git a/test/suites/tracing-tests/test-trace-filter.ts b/test/suites/tracing-tests/test-trace-filter.ts index 51a8937818..1d62106601 100644 --- a/test/suites/tracing-tests/test-trace-filter.ts +++ b/test/suites/tracing-tests/test-trace-filter.ts @@ -2,7 +2,7 @@ import { beforeAll, customDevRpcRequest, describeSuite, expect } from "@moonwall import { ALITH_ADDRESS, ALITH_CONTRACT_ADDRESSES, alith } from "@moonwall/util"; describeSuite({ - id: "T13", + id: "T14", title: "Trace filter - Contract creation ", foundationMethods: "dev", testCases: ({ context, it }) => { diff --git a/test/suites/tracing-tests/test-trace-gas.ts b/test/suites/tracing-tests/test-trace-gas.ts index f5bc4a9f48..61c0450d60 100644 --- a/test/suites/tracing-tests/test-trace-gas.ts +++ b/test/suites/tracing-tests/test-trace-gas.ts @@ -5,7 +5,7 @@ import { Abi, encodeFunctionData } from "viem"; import { numberToHex } from "@polkadot/util"; describeSuite({ - id: "T14", + id: "T15", title: "Trace filter - Gas Loop", foundationMethods: "dev", testCases: ({ context, it }) => {