1
// Copyright 2019-2025 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
use cumulus_primitives_core::ParaId;
18
use cumulus_primitives_core::XcmpMessageFormat;
19
use jsonrpsee::{
20
	core::RpcResult,
21
	proc_macros::rpc,
22
	types::{
23
		error::{INTERNAL_ERROR_CODE, INTERNAL_ERROR_MSG},
24
		ErrorObjectOwned,
25
	},
26
};
27
use parity_scale_codec::Encode;
28
use xcm::opaque::lts::Weight;
29
use xcm::v5::prelude::*;
30
use xcm_primitives::DEFAULT_PROOF_SIZE;
31

            
32
/// This RPC interface is used to provide methods in dev mode only
33
2524
#[rpc(server)]
34
#[jsonrpsee::core::async_trait]
35
pub trait DevApi {
36
	/// Inject a downward xcm message - A message that comes from the relay chain.
37
	/// You may provide an arbitrary message, or if you provide an empty byte array,
38
	/// Then a default message (DOT transfer down to ALITH) will be injected
39
	#[method(name = "xcm_injectDownwardMessage")]
40
	async fn inject_downward_message(&self, message: Vec<u8>) -> RpcResult<()>;
41

            
42
	/// Inject an HRMP message - A message that comes from a dedicated channel to a sibling
43
	/// parachain.
44
	///
45
	/// Cumulus Parachain System seems to have a constraint that at most one hrmp message will be
46
	/// sent on a channel per block. At least that's what this comment implies:
47
	/// https://github.com/paritytech/cumulus/blob/c308c01b/pallets/parachain-system/src/lib.rs#L204
48
	/// Neither this RPC, nor the mock inherent data provider make any attempt to enforce this
49
	/// constraint. In fact, violating it may be useful for testing.
50
	/// The method accepts a sending paraId and a bytearray representing an arbitrary message as
51
	/// parameters. If you provide an emtpy byte array, then a default message representing a
52
	/// transfer of the sending paraId's native token will be injected.
53
	#[method(name = "xcm_injectHrmpMessage")]
54
	async fn inject_hrmp_message(&self, sender: ParaId, message: Vec<u8>) -> RpcResult<()>;
55

            
56
	/// Skip N relay blocks, for testing purposes
57
	#[method(name = "test_skipRelayBlocks")]
58
	async fn skip_relay_blocks(&self, n: u32) -> RpcResult<()>;
59
}
60

            
61
pub struct DevRpc {
62
	pub downward_message_channel: flume::Sender<Vec<u8>>,
63
	pub hrmp_message_channel: flume::Sender<(ParaId, Vec<u8>)>,
64
	pub additional_relay_offset: std::sync::Arc<std::sync::atomic::AtomicU32>,
65
}
66

            
67
#[jsonrpsee::core::async_trait]
68
impl DevApiServer for DevRpc {
69
32
	async fn inject_downward_message(&self, msg: Vec<u8>) -> RpcResult<()> {
70
32
		let downward_message_channel = self.downward_message_channel.clone();
71
		// If no message is supplied, inject a default one.
72
32
		let msg = if msg.is_empty() {
73
4
			xcm::VersionedXcm::<()>::V5(Xcm(vec![
74
4
				ReserveAssetDeposited((Parent, 10000000000000u128).into()),
75
4
				ClearOrigin,
76
4
				BuyExecution {
77
4
					fees: (Parent, 10000000000000u128).into(),
78
4
					weight_limit: Limited(Weight::from_parts(
79
4
						10_000_000_000u64,
80
4
						DEFAULT_PROOF_SIZE,
81
4
					)),
82
4
				},
83
4
				DepositAsset {
84
4
					assets: AllCounted(1).into(),
85
4
					beneficiary: Location::new(
86
4
						0,
87
4
						[AccountKey20 {
88
4
							network: None,
89
4
							key: hex_literal::hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"),
90
4
						}],
91
4
					),
92
4
				},
93
4
			]))
94
4
			.encode()
95
		} else {
96
28
			msg
97
		};
98

            
99
		// Push the message to the shared channel where it will be queued up
100
		// to be injected into an upcoming block.
101
32
		downward_message_channel
102
32
			.send_async(msg)
103
			.await
104
32
			.map_err(|err| internal_err(err.to_string()))?;
105

            
106
32
		Ok(())
107
64
	}
108

            
109
294
	async fn inject_hrmp_message(&self, sender: ParaId, msg: Vec<u8>) -> RpcResult<()> {
110
294
		let hrmp_message_channel = self.hrmp_message_channel.clone();
111

            
112
		// If no message is supplied, inject a default one.
113
294
		let msg = if msg.is_empty() {
114
4
			let mut mes = XcmpMessageFormat::ConcatenatedVersionedXcm.encode();
115
4
			mes.append(
116
4
				&mut (xcm::VersionedXcm::<()>::V5(Xcm(vec![
117
4
					ReserveAssetDeposited(
118
4
						((Parent, Parachain(sender.into())), 10000000000000u128).into(),
119
4
					),
120
4
					ClearOrigin,
121
4
					BuyExecution {
122
4
						fees: ((Parent, Parachain(sender.into())), 10000000000000u128).into(),
123
4
						weight_limit: Limited(Weight::from_parts(
124
4
							10_000_000_000u64,
125
4
							DEFAULT_PROOF_SIZE,
126
4
						)),
127
4
					},
128
4
					DepositAsset {
129
4
						assets: AllCounted(1).into(),
130
4
						beneficiary: Location::new(
131
4
							0,
132
4
							[AccountKey20 {
133
4
								network: None,
134
4
								key: hex_literal::hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"),
135
4
							}],
136
4
						),
137
4
					},
138
4
				]))
139
4
				.encode()),
140
4
			);
141
4
			mes
142
		} else {
143
290
			msg
144
		};
145

            
146
		// Push the message to the shared channel where it will be queued up
147
		// to be injected into an upcoming block.
148
294
		hrmp_message_channel
149
294
			.send_async((sender, msg))
150
			.await
151
294
			.map_err(|err| internal_err(err.to_string()))?;
152

            
153
294
		Ok(())
154
588
	}
155

            
156
2
	async fn skip_relay_blocks(&self, n: u32) -> RpcResult<()> {
157
2
		self.additional_relay_offset
158
2
			.fetch_add(n, std::sync::atomic::Ordering::SeqCst);
159
2
		Ok(())
160
4
	}
161
}
162

            
163
// This bit cribbed from frontier.
164
pub fn internal_err<T: ToString>(message: T) -> ErrorObjectOwned {
165
	ErrorObjectOwned::owned(
166
		INTERNAL_ERROR_CODE,
167
		INTERNAL_ERROR_MSG,
168
		Some(message.to_string()),
169
	)
170
}