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(AvailableStakeCalls::Bond(
103
1
						100u32.into(),
104
1
						RewardDestination::Account(controller),
105
1
					))
106
1
					.as_slice(),
107
1
				));
108
1
		});
109
1
}
110

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

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

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

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

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

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

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

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

            
285
#[test]
286
1
fn test_encode_withdraw_unbonded() {
287
1
	ExtBuilder::default()
288
1
		.with_balances(vec![(Alice.into(), 1000)])
289
1
		.build()
290
1
		.execute_with(|| {
291
1
			precompiles()
292
1
				.prepare_test(
293
1
					Alice,
294
1
					Precompile1,
295
1
					PCall::encode_withdraw_unbonded { slashes: 100 },
296
1
				)
297
1
				.expect_cost(1000)
298
1
				.expect_no_logs()
299
1
				.execute_returns(UnboundedBytes::from(
300
1
					TestEncoder::encode_call(AvailableStakeCalls::WithdrawUnbonded(100u32.into()))
301
1
						.as_slice(),
302
1
				));
303
1
		});
304
1
}
305

            
306
#[test]
307
1
fn test_solidity_interface_has_all_function_selectors_documented_and_implemented() {
308
1
	check_precompile_implements_solidity_interfaces(&["RelayEncoder.sol"], PCall::supports_selector)
309
1
}
310

            
311
#[test]
312
1
fn test_deprecated_solidity_selectors_are_supported() {
313
10
	for deprecated_function in [
314
		"encode_bond(uint256,bytes)",
315
1
		"encode_bond_extra(uint256)",
316
1
		"encode_unbond(uint256)",
317
1
		"encode_withdraw_unbonded(uint32)",
318
1
		"encode_validate(uint256,bool)",
319
1
		"encode_nominate(bytes32[])",
320
1
		"encode_chill()",
321
1
		"encode_set_payee(bytes)",
322
1
		"encode_set_controller()",
323
1
		"encode_rebond(uint256)",
324
	] {
325
10
		let selector = compute_selector(deprecated_function);
326
10
		if !PCall::supports_selector(selector) {
327
			panic!(
328
				"failed decoding selector 0x{:x} => '{}' as Action",
329
				selector, deprecated_function,
330
			)
331
10
		}
332
	}
333
1
}