1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// Copyright 2019-2022 PureStake Inc.
// This file is part of Moonbeam.

// Moonbeam is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Moonbeam is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.

#[macro_export]
macro_rules! impl_moonbeam_xcm_call_tracing {
	{} => {

		type CallResult =
			Result<
				PostDispatchInfoOf<RuntimeCall>,
				DispatchErrorWithPostInfo<PostDispatchInfoOf<RuntimeCall>>
			>;

		pub struct MoonbeamCall;
		impl CallDispatcher<RuntimeCall> for MoonbeamCall {
			fn dispatch(
				call: RuntimeCall,
				origin: RuntimeOrigin,
			) -> CallResult {
				if let Ok(raw_origin) = TryInto::<RawOrigin<AccountId>>::try_into(origin.clone().caller) {
					match (call.clone(), raw_origin) {
						(
							RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::transact { xcm_transaction }) |
							RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::transact_through_proxy {
								xcm_transaction, ..
							 }),
							RawOrigin::Signed(account_id)
						) => {
							use crate::EthereumXcm;
							use moonbeam_evm_tracer::tracer::EvmTracer;
							use xcm_primitives::{
								XcmToEthereum,
								EthereumXcmTracingStatus,
								ETHEREUM_XCM_TRACING_STORAGE_KEY
							};
							use frame_support::storage::unhashed;
							use frame_support::traits::Get;

							let dispatch_call = || {
								RuntimeCall::dispatch(
									call,
									pallet_ethereum_xcm::Origin::XcmEthereumTransaction(
										account_id.into()
									).into()
								)
							};

							return match unhashed::get(
								ETHEREUM_XCM_TRACING_STORAGE_KEY
							) {
								// This runtime instance is used for tracing.
								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.
										EvmTracer::emit_new();
										let mut res: Option<CallResult> = None;
										EvmTracer::new().trace(|| {
											res = Some(dispatch_call());
										});
										res.expect("Invalid dispatch result")
									},
									// Tracing a transaction, the one matching the trace request
									// is done using environmental, the rest dispatched normally.
									EthereumXcmTracingStatus::Transaction(traced_transaction_hash) => {
										let transaction_hash = xcm_transaction.into_transaction_v2(
											EthereumXcm::nonce(),
											<Runtime as pallet_evm::Config>::ChainId::get(),
											false
										)
										.expect("Invalid transaction conversion")
										.hash();
										if transaction_hash == traced_transaction_hash {
											let mut res: Option<CallResult> = None;
											EvmTracer::new().trace(|| {
												res = Some(dispatch_call());
											});
											// Tracing runtime work is done, just signal instance exit.
											unhashed::put::<EthereumXcmTracingStatus>(
												xcm_primitives::ETHEREUM_XCM_TRACING_STORAGE_KEY,
												&EthereumXcmTracingStatus::TransactionExited,
											);
											return res.expect("Invalid dispatch result");
										}
										dispatch_call()
									},
									// 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()
							};
						},
						_ => {}
					}
				}
				RuntimeCall::dispatch(call, origin)
			}
		}
	}
}