referrerpolicy=no-referrer-when-downgrade

bp_test_utils/
lib.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Parity Bridges Common.
3
4// Parity Bridges Common 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// Parity Bridges Common 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 Parity Bridges Common.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Utilities for testing runtime code.
18
19#![warn(missing_docs)]
20#![cfg_attr(not(feature = "std"), no_std)]
21
22use bp_header_chain::justification::{required_justification_precommits, GrandpaJustification};
23use bp_parachains::parachain_head_storage_key_at_source;
24use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId};
25use bp_runtime::record_all_trie_keys;
26use codec::Encode;
27use sp_consensus_grandpa::{AuthorityId, AuthoritySignature, AuthorityWeight, SetId};
28use sp_runtime::traits::{Header as HeaderT, One, Zero};
29use sp_std::prelude::*;
30use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut};
31
32// Re-export all our test account utilities
33pub use keyring::*;
34
35mod keyring;
36
37/// GRANDPA round number used across tests.
38pub const TEST_GRANDPA_ROUND: u64 = 1;
39/// GRANDPA validators set id used across tests.
40pub const TEST_GRANDPA_SET_ID: SetId = 1;
41/// Name of the `Paras` pallet used across tests.
42pub const PARAS_PALLET_NAME: &str = "Paras";
43
44/// Configuration parameters when generating test GRANDPA justifications.
45#[derive(Clone)]
46pub struct JustificationGeneratorParams<H> {
47	/// The header which we want to finalize.
48	pub header: H,
49	/// The GRANDPA round number for the current authority set.
50	pub round: u64,
51	/// The current authority set ID.
52	pub set_id: SetId,
53	/// The current GRANDPA authority set.
54	///
55	/// The size of the set will determine the number of pre-commits in our justification.
56	pub authorities: Vec<(Account, AuthorityWeight)>,
57	/// The total number of precommit ancestors in the `votes_ancestries` field our justification.
58	///
59	/// These may be distributed among many forks.
60	pub ancestors: u32,
61	/// The number of forks.
62	///
63	/// Useful for creating a "worst-case" scenario in which each authority is on its own fork.
64	pub forks: u32,
65}
66
67impl<H: HeaderT> Default for JustificationGeneratorParams<H> {
68	fn default() -> Self {
69		let required_signatures = required_justification_precommits(test_keyring().len() as _);
70		Self {
71			header: test_header(One::one()),
72			round: TEST_GRANDPA_ROUND,
73			set_id: TEST_GRANDPA_SET_ID,
74			authorities: test_keyring().into_iter().take(required_signatures as _).collect(),
75			ancestors: 2,
76			forks: 1,
77		}
78	}
79}
80
81/// Make a valid GRANDPA justification with sensible defaults
82pub fn make_default_justification<H: HeaderT>(header: &H) -> GrandpaJustification<H> {
83	let params = JustificationGeneratorParams::<H> { header: header.clone(), ..Default::default() };
84
85	make_justification_for_header(params)
86}
87
88/// Generate justifications in a way where we are able to tune the number of pre-commits
89/// and vote ancestries which are included in the justification.
90///
91/// This is useful for benchmarks where we want to generate valid justifications with
92/// a specific number of pre-commits (tuned with the number of "authorities") and/or a specific
93/// number of vote ancestries (tuned with the "votes" parameter).
94///
95/// Note: This needs at least three authorities or else the verifier will complain about
96/// being given an invalid commit.
97pub fn make_justification_for_header<H: HeaderT>(
98	params: JustificationGeneratorParams<H>,
99) -> GrandpaJustification<H> {
100	let JustificationGeneratorParams { header, round, set_id, authorities, mut ancestors, forks } =
101		params;
102	let (target_hash, target_number) = (header.hash(), *header.number());
103	let mut votes_ancestries = vec![];
104	let mut precommits = vec![];
105
106	assert!(forks != 0, "Need at least one fork to have a chain..");
107	assert!(
108		forks as usize <= authorities.len(),
109		"If we have more forks than authorities we can't create valid pre-commits for all the forks."
110	);
111
112	// Roughly, how many vote ancestries do we want per fork
113	let target_depth = ancestors.div_ceil(forks);
114
115	let mut unsigned_precommits = vec![];
116	for i in 0..forks {
117		let depth = if ancestors >= target_depth {
118			ancestors -= target_depth;
119			target_depth
120		} else {
121			ancestors
122		};
123
124		// Note: Adding 1 to account for the target header
125		let chain = generate_chain(i, depth + 1, &header);
126
127		// We don't include our finality target header in the vote ancestries
128		for child in &chain[1..] {
129			votes_ancestries.push(child.clone());
130		}
131
132		// The header we need to use when pre-committing is the one at the highest height
133		// on our chain.
134		let precommit_candidate = chain.last().map(|h| (h.hash(), *h.number())).unwrap();
135		unsigned_precommits.push(precommit_candidate);
136	}
137
138	for (i, (id, _weight)) in authorities.iter().enumerate() {
139		// Assign authorities to sign pre-commits in a round-robin fashion
140		let target = unsigned_precommits[i % forks as usize];
141		let precommit = signed_precommit::<H>(id, target, round, set_id);
142
143		precommits.push(precommit);
144	}
145
146	GrandpaJustification {
147		round,
148		commit: finality_grandpa::Commit { target_hash, target_number, precommits },
149		votes_ancestries,
150	}
151}
152
153fn generate_chain<H: HeaderT>(fork_id: u32, depth: u32, ancestor: &H) -> Vec<H> {
154	let mut headers = vec![ancestor.clone()];
155
156	for i in 1..depth {
157		let parent = &headers[(i - 1) as usize];
158		let (hash, num) = (parent.hash(), *parent.number());
159
160		let mut header = test_header::<H>(num + One::one());
161		header.set_parent_hash(hash);
162
163		// Modifying the digest so headers at the same height but in different forks have different
164		// hashes
165		header.digest_mut().logs.push(sp_runtime::DigestItem::Other(fork_id.encode()));
166
167		headers.push(header);
168	}
169
170	headers
171}
172
173/// Make valid proof for parachain `heads`
174pub fn prepare_parachain_heads_proof<H: HeaderT>(
175	heads: Vec<(u32, ParaHead)>,
176) -> (H::Hash, ParaHeadsProof, Vec<(ParaId, ParaHash)>) {
177	let mut parachains = Vec::with_capacity(heads.len());
178	let mut root = Default::default();
179	let mut mdb = MemoryDB::default();
180	let mut storage_keys = vec![];
181	{
182		let mut trie = TrieDBMutBuilderV1::<H::Hashing>::new(&mut mdb, &mut root).build();
183		for (parachain, head) in heads {
184			let storage_key =
185				parachain_head_storage_key_at_source(PARAS_PALLET_NAME, ParaId(parachain));
186			trie.insert(&storage_key.0, &head.encode())
187				.map_err(|_| "TrieMut::insert has failed")
188				.expect("TrieMut::insert should not fail in tests");
189			storage_keys.push(storage_key.0);
190			parachains.push((ParaId(parachain), head.hash()));
191		}
192	}
193
194	// generate storage proof to be delivered to this chain
195	let storage_proof = record_all_trie_keys::<LayoutV1<H::Hashing>, _>(&mdb, &root)
196		.map_err(|_| "record_all_trie_keys has failed")
197		.expect("record_all_trie_keys should not fail in benchmarks");
198
199	(root, ParaHeadsProof { storage_proof }, parachains)
200}
201
202/// Create signed precommit with given target.
203pub fn signed_precommit<H: HeaderT>(
204	signer: &Account,
205	target: (H::Hash, H::Number),
206	round: u64,
207	set_id: SetId,
208) -> finality_grandpa::SignedPrecommit<H::Hash, H::Number, AuthoritySignature, AuthorityId> {
209	let precommit = finality_grandpa::Precommit { target_hash: target.0, target_number: target.1 };
210
211	let encoded = sp_consensus_grandpa::localized_payload(
212		round,
213		set_id,
214		&finality_grandpa::Message::Precommit(precommit.clone()),
215	);
216
217	let signature = signer.sign(&encoded);
218	let raw_signature: Vec<u8> = signature.to_bytes().into();
219
220	// Need to wrap our signature and id types that they match what our `SignedPrecommit` is
221	// expecting
222	let signature = AuthoritySignature::try_from(raw_signature).expect(
223		"We know our Keypair is good,
224		so our signature must also be good.",
225	);
226	let id = (*signer).into();
227
228	finality_grandpa::SignedPrecommit { precommit, signature, id }
229}
230
231/// Get a header for testing.
232///
233/// The correct parent hash will be used if given a non-zero header.
234pub fn test_header<H: HeaderT>(number: H::Number) -> H {
235	let default = |num| {
236		H::new(num, Default::default(), Default::default(), Default::default(), Default::default())
237	};
238
239	let mut header = default(number);
240	if number != Zero::zero() {
241		let parent_hash = default(number - One::one()).hash();
242		header.set_parent_hash(parent_hash);
243	}
244
245	header
246}
247
248/// Get a header for testing with given `state_root`.
249///
250/// The correct parent hash will be used if given a non-zero header.
251pub fn test_header_with_root<H: HeaderT>(number: H::Number, state_root: H::Hash) -> H {
252	let mut header: H = test_header(number);
253	header.set_state_root(state_root);
254	header
255}
256
257/// Convenience function for generating a Header ID at a given block number.
258pub fn header_id<H: HeaderT>(index: u8) -> (H::Hash, H::Number) {
259	(test_header::<H>(index.into()).hash(), index.into())
260}
261
262#[macro_export]
263/// Adds methods for testing the `set_owner()` and `set_operating_mode()` for a pallet.
264/// Some values are hardcoded like:
265/// - `run_test()`
266/// - `Pallet::<TestRuntime>`
267/// - `PalletOwner::<TestRuntime>`
268/// - `PalletOperatingMode::<TestRuntime>`
269/// While this is not ideal, all the pallets use the same names, so it works for the moment.
270/// We can revisit this in the future if anything changes.
271macro_rules! generate_owned_bridge_module_tests {
272	($normal_operating_mode: expr, $halted_operating_mode: expr) => {
273		#[test]
274		fn test_set_owner() {
275			run_test(|| {
276				PalletOwner::<TestRuntime>::put(1);
277
278				// The root should be able to change the owner.
279				assert_ok!(Pallet::<TestRuntime>::set_owner(RuntimeOrigin::root(), Some(2)));
280				assert_eq!(PalletOwner::<TestRuntime>::get(), Some(2));
281
282				// The owner should be able to change the owner.
283				assert_ok!(Pallet::<TestRuntime>::set_owner(RuntimeOrigin::signed(2), Some(3)));
284				assert_eq!(PalletOwner::<TestRuntime>::get(), Some(3));
285
286				// Other users shouldn't be able to change the owner.
287				assert_noop!(
288					Pallet::<TestRuntime>::set_owner(RuntimeOrigin::signed(1), Some(4)),
289					DispatchError::BadOrigin
290				);
291				assert_eq!(PalletOwner::<TestRuntime>::get(), Some(3));
292			});
293		}
294
295		#[test]
296		fn test_set_operating_mode() {
297			run_test(|| {
298				PalletOwner::<TestRuntime>::put(1);
299				PalletOperatingMode::<TestRuntime>::put($normal_operating_mode);
300
301				// The root should be able to halt the pallet.
302				assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
303					RuntimeOrigin::root(),
304					$halted_operating_mode
305				));
306				assert_eq!(PalletOperatingMode::<TestRuntime>::get(), $halted_operating_mode);
307				// The root should be able to resume the pallet.
308				assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
309					RuntimeOrigin::root(),
310					$normal_operating_mode
311				));
312				assert_eq!(PalletOperatingMode::<TestRuntime>::get(), $normal_operating_mode);
313
314				// The owner should be able to halt the pallet.
315				assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
316					RuntimeOrigin::signed(1),
317					$halted_operating_mode
318				));
319				assert_eq!(PalletOperatingMode::<TestRuntime>::get(), $halted_operating_mode);
320				// The owner should be able to resume the pallet.
321				assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
322					RuntimeOrigin::signed(1),
323					$normal_operating_mode
324				));
325				assert_eq!(PalletOperatingMode::<TestRuntime>::get(), $normal_operating_mode);
326
327				// Other users shouldn't be able to halt the pallet.
328				assert_noop!(
329					Pallet::<TestRuntime>::set_operating_mode(
330						RuntimeOrigin::signed(2),
331						$halted_operating_mode
332					),
333					DispatchError::BadOrigin
334				);
335				assert_eq!(PalletOperatingMode::<TestRuntime>::get(), $normal_operating_mode);
336				// Other users shouldn't be able to resume the pallet.
337				PalletOperatingMode::<TestRuntime>::put($halted_operating_mode);
338				assert_noop!(
339					Pallet::<TestRuntime>::set_operating_mode(
340						RuntimeOrigin::signed(2),
341						$normal_operating_mode
342					),
343					DispatchError::BadOrigin
344				);
345				assert_eq!(PalletOperatingMode::<TestRuntime>::get(), $halted_operating_mode);
346			});
347		}
348	};
349}