1
// Copyright 2019-2022 PureStake Inc.
2
// This file is part of Moonbeam.
3

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

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

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

            
17
#[macro_export]
18
macro_rules! impl_moonbeam_xcm_call_tracing {
19
	{} => {
20

            
21
		type CallResult =
22
			Result<
23
				PostDispatchInfoOf<RuntimeCall>,
24
				DispatchErrorWithPostInfo<PostDispatchInfoOf<RuntimeCall>>
25
			>;
26

            
27
		pub struct MoonbeamCall;
28
		impl CallDispatcher<RuntimeCall> for MoonbeamCall {
29
104
			fn dispatch(
30
104
				call: RuntimeCall,
31
104
				origin: RuntimeOrigin,
32
104
			) -> CallResult {
33
104
				if let Ok(raw_origin) = TryInto::<RawOrigin<AccountId>>::try_into(origin.clone().caller) {
34
104
					match (call.clone(), raw_origin) {
35
						(
36
64
							RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::transact { xcm_transaction }) |
37
							RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::transact_through_proxy {
38
								xcm_transaction, ..
39
							 }),
40
64
							RawOrigin::Signed(account_id)
41
						) => {
42
							use crate::EthereumXcm;
43
							use moonbeam_evm_tracer::tracer::EvmTracer;
44
							use xcm_primitives::{
45
								XcmToEthereum,
46
								EthereumXcmTracingStatus,
47
								ETHEREUM_XCM_TRACING_STORAGE_KEY
48
							};
49
							use frame_support::storage::unhashed;
50
							use frame_support::traits::Get;
51

            
52
64
							let dispatch_call = || {
53
								RuntimeCall::dispatch(
54
									call,
55
									pallet_ethereum_xcm::Origin::XcmEthereumTransaction(
56
										account_id.into()
57
									).into()
58
								)
59
							};
60

            
61
64
							return match unhashed::get(
62
64
								ETHEREUM_XCM_TRACING_STORAGE_KEY
63
64
							) {
64
								// This runtime instance is used for tracing.
65
								Some(tracing_status) => match tracing_status {
66
									// Tracing a block, all calls are done using environmental.
67
									EthereumXcmTracingStatus::Block => {
68
										// Each known extrinsic is a new call stack.
69
										EvmTracer::emit_new();
70
										let mut res: Option<CallResult> = None;
71
										EvmTracer::new().trace(|| {
72
											res = Some(dispatch_call());
73
										});
74
										res.expect("Invalid dispatch result")
75
									},
76
									// Tracing a transaction, the one matching the trace request
77
									// is done using environmental, the rest dispatched normally.
78
									EthereumXcmTracingStatus::Transaction(traced_transaction_hash) => {
79
										let transaction_hash = xcm_transaction.into_transaction_v2(
80
											EthereumXcm::nonce(),
81
											<Runtime as pallet_evm::Config>::ChainId::get(),
82
											false
83
										)
84
										.expect("Invalid transaction conversion")
85
										.hash();
86
										if transaction_hash == traced_transaction_hash {
87
											let mut res: Option<CallResult> = None;
88
											EvmTracer::new().trace(|| {
89
												res = Some(dispatch_call());
90
											});
91
											// Tracing runtime work is done, just signal instance exit.
92
											unhashed::put::<EthereumXcmTracingStatus>(
93
												xcm_primitives::ETHEREUM_XCM_TRACING_STORAGE_KEY,
94
												&EthereumXcmTracingStatus::TransactionExited,
95
											);
96
											return res.expect("Invalid dispatch result");
97
										}
98
										dispatch_call()
99
									},
100
									// Tracing a transaction that has already been found and
101
									// executed. There's no need to dispatch the rest of the
102
									// calls.
103
									EthereumXcmTracingStatus::TransactionExited => Ok(crate::PostDispatchInfo {
104
										actual_weight: None,
105
										pays_fee: frame_support::pallet_prelude::Pays::No,
106
									}),
107
								},
108
								// This runtime instance is importing a block.
109
64
								None => dispatch_call()
110
							};
111
						},
112
40
						_ => {}
113
					}
114
				}
115
40
				RuntimeCall::dispatch(call, origin)
116
			}
117
		}
118
	}
119
}