referrerpolicy=no-referrer-when-downgrade

cumulus_test_client/
block_builder.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Cumulus.
3
4// Cumulus 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// Cumulus 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 Cumulus.  If not, see <http://www.gnu.org/licenses/>.
16
17use crate::Client;
18use codec::Encode;
19use cumulus_primitives_core::{ParachainBlockData, PersistedValidationData};
20use cumulus_primitives_parachain_inherent::{ParachainInherentData, INHERENT_IDENTIFIER};
21use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
22use cumulus_test_runtime::{Block, GetLastTimestamp, Hash, Header};
23use polkadot_primitives::{BlockNumber as PBlockNumber, Hash as PHash};
24use sc_block_builder::BlockBuilderBuilder;
25use sp_api::{ProofRecorder, ProofRecorderIgnoredNodes, ProvideRuntimeApi};
26use sp_consensus_aura::{AuraApi, Slot};
27use sp_runtime::{traits::Header as HeaderT, Digest, DigestItem};
28
29/// A struct containing a block builder and support data required to build test scenarios.
30pub struct BlockBuilderAndSupportData<'a> {
31	pub block_builder: sc_block_builder::BlockBuilder<'a, Block, Client>,
32	pub persisted_validation_data: PersistedValidationData<PHash, PBlockNumber>,
33}
34
35/// An extension for the Cumulus test client to init a block builder.
36pub trait InitBlockBuilder {
37	/// Init a specific block builder that works for the test runtime.
38	///
39	/// This will automatically create and push the inherents for you to make the block
40	/// valid for the test runtime.
41	///
42	/// You can use the relay chain state sproof builder to arrange required relay chain state or
43	/// just use a default one. The relay chain slot in the storage proof
44	/// will be adjusted to align with the parachain slot to pass validation.
45	///
46	/// Returns the block builder and validation data for further usage.
47	fn init_block_builder(
48		&self,
49		validation_data: Option<PersistedValidationData<PHash, PBlockNumber>>,
50		relay_sproof_builder: RelayStateSproofBuilder,
51	) -> BlockBuilderAndSupportData;
52
53	/// Init a specific block builder at a specific block that works for the test runtime.
54	///
55	/// Same as [`InitBlockBuilder::init_block_builder`] besides that it takes a
56	/// [`type@Hash`] to say which should be the parent block of the block that is being build.
57	fn init_block_builder_at(
58		&self,
59		at: Hash,
60		validation_data: Option<PersistedValidationData<PHash, PBlockNumber>>,
61		relay_sproof_builder: RelayStateSproofBuilder,
62	) -> BlockBuilderAndSupportData;
63
64	/// Init a specific block builder using the given pre-digests.
65	///
66	/// Same as [`InitBlockBuilder::init_block_builder`] besides that it takes vector of
67	/// [`DigestItem`]'s that are passed as pre-digest to the block builder.
68	fn init_block_builder_with_pre_digests(
69		&self,
70		validation_data: Option<PersistedValidationData<PHash, PBlockNumber>>,
71		relay_sproof_builder: RelayStateSproofBuilder,
72		pre_digests: Vec<DigestItem>,
73	) -> BlockBuilderAndSupportData;
74	/// Init a specific block builder at a specific block that works for the test runtime.
75	///
76	/// Same as [`InitBlockBuilder::init_block_builder_with_timestamp`] besides that it takes
77	/// `ignored_nodes` that instruct the proof recorder to not record these nodes.
78	fn init_block_builder_with_ignored_nodes(
79		&self,
80		at: Hash,
81		validation_data: Option<PersistedValidationData<PHash, PBlockNumber>>,
82		relay_sproof_builder: RelayStateSproofBuilder,
83		timestamp: u64,
84		ignored_nodes: ProofRecorderIgnoredNodes<Block>,
85	) -> BlockBuilderAndSupportData;
86
87	/// Init a specific block builder that works for the test runtime.
88	///
89	/// Same as [`InitBlockBuilder::init_block_builder`] besides that it takes a
90	/// [`type@Hash`] to say which should be the parent block of the block that is being build and
91	/// it will use the given `timestamp` as input for the timestamp inherent.
92	fn init_block_builder_with_timestamp(
93		&self,
94		at: Hash,
95		validation_data: Option<PersistedValidationData<PHash, PBlockNumber>>,
96		relay_sproof_builder: RelayStateSproofBuilder,
97		timestamp: u64,
98	) -> BlockBuilderAndSupportData;
99}
100
101fn init_block_builder(
102	client: &Client,
103	at: Hash,
104	validation_data: Option<PersistedValidationData<PHash, PBlockNumber>>,
105	mut relay_sproof_builder: RelayStateSproofBuilder,
106	timestamp: Option<u64>,
107	extra_pre_digests: Option<Vec<DigestItem>>,
108	ignored_nodes: Option<ProofRecorderIgnoredNodes<Block>>,
109) -> BlockBuilderAndSupportData<'_> {
110	let timestamp = timestamp.unwrap_or_else(|| {
111		let last_timestamp =
112			client.runtime_api().get_last_timestamp(at).expect("Get last timestamp");
113
114		if last_timestamp == 0 {
115			if relay_sproof_builder.current_slot != 0u64 {
116				*relay_sproof_builder.current_slot * 6_000
117			} else {
118				std::time::SystemTime::now()
119					.duration_since(std::time::SystemTime::UNIX_EPOCH)
120					.expect("Time is always after UNIX_EPOCH; qed")
121					.as_millis() as u64
122			}
123		} else {
124			last_timestamp + client.runtime_api().slot_duration(at).unwrap().as_millis()
125		}
126	});
127
128	let slot: Slot =
129		(timestamp / client.runtime_api().slot_duration(at).unwrap().as_millis()).into();
130
131	if relay_sproof_builder.current_slot == 0u64 {
132		relay_sproof_builder.current_slot = (timestamp / 6_000).into();
133	}
134
135	let pre_digests = Digest {
136		logs: extra_pre_digests
137			.unwrap_or_default()
138			.into_iter()
139			.chain(std::iter::once(DigestItem::PreRuntime(
140				sp_consensus_aura::AURA_ENGINE_ID,
141				slot.encode(),
142			)))
143			.collect::<Vec<_>>(),
144	};
145
146	let mut block_builder = BlockBuilderBuilder::new(client)
147		.on_parent_block(at)
148		.fetch_parent_block_number(client)
149		.unwrap()
150		.with_proof_recorder(Some(ProofRecorder::<Block>::with_ignored_nodes(
151			ignored_nodes.unwrap_or_default(),
152		)))
153		.with_inherent_digests(pre_digests)
154		.build()
155		.expect("Creates new block builder for test runtime");
156
157	let mut inherent_data = sp_inherents::InherentData::new();
158
159	inherent_data
160		.put_data(sp_timestamp::INHERENT_IDENTIFIER, &timestamp)
161		.expect("Put timestamp failed");
162
163	let (relay_parent_storage_root, relay_chain_state) =
164		relay_sproof_builder.into_state_root_and_proof();
165
166	let mut validation_data = validation_data.unwrap_or_default();
167	validation_data.relay_parent_storage_root = relay_parent_storage_root;
168
169	inherent_data
170		.put_data(
171			INHERENT_IDENTIFIER,
172			&ParachainInherentData {
173				validation_data: validation_data.clone(),
174				relay_chain_state,
175				downward_messages: Default::default(),
176				horizontal_messages: Default::default(),
177				relay_parent_descendants: Default::default(),
178				collator_peer_id: None,
179			},
180		)
181		.expect("Put validation function params failed");
182
183	let inherents = block_builder.create_inherents(inherent_data).expect("Creates inherents");
184
185	inherents
186		.into_iter()
187		.for_each(|ext| block_builder.push(ext).expect("Pushes inherent"));
188
189	BlockBuilderAndSupportData { block_builder, persisted_validation_data: validation_data }
190}
191
192impl InitBlockBuilder for Client {
193	fn init_block_builder(
194		&self,
195		validation_data: Option<PersistedValidationData<PHash, PBlockNumber>>,
196		relay_sproof_builder: RelayStateSproofBuilder,
197	) -> BlockBuilderAndSupportData {
198		let chain_info = self.chain_info();
199		self.init_block_builder_at(chain_info.best_hash, validation_data, relay_sproof_builder)
200	}
201
202	fn init_block_builder_with_pre_digests(
203		&self,
204		validation_data: Option<PersistedValidationData<PHash, PBlockNumber>>,
205		relay_sproof_builder: RelayStateSproofBuilder,
206		pre_digests: Vec<DigestItem>,
207	) -> BlockBuilderAndSupportData {
208		let chain_info = self.chain_info();
209		init_block_builder(
210			self,
211			chain_info.best_hash,
212			validation_data,
213			relay_sproof_builder,
214			None,
215			Some(pre_digests),
216			None,
217		)
218	}
219
220	fn init_block_builder_at(
221		&self,
222		at: Hash,
223		validation_data: Option<PersistedValidationData<PHash, PBlockNumber>>,
224		relay_sproof_builder: RelayStateSproofBuilder,
225	) -> BlockBuilderAndSupportData {
226		init_block_builder(self, at, validation_data, relay_sproof_builder, None, None, None)
227	}
228
229	fn init_block_builder_with_ignored_nodes(
230		&self,
231		at: Hash,
232		validation_data: Option<PersistedValidationData<PHash, PBlockNumber>>,
233		relay_sproof_builder: RelayStateSproofBuilder,
234		timestamp: u64,
235		ignored_nodes: ProofRecorderIgnoredNodes<Block>,
236	) -> BlockBuilderAndSupportData {
237		init_block_builder(
238			self,
239			at,
240			validation_data,
241			relay_sproof_builder,
242			Some(timestamp),
243			None,
244			Some(ignored_nodes),
245		)
246	}
247
248	fn init_block_builder_with_timestamp(
249		&self,
250		at: Hash,
251		validation_data: Option<PersistedValidationData<PHash, PBlockNumber>>,
252		relay_sproof_builder: RelayStateSproofBuilder,
253		timestamp: u64,
254	) -> BlockBuilderAndSupportData {
255		init_block_builder(
256			self,
257			at,
258			validation_data,
259			relay_sproof_builder,
260			Some(timestamp),
261			None,
262			None,
263		)
264	}
265}
266
267/// Extension trait for the [`BlockBuilder`](sc_block_builder::BlockBuilder) to build directly a
268/// [`ParachainBlockData`].
269pub trait BuildParachainBlockData {
270	/// Directly build the [`ParachainBlockData`] from the block that comes out of the block
271	/// builder.
272	fn build_parachain_block(self, parent_state_root: Hash) -> ParachainBlockData<Block>;
273}
274
275impl<'a> BuildParachainBlockData for sc_block_builder::BlockBuilder<'a, Block, Client> {
276	fn build_parachain_block(self, parent_state_root: Hash) -> ParachainBlockData<Block> {
277		let built_block = self.build().expect("Builds the block");
278
279		let storage_proof = built_block
280			.proof
281			.expect("We enabled proof recording before.")
282			.into_compact_proof::<<Header as HeaderT>::Hashing>(parent_state_root)
283			.expect("Creates the compact proof");
284
285		ParachainBlockData::new(vec![built_block.block], storage_proof)
286	}
287}