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
use crate::mock::{ExtBuilder, PCall, Precompiles, PrecompilesValue, Runtime};
17
use crate::test_relay_runtime::TestEncoder;
18
use crate::AvailableStakeCalls;
19
use crate::StakeEncodeCall;
20
use crate::*;
21
use pallet_staking::RewardDestination;
22
use pallet_staking::ValidatorPrefs;
23
use precompile_utils::testing::*;
24
use sp_runtime::Perbill;
25

            
26
12
fn precompiles() -> Precompiles<Runtime> {
27
12
	PrecompilesValue::get()
28
12
}
29

            
30
#[test]
31
1
fn selectors() {
32
1
	assert!(PCall::encode_bond_selectors().contains(&0x72a9fbc6));
33
1
	assert!(PCall::encode_bond_extra_selectors().contains(&0x813667a0));
34
1
	assert!(PCall::encode_unbond_selectors().contains(&0x51b14e57));
35
1
	assert!(PCall::encode_withdraw_unbonded_selectors().contains(&0xd5ad108e));
36
1
	assert!(PCall::encode_validate_selectors().contains(&0xbb64ca0c));
37
1
	assert!(PCall::encode_nominate_selectors().contains(&0xdcf06883));
38
1
	assert!(PCall::encode_chill_selectors().contains(&0xb5eaac43));
39
1
	assert!(PCall::encode_set_payee_selectors().contains(&0x414be337));
40
1
	assert!(PCall::encode_set_controller_selectors().contains(&0x15490616));
41
1
	assert!(PCall::encode_rebond_selectors().contains(&0x0922ee17));
42
1
}
43

            
44
#[test]
45
1
fn modifiers() {
46
1
	ExtBuilder::default().build().execute_with(|| {
47
1
		let mut tester =
48
1
			PrecompilesModifierTester::new(PrecompilesValue::get(), Alice, Precompile1);
49
1

            
50
1
		tester.test_view_modifier(PCall::encode_bond_selectors());
51
1
		tester.test_view_modifier(PCall::encode_bond_extra_selectors());
52
1
		tester.test_view_modifier(PCall::encode_unbond_selectors());
53
1
		tester.test_view_modifier(PCall::encode_withdraw_unbonded_selectors());
54
1
		tester.test_view_modifier(PCall::encode_validate_selectors());
55
1
		tester.test_view_modifier(PCall::encode_nominate_selectors());
56
1
		tester.test_view_modifier(PCall::encode_chill_selectors());
57
1
		tester.test_view_modifier(PCall::encode_set_payee_selectors());
58
1
		tester.test_view_modifier(PCall::encode_set_controller_selectors());
59
1
		tester.test_view_modifier(PCall::encode_rebond_selectors());
60
1
	});
61
1
}
62

            
63
#[test]
64
1
fn selector_less_than_four_bytes() {
65
1
	ExtBuilder::default().build().execute_with(|| {
66
1
		precompiles()
67
1
			.prepare_test(Alice, Precompile1, vec![1u8, 2u8, 3u8])
68
1
			.execute_reverts(|output| output == b"Tried to read selector out of bounds");
69
1
	});
70
1
}
71

            
72
#[test]
73
1
fn no_selector_exists_but_length_is_right() {
74
1
	ExtBuilder::default().build().execute_with(|| {
75
1
		precompiles()
76
1
			.prepare_test(Alice, Precompile1, vec![1u8, 2u8, 3u8, 4u8])
77
1
			.execute_reverts(|output| output == b"Unknown selector");
78
1
	});
79
1
}
80

            
81
#[test]
82
1
fn test_encode_bond() {
83
1
	let controller = sp_runtime::AccountId32::from([0; 32]);
84
1
	ExtBuilder::default()
85
1
		.with_balances(vec![(Alice.into(), 1000)])
86
1
		.build()
87
1
		.execute_with(|| {
88
1
			precompiles()
89
1
				.prepare_test(
90
1
					Alice,
91
1
					Precompile1,
92
1
					PCall::encode_bond {
93
1
						amount: 100.into(),
94
1
						reward_destination: RewardDestinationWrapper(RewardDestination::Account(
95
1
							controller.clone(),
96
1
						)),
97
1
					},
98
1
				)
99
1
				.expect_cost(1000)
100
1
				.expect_no_logs()
101
1
				.execute_returns(UnboundedBytes::from(
102
1
					TestEncoder::encode_call(
103
1
						(),
104
1
						AvailableStakeCalls::Bond(
105
1
							100u32.into(),
106
1
							RewardDestination::Account(controller),
107
1
						),
108
1
					)
109
1
					.as_slice(),
110
1
				));
111
1
		});
112
1
}
113

            
114
#[test]
115
1
fn test_encode_bond_more() {
116
1
	ExtBuilder::default()
117
1
		.with_balances(vec![(Alice.into(), 1000)])
118
1
		.build()
119
1
		.execute_with(|| {
120
1
			precompiles()
121
1
				.prepare_test(
122
1
					Alice,
123
1
					Precompile1,
124
1
					PCall::encode_bond_extra { amount: 100.into() },
125
1
				)
126
1
				.expect_cost(1000)
127
1
				.expect_no_logs()
128
1
				.execute_returns(UnboundedBytes::from(
129
1
					TestEncoder::encode_call((), AvailableStakeCalls::BondExtra(100u32.into()))
130
1
						.as_slice(),
131
1
				));
132
1
		});
133
1
}
134

            
135
#[test]
136
1
fn test_encode_chill() {
137
1
	ExtBuilder::default()
138
1
		.with_balances(vec![(Alice.into(), 1000)])
139
1
		.build()
140
1
		.execute_with(|| {
141
1
			precompiles()
142
1
				.prepare_test(Alice, Precompile1, PCall::encode_chill {})
143
1
				.expect_cost(1000)
144
1
				.expect_no_logs()
145
1
				.execute_returns(UnboundedBytes::from(
146
1
					TestEncoder::encode_call((), AvailableStakeCalls::Chill).as_slice(),
147
1
				));
148
1
		});
149
1
}
150

            
151
#[test]
152
1
fn test_encode_nominate() {
153
1
	ExtBuilder::default()
154
1
		.with_balances(vec![(Alice.into(), 1000)])
155
1
		.build()
156
1
		.execute_with(|| {
157
1
			precompiles()
158
1
				.prepare_test(
159
1
					Alice,
160
1
					Precompile1,
161
1
					PCall::encode_nominate {
162
1
						nominees: vec![H256::from([1u8; 32]), H256::from([2u8; 32])].into(),
163
1
					},
164
1
				)
165
1
				.expect_cost(1000)
166
1
				.expect_no_logs()
167
1
				.execute_returns(UnboundedBytes::from(
168
1
					TestEncoder::encode_call(
169
1
						(),
170
1
						AvailableStakeCalls::Nominate(vec![[1u8; 32].into(), [2u8; 32].into()]),
171
1
					)
172
1
					.as_slice(),
173
1
				));
174
1
		});
175
1
}
176

            
177
#[test]
178
1
fn test_encode_rebond() {
179
1
	ExtBuilder::default()
180
1
		.with_balances(vec![(Alice.into(), 1000)])
181
1
		.build()
182
1
		.execute_with(|| {
183
1
			precompiles()
184
1
				.prepare_test(
185
1
					Alice,
186
1
					Precompile1,
187
1
					PCall::encode_rebond { amount: 100.into() },
188
1
				)
189
1
				.expect_cost(1000)
190
1
				.expect_no_logs()
191
1
				.execute_returns(UnboundedBytes::from(
192
1
					TestEncoder::encode_call((), AvailableStakeCalls::Rebond(100u128)).as_slice(),
193
1
				));
194
1
		});
195
1
}
196

            
197
#[test]
198
1
fn test_encode_set_controller() {
199
1
	ExtBuilder::default()
200
1
		.with_balances(vec![(Alice.into(), 1000)])
201
1
		.build()
202
1
		.execute_with(|| {
203
1
			precompiles()
204
1
				.prepare_test(Alice, Precompile1, PCall::encode_set_controller {})
205
1
				.expect_cost(1000)
206
1
				.expect_no_logs()
207
1
				.execute_returns(UnboundedBytes::from(
208
1
					TestEncoder::encode_call((), AvailableStakeCalls::SetController).as_slice(),
209
1
				))
210
1
		});
211
1
}
212

            
213
#[test]
214
1
fn test_encode_set_payee() {
215
1
	let controller = sp_runtime::AccountId32::from([0; 32]);
216
1
	ExtBuilder::default()
217
1
		.with_balances(vec![(Alice.into(), 1000)])
218
1
		.build()
219
1
		.execute_with(|| {
220
1
			precompiles()
221
1
				.prepare_test(
222
1
					Alice,
223
1
					Precompile1,
224
1
					PCall::encode_set_payee {
225
1
						reward_destination: RewardDestinationWrapper(RewardDestination::Account(
226
1
							controller.clone(),
227
1
						)),
228
1
					},
229
1
				)
230
1
				.expect_cost(1000)
231
1
				.expect_no_logs()
232
1
				.execute_returns(UnboundedBytes::from(
233
1
					TestEncoder::encode_call(
234
1
						(),
235
1
						AvailableStakeCalls::SetPayee(RewardDestination::Account(controller)),
236
1
					)
237
1
					.as_slice(),
238
1
				));
239
1
		});
240
1
}
241

            
242
#[test]
243
1
fn test_encode_unbond() {
244
1
	ExtBuilder::default()
245
1
		.with_balances(vec![(Alice.into(), 1000)])
246
1
		.build()
247
1
		.execute_with(|| {
248
1
			precompiles()
249
1
				.prepare_test(
250
1
					Alice,
251
1
					Precompile1,
252
1
					PCall::encode_unbond { amount: 100.into() },
253
1
				)
254
1
				.expect_cost(1000)
255
1
				.expect_no_logs()
256
1
				.execute_returns(UnboundedBytes::from(
257
1
					TestEncoder::encode_call((), AvailableStakeCalls::Unbond(100u32.into()))
258
1
						.as_slice(),
259
1
				));
260
1
		});
261
1
}
262

            
263
#[test]
264
1
fn test_encode_validate() {
265
1
	ExtBuilder::default()
266
1
		.with_balances(vec![(Alice.into(), 1000)])
267
1
		.build()
268
1
		.execute_with(|| {
269
1
			precompiles()
270
1
				.prepare_test(
271
1
					Alice,
272
1
					Precompile1,
273
1
					PCall::encode_validate {
274
1
						commission: 100.into(),
275
1
						blocked: true,
276
1
					},
277
1
				)
278
1
				.expect_cost(1000)
279
1
				.expect_no_logs()
280
1
				.execute_returns(UnboundedBytes::from(
281
1
					TestEncoder::encode_call(
282
1
						(),
283
1
						AvailableStakeCalls::Validate(ValidatorPrefs {
284
1
							commission: Perbill::from_parts(100u32.into()),
285
1
							blocked: true,
286
1
						}),
287
1
					)
288
1
					.as_slice(),
289
1
				));
290
1
		});
291
1
}
292

            
293
#[test]
294
1
fn test_encode_withdraw_unbonded() {
295
1
	ExtBuilder::default()
296
1
		.with_balances(vec![(Alice.into(), 1000)])
297
1
		.build()
298
1
		.execute_with(|| {
299
1
			precompiles()
300
1
				.prepare_test(
301
1
					Alice,
302
1
					Precompile1,
303
1
					PCall::encode_withdraw_unbonded { slashes: 100 },
304
1
				)
305
1
				.expect_cost(1000)
306
1
				.expect_no_logs()
307
1
				.execute_returns(UnboundedBytes::from(
308
1
					TestEncoder::encode_call(
309
1
						(),
310
1
						AvailableStakeCalls::WithdrawUnbonded(100u32.into()),
311
1
					)
312
1
					.as_slice(),
313
1
				));
314
1
		});
315
1
}
316

            
317
#[test]
318
1
fn test_solidity_interface_has_all_function_selectors_documented_and_implemented() {
319
1
	check_precompile_implements_solidity_interfaces(&["RelayEncoder.sol"], PCall::supports_selector)
320
1
}
321

            
322
#[test]
323
1
fn test_deprecated_solidity_selectors_are_supported() {
324
10
	for deprecated_function in [
325
		"encode_bond(uint256,bytes)",
326
1
		"encode_bond_extra(uint256)",
327
1
		"encode_unbond(uint256)",
328
1
		"encode_withdraw_unbonded(uint32)",
329
1
		"encode_validate(uint256,bool)",
330
1
		"encode_nominate(bytes32[])",
331
1
		"encode_chill()",
332
1
		"encode_set_payee(bytes)",
333
1
		"encode_set_controller()",
334
1
		"encode_rebond(uint256)",
335
	] {
336
10
		let selector = compute_selector(deprecated_function);
337
10
		if !PCall::supports_selector(selector) {
338
			panic!(
339
				"failed decoding selector 0x{:x} => '{}' as Action",
340
				selector, deprecated_function,
341
			)
342
10
		}
343
	}
344
1
}