1
// Copyright 2025 Moonbeam Foundation.
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::*, GetArrayLimit};
17
use parity_scale_codec::Encode;
18

            
19
use precompile_utils::{
20
	solidity::codec::{BoundedBytes, BoundedVec, UnboundedBytes},
21
	testing::*,
22
};
23
use sp_core::H256;
24

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

            
29
#[test]
30
1
fn test_solidity_interface_has_all_function_selectors_documented_and_implemented() {
31
1
	check_precompile_implements_solidity_interfaces(
32
1
		&["RelayDataVerifier.sol"],
33
1
		PCall::supports_selector,
34
1
	)
35
1
}
36

            
37
#[test]
38
1
fn selectors() {
39
1
	assert!(PCall::verify_entry_selectors().contains(&0x27001faa));
40
1
	assert!(PCall::verify_entries_selectors().contains(&0x2da33a45));
41
1
	assert!(PCall::latest_relay_block_selectors().contains(&0xaed36869));
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::latest_relay_block_selectors());
51
1
	});
52
1
}
53

            
54
#[test]
55
1
fn selector_less_than_four_bytes() {
56
1
	ExtBuilder::default().build().execute_with(|| {
57
1
		// This selector is only three bytes long when four are required.
58
1
		precompiles()
59
1
			.prepare_test(Alice, Precompile1, vec![1u8, 2u8, 3u8])
60
1
			.execute_reverts(|output| output == b"Tried to read selector out of bounds");
61
1
	});
62
1
}
63

            
64
#[test]
65
1
fn no_selector_exists_but_length_is_right() {
66
1
	ExtBuilder::default().build().execute_with(|| {
67
1
		precompiles()
68
1
			.prepare_test(Alice, Precompile1, vec![1u8, 2u8, 3u8, 4u8])
69
1
			.execute_reverts(|output| output == b"Unknown selector");
70
1
	});
71
1
}
72

            
73
// Test that the latest relay block number is returned correctly
74
#[test]
75
1
fn test_last_relay_block_retrieval() {
76
1
	ExtBuilder::default()
77
1
		.with_balances(vec![(Alice.into(), 1000)])
78
1
		.build()
79
1
		.execute_with(|| {
80
1
			fill_relay_storage_roots::<Runtime>();
81
1
			set_current_relay_chain_state(250, H256::default());
82
1

            
83
1
			precompiles()
84
1
				.prepare_test(Alice, Precompile1, PCall::latest_relay_block {})
85
1
				.expect_cost(0)
86
1
				.expect_no_logs()
87
1
				.execute_returns(250u32);
88
1
		});
89
1
}
90

            
91
// Test that the latest_relay_block fails when no relay block is stored on chain yet
92
#[test]
93
1
fn test_last_relay_block_not_stored() {
94
1
	ExtBuilder::default()
95
1
		.with_balances(vec![(Alice.into(), 1000)])
96
1
		.build()
97
1
		.execute_with(|| {
98
1
			precompiles()
99
1
				.prepare_test(Alice, Precompile1, PCall::latest_relay_block {})
100
1
				.expect_cost(0)
101
1
				.expect_no_logs()
102
1
				.execute_reverts(|output| output == b"No relay block found");
103
1
		});
104
1
}
105

            
106
// Test that verify_entry and verify_entries functions fail when the relay block number is not found
107
#[test]
108
1
fn test_block_not_found() {
109
1
	ExtBuilder::default()
110
1
		.with_balances(vec![(Alice.into(), 1000)])
111
1
		.build()
112
1
		.execute_with(|| {
113
1
			fill_relay_storage_roots::<Runtime>();
114
1

            
115
1
			precompiles()
116
1
				.prepare_test(
117
1
					Alice,
118
1
					Precompile1,
119
1
					PCall::verify_entry {
120
1
						relay_block_number: 4,
121
1
						proof: mocked_read_proof(),
122
1
						key: BoundedBytes::from(vec![0u8; 32]),
123
1
					},
124
1
				)
125
1
				.expect_cost(0)
126
1
				.expect_no_logs()
127
1
				.execute_reverts(|output| output == b"Block number not present");
128
1

            
129
1
			precompiles()
130
1
				.prepare_test(
131
1
					Alice,
132
1
					Precompile1,
133
1
					PCall::verify_entries {
134
1
						relay_block_number: 4,
135
1
						proof: mocked_read_proof(),
136
1
						keys: BoundedVec::from(vec![BoundedBytes::from(vec![0u8; 32])]),
137
1
					},
138
1
				)
139
1
				.expect_cost(0)
140
1
				.expect_no_logs()
141
1
				.execute_reverts(|output| output == b"Block number not present");
142
1
		});
143
1
}
144

            
145
// Test that verify_entry and verify_entries functions fail when the root does not match
146
#[test]
147
1
fn test_root_mismatch() {
148
1
	ExtBuilder::default()
149
1
		.with_balances(vec![(Alice.into(), 1000)])
150
1
		.build()
151
1
		.execute_with(|| {
152
1
			fill_relay_storage_roots::<Runtime>();
153
1
			let relay_block_number = 250;
154
1
			let state_root = H256::from_slice(
155
1
				&hex::decode("767caa877bcea0d34dd515a202b75efa41bffbc9f814ab59e2c1c96716d4c65e")
156
1
					.unwrap(),
157
1
			);
158
1
			set_current_relay_chain_state(relay_block_number, state_root);
159
1

            
160
1
			precompiles()
161
1
				.prepare_test(
162
1
					Alice,
163
1
					Precompile1,
164
1
					PCall::verify_entry {
165
1
						relay_block_number,
166
1
						proof: mocked_read_proof(),
167
1
						key: BoundedBytes::from(vec![0u8; 32]),
168
1
					},
169
1
				)
170
1
				.expect_cost(0)
171
1
				.expect_no_logs()
172
1
				.execute_reverts(|output| output == b"Root Mismatch");
173
1

            
174
1
			precompiles()
175
1
				.prepare_test(
176
1
					Alice,
177
1
					Precompile1,
178
1
					PCall::verify_entries {
179
1
						relay_block_number,
180
1
						proof: mocked_read_proof(),
181
1
						keys: BoundedVec::from(vec![BoundedBytes::from(vec![0u8; 32])]),
182
1
					},
183
1
				)
184
1
				.expect_cost(0)
185
1
				.expect_no_logs()
186
1
				.execute_reverts(|output| output == b"Root Mismatch");
187
1
		});
188
1
}
189

            
190
// Test that verify_entry and verify_entries functions fail when the entry is not found
191
#[test]
192
1
fn test_entry_not_found() {
193
1
	ExtBuilder::default()
194
1
		.with_balances(vec![(Alice.into(), 1000)])
195
1
		.build()
196
1
		.execute_with(|| {
197
1
			fill_relay_storage_roots::<Runtime>();
198
1
			let relay_block_number = 250;
199
1
			set_current_relay_chain_state(relay_block_number, H256::from_slice(STORAGE_ROOT));
200
1

            
201
1
			precompiles()
202
1
				.prepare_test(
203
1
					Alice,
204
1
					Precompile1,
205
1
					PCall::verify_entry {
206
1
						relay_block_number,
207
1
						proof: mocked_read_proof(),
208
1
						key: BoundedBytes::from(
209
1
							hex::decode(
210
1
								"89d139e01a5eb2256f222e5fc5dbe6b33c9c1284130706f5aea0c8b3d4c54d2c",
211
1
							)
212
1
							.unwrap(),
213
1
						),
214
1
					},
215
1
				)
216
1
				.expect_cost(0)
217
1
				.expect_no_logs()
218
1
				.execute_reverts(|output| output == b"Value is not present");
219
1

            
220
1
			precompiles()
221
1
				.prepare_test(
222
1
					Alice,
223
1
					Precompile1,
224
1
					PCall::verify_entries {
225
1
						relay_block_number,
226
1
						proof: mocked_read_proof(),
227
1
						keys: BoundedVec::from(vec![
228
1
							BoundedBytes::from(TIMESTAMP_KEY),
229
1
							BoundedBytes::from(TOTAL_ISSUANCE_KEY),
230
1
							BoundedBytes::from(TREASURY_APPROVALS_KEY),
231
1
							// This key is not present in the proof
232
1
							BoundedBytes::from(hex::decode(
233
1
								"89d139e01a5eb2256f222e5fc5dbe6b33c9c1284130706f5aea0c8b3d4c54d89ec",
234
1
							).unwrap()),
235
1
						]),
236
1
					},
237
1
				)
238
1
				.expect_cost(0)
239
1
				.expect_no_logs()
240
1
				.execute_reverts(|output| output == b"Value is not present");
241
1
		});
242
1
}
243

            
244
// Test that verify_entry returns the correct value
245
#[test]
246
1
fn test_verify_entry() {
247
1
	ExtBuilder::default()
248
1
		.with_balances(vec![(Alice.into(), 1000)])
249
1
		.build()
250
1
		.execute_with(|| {
251
1
			fill_relay_storage_roots::<Runtime>();
252
1
			let relay_block_number = 250;
253
1
			set_current_relay_chain_state(relay_block_number, H256::from_slice(STORAGE_ROOT));
254
1

            
255
1
			precompiles()
256
1
				.prepare_test(
257
1
					Alice,
258
1
					Precompile1,
259
1
					PCall::verify_entry {
260
1
						relay_block_number,
261
1
						proof: mocked_read_proof(),
262
1
						key: BoundedBytes::from(TIMESTAMP_KEY),
263
1
					},
264
1
				)
265
1
				.expect_cost(0)
266
1
				.expect_no_logs()
267
1
				.execute_returns(UnboundedBytes::from(1_708_190_328_000u64.encode()));
268
1
		});
269
1
}
270

            
271
// Test that verify_entries fails with an empty keys array
272
#[test]
273
1
fn test_verify_entries_empty_keys() {
274
1
	ExtBuilder::default()
275
1
		.with_balances(vec![(Alice.into(), 1000)])
276
1
		.build()
277
1
		.execute_with(|| {
278
1
			fill_relay_storage_roots::<Runtime>();
279
1
			let relay_block_number = 250;
280
1
			set_current_relay_chain_state(relay_block_number, H256::from_slice(STORAGE_ROOT));
281
1

            
282
1
			precompiles()
283
1
				.prepare_test(
284
1
					Alice,
285
1
					Precompile1,
286
1
					PCall::verify_entries {
287
1
						relay_block_number,
288
1
						proof: mocked_read_proof(),
289
1
						keys: BoundedVec::from(vec![]),
290
1
					},
291
1
				)
292
1
				.expect_cost(0)
293
1
				.expect_no_logs()
294
1
				.execute_reverts(|output| output == b"Keys must not be empty");
295
1
		});
296
1
}
297

            
298
// Test that verify_entries returns the correct values
299
#[test]
300
1
fn test_verify_entries() {
301
1
	ExtBuilder::default()
302
1
		.with_balances(vec![(Alice.into(), 1000)])
303
1
		.build()
304
1
		.execute_with(|| {
305
1
			fill_relay_storage_roots::<Runtime>();
306
1
			let relay_block_number = 250;
307
1
			set_current_relay_chain_state(relay_block_number, H256::from_slice(STORAGE_ROOT));
308
1

            
309
1
			precompiles()
310
1
				.prepare_test(
311
1
					Alice,
312
1
					Precompile1,
313
1
					PCall::verify_entries {
314
1
						relay_block_number,
315
1
						proof: mocked_read_proof(),
316
1
						keys: BoundedVec::from(vec![
317
1
							BoundedBytes::from(TIMESTAMP_KEY),
318
1
							BoundedBytes::from(TOTAL_ISSUANCE_KEY),
319
1
							BoundedBytes::from(TREASURY_APPROVALS_KEY),
320
1
						]),
321
1
					},
322
1
				)
323
1
				.expect_cost(0)
324
1
				.expect_no_logs()
325
1
				.execute_returns(BoundedVec::<UnboundedBytes, GetArrayLimit>::from(vec![
326
1
					UnboundedBytes::from(1_708_190_328_000u64.encode()),
327
1
					UnboundedBytes::from(14_123_366_426_803_276_130u128.encode()),
328
1
					UnboundedBytes::from(
329
1
						vec![
330
1
							607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620,
331
1
							621, 622, 623,
332
1
						]
333
1
						.encode(),
334
1
					),
335
1
				]));
336
1
		});
337
1
}