referrerpolicy=no-referrer-when-downgrade

cumulus_relay_chain_minimal_node/
blockchain_rpc_client.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Cumulus.
3// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
4
5// Cumulus is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9
10// Cumulus is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14
15// You should have received a copy of the GNU General Public License
16// along with Cumulus. If not, see <https://www.gnu.org/licenses/>.
17
18use std::{
19	collections::{BTreeMap, VecDeque},
20	pin::Pin,
21};
22
23use cumulus_primitives_core::{InboundDownwardMessage, ParaId, PersistedValidationData};
24use cumulus_relay_chain_interface::{RelayChainError, RelayChainResult};
25use cumulus_relay_chain_rpc_interface::RelayChainRpcClient;
26use futures::{Stream, StreamExt};
27use polkadot_core_primitives::{Block, BlockNumber, Hash, Header};
28use polkadot_overseer::{ChainApiBackend, RuntimeApiSubsystemClient};
29use polkadot_primitives::{
30	async_backing::{AsyncBackingParams, BackingState, Constraints},
31	slashing, ApprovalVotingParams, CoreIndex, NodeFeatures,
32};
33use sc_authority_discovery::{AuthorityDiscovery, Error as AuthorityDiscoveryError};
34use sc_client_api::AuxStore;
35use sp_api::{ApiError, RuntimeApiInfo};
36use sp_blockchain::Info;
37use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor};
38
39#[derive(Clone)]
40pub struct BlockChainRpcClient {
41	rpc_client: RelayChainRpcClient,
42}
43
44impl BlockChainRpcClient {
45	pub fn new(rpc_client: RelayChainRpcClient) -> Self {
46		Self { rpc_client }
47	}
48
49	pub async fn chain_get_header(
50		&self,
51		hash: Option<Hash>,
52	) -> Result<Option<Header>, RelayChainError> {
53		self.rpc_client.chain_get_header(hash).await
54	}
55
56	pub async fn block_get_hash(
57		&self,
58		number: Option<BlockNumber>,
59	) -> Result<Option<Hash>, RelayChainError> {
60		self.rpc_client.chain_get_block_hash(number).await
61	}
62}
63
64#[async_trait::async_trait]
65impl ChainApiBackend for BlockChainRpcClient {
66	async fn header(
67		&self,
68		hash: <Block as BlockT>::Hash,
69	) -> sp_blockchain::Result<Option<<Block as BlockT>::Header>> {
70		Ok(self.rpc_client.chain_get_header(Some(hash)).await?)
71	}
72
73	async fn info(&self) -> sp_blockchain::Result<Info<Block>> {
74		let (best_header_opt, genesis_hash, finalized_head) = futures::try_join!(
75			self.rpc_client.chain_get_header(None),
76			self.rpc_client.chain_get_head(Some(0)),
77			self.rpc_client.chain_get_finalized_head()
78		)?;
79		let best_header = best_header_opt.ok_or_else(|| {
80			RelayChainError::GenericError(
81				"Unable to retrieve best header from relay chain.".to_string(),
82			)
83		})?;
84
85		let finalized_header =
86			self.rpc_client.chain_get_header(Some(finalized_head)).await?.ok_or_else(|| {
87				RelayChainError::GenericError(
88					"Unable to retrieve finalized header from relay chain.".to_string(),
89				)
90			})?;
91		Ok(Info {
92			best_hash: best_header.hash(),
93			best_number: best_header.number,
94			genesis_hash,
95			finalized_hash: finalized_head,
96			finalized_number: finalized_header.number,
97			finalized_state: Some((finalized_header.hash(), finalized_header.number)),
98			number_leaves: 1,
99			block_gap: None,
100		})
101	}
102
103	async fn number(
104		&self,
105		hash: <Block as BlockT>::Hash,
106	) -> sp_blockchain::Result<Option<<<Block as BlockT>::Header as HeaderT>::Number>> {
107		Ok(self
108			.rpc_client
109			.chain_get_header(Some(hash))
110			.await?
111			.map(|maybe_header| maybe_header.number))
112	}
113
114	async fn hash(
115		&self,
116		number: NumberFor<Block>,
117	) -> sp_blockchain::Result<Option<<Block as BlockT>::Hash>> {
118		Ok(self.rpc_client.chain_get_block_hash(number.into()).await?)
119	}
120}
121
122#[async_trait::async_trait]
123impl RuntimeApiSubsystemClient for BlockChainRpcClient {
124	async fn validators(
125		&self,
126		at: Hash,
127	) -> Result<Vec<polkadot_primitives::ValidatorId>, sp_api::ApiError> {
128		Ok(self.rpc_client.parachain_host_validators(at).await?)
129	}
130
131	async fn validator_groups(
132		&self,
133		at: Hash,
134	) -> Result<
135		(
136			Vec<Vec<polkadot_primitives::ValidatorIndex>>,
137			polkadot_primitives::GroupRotationInfo<BlockNumber>,
138		),
139		sp_api::ApiError,
140	> {
141		Ok(self.rpc_client.parachain_host_validator_groups(at).await?)
142	}
143
144	async fn availability_cores(
145		&self,
146		at: Hash,
147	) -> Result<
148		Vec<polkadot_primitives::CoreState<Hash, polkadot_core_primitives::BlockNumber>>,
149		sp_api::ApiError,
150	> {
151		Ok(self.rpc_client.parachain_host_availability_cores(at).await?)
152	}
153
154	async fn persisted_validation_data(
155		&self,
156		at: Hash,
157		para_id: ParaId,
158		assumption: polkadot_primitives::OccupiedCoreAssumption,
159	) -> Result<Option<PersistedValidationData<Hash, BlockNumber>>, sp_api::ApiError> {
160		Ok(self
161			.rpc_client
162			.parachain_host_persisted_validation_data(at, para_id, assumption)
163			.await?)
164	}
165
166	async fn assumed_validation_data(
167		&self,
168		at: Hash,
169		para_id: ParaId,
170		expected_persisted_validation_data_hash: Hash,
171	) -> Result<
172		Option<(
173			PersistedValidationData<Hash, BlockNumber>,
174			polkadot_primitives::ValidationCodeHash,
175		)>,
176		sp_api::ApiError,
177	> {
178		Ok(self
179			.rpc_client
180			.parachain_host_assumed_validation_data(
181				at,
182				para_id,
183				expected_persisted_validation_data_hash,
184			)
185			.await?)
186	}
187
188	async fn check_validation_outputs(
189		&self,
190		at: Hash,
191		para_id: ParaId,
192		outputs: polkadot_primitives::CandidateCommitments,
193	) -> Result<bool, sp_api::ApiError> {
194		Ok(self
195			.rpc_client
196			.parachain_host_check_validation_outputs(at, para_id, outputs)
197			.await?)
198	}
199
200	async fn session_index_for_child(
201		&self,
202		at: Hash,
203	) -> Result<polkadot_primitives::SessionIndex, sp_api::ApiError> {
204		Ok(self.rpc_client.parachain_host_session_index_for_child(at).await?)
205	}
206
207	async fn validation_code(
208		&self,
209		at: Hash,
210		para_id: ParaId,
211		assumption: polkadot_primitives::OccupiedCoreAssumption,
212	) -> Result<Option<polkadot_primitives::ValidationCode>, sp_api::ApiError> {
213		Ok(self.rpc_client.parachain_host_validation_code(at, para_id, assumption).await?)
214	}
215
216	async fn candidate_pending_availability(
217		&self,
218		at: Hash,
219		para_id: cumulus_primitives_core::ParaId,
220	) -> Result<Option<polkadot_primitives::CommittedCandidateReceiptV2<Hash>>, sp_api::ApiError> {
221		Ok(self
222			.rpc_client
223			.parachain_host_candidate_pending_availability(at, para_id)
224			.await?)
225	}
226
227	async fn candidate_events(
228		&self,
229		at: Hash,
230	) -> Result<Vec<polkadot_primitives::CandidateEvent<Hash>>, sp_api::ApiError> {
231		Ok(self.rpc_client.parachain_host_candidate_events(at).await?)
232	}
233
234	async fn dmq_contents(
235		&self,
236		at: Hash,
237		recipient: ParaId,
238	) -> Result<Vec<InboundDownwardMessage<BlockNumber>>, sp_api::ApiError> {
239		Ok(self.rpc_client.parachain_host_dmq_contents(recipient, at).await?)
240	}
241
242	async fn inbound_hrmp_channels_contents(
243		&self,
244		at: Hash,
245		recipient: ParaId,
246	) -> Result<
247		std::collections::BTreeMap<
248			ParaId,
249			Vec<polkadot_core_primitives::InboundHrmpMessage<BlockNumber>>,
250		>,
251		sp_api::ApiError,
252	> {
253		Ok(self
254			.rpc_client
255			.parachain_host_inbound_hrmp_channels_contents(recipient, at)
256			.await?)
257	}
258
259	async fn validation_code_by_hash(
260		&self,
261		at: Hash,
262		validation_code_hash: polkadot_primitives::ValidationCodeHash,
263	) -> Result<Option<polkadot_primitives::ValidationCode>, sp_api::ApiError> {
264		Ok(self
265			.rpc_client
266			.parachain_host_validation_code_by_hash(at, validation_code_hash)
267			.await?)
268	}
269
270	async fn on_chain_votes(
271		&self,
272		at: Hash,
273	) -> Result<Option<polkadot_primitives::ScrapedOnChainVotes<Hash>>, sp_api::ApiError> {
274		Ok(self.rpc_client.parachain_host_on_chain_votes(at).await?)
275	}
276
277	async fn session_info(
278		&self,
279		at: Hash,
280		index: polkadot_primitives::SessionIndex,
281	) -> Result<Option<polkadot_primitives::SessionInfo>, sp_api::ApiError> {
282		Ok(self.rpc_client.parachain_host_session_info(at, index).await?)
283	}
284
285	async fn session_executor_params(
286		&self,
287		at: Hash,
288		session_index: polkadot_primitives::SessionIndex,
289	) -> Result<Option<polkadot_primitives::ExecutorParams>, sp_api::ApiError> {
290		Ok(self
291			.rpc_client
292			.parachain_host_session_executor_params(at, session_index)
293			.await?)
294	}
295
296	async fn submit_pvf_check_statement(
297		&self,
298		at: Hash,
299		stmt: polkadot_primitives::PvfCheckStatement,
300		signature: polkadot_primitives::ValidatorSignature,
301	) -> Result<(), sp_api::ApiError> {
302		Ok(self
303			.rpc_client
304			.parachain_host_submit_pvf_check_statement(at, stmt, signature)
305			.await?)
306	}
307
308	async fn pvfs_require_precheck(
309		&self,
310		at: Hash,
311	) -> Result<Vec<polkadot_primitives::ValidationCodeHash>, sp_api::ApiError> {
312		Ok(self.rpc_client.parachain_host_pvfs_require_precheck(at).await?)
313	}
314
315	async fn validation_code_hash(
316		&self,
317		at: Hash,
318		para_id: ParaId,
319		assumption: polkadot_primitives::OccupiedCoreAssumption,
320	) -> Result<Option<polkadot_primitives::ValidationCodeHash>, sp_api::ApiError> {
321		Ok(self
322			.rpc_client
323			.parachain_host_validation_code_hash(at, para_id, assumption)
324			.await?)
325	}
326
327	async fn current_epoch(&self, at: Hash) -> Result<sp_consensus_babe::Epoch, sp_api::ApiError> {
328		Ok(self.rpc_client.babe_api_current_epoch(at).await?)
329	}
330
331	async fn authorities(
332		&self,
333		at: Hash,
334	) -> std::result::Result<Vec<polkadot_primitives::AuthorityDiscoveryId>, sp_api::ApiError> {
335		Ok(self.rpc_client.authority_discovery_authorities(at).await?)
336	}
337
338	async fn api_version_parachain_host(&self, at: Hash) -> Result<Option<u32>, sp_api::ApiError> {
339		let api_id = <dyn polkadot_primitives::runtime_api::ParachainHost<Block>>::ID;
340		Ok(self.rpc_client.runtime_version(at).await.map(|v| v.api_version(&api_id))?)
341	}
342
343	async fn disputes(
344		&self,
345		at: Hash,
346	) -> Result<
347		Vec<(
348			polkadot_primitives::SessionIndex,
349			polkadot_primitives::CandidateHash,
350			polkadot_primitives::DisputeState<polkadot_primitives::BlockNumber>,
351		)>,
352		ApiError,
353	> {
354		Ok(self.rpc_client.parachain_host_disputes(at).await?)
355	}
356
357	async fn unapplied_slashes(
358		&self,
359		at: Hash,
360	) -> Result<
361		Vec<(
362			polkadot_primitives::SessionIndex,
363			polkadot_primitives::CandidateHash,
364			slashing::PendingSlashes,
365		)>,
366		ApiError,
367	> {
368		Ok(self.rpc_client.parachain_host_unapplied_slashes(at).await?)
369	}
370
371	async fn key_ownership_proof(
372		&self,
373		at: Hash,
374		validator_id: polkadot_primitives::ValidatorId,
375	) -> Result<Option<slashing::OpaqueKeyOwnershipProof>, ApiError> {
376		Ok(self.rpc_client.parachain_host_key_ownership_proof(at, validator_id).await?)
377	}
378
379	async fn submit_report_dispute_lost(
380		&self,
381		at: Hash,
382		dispute_proof: slashing::DisputeProof,
383		key_ownership_proof: slashing::OpaqueKeyOwnershipProof,
384	) -> Result<Option<()>, ApiError> {
385		Ok(self
386			.rpc_client
387			.parachain_host_submit_report_dispute_lost(at, dispute_proof, key_ownership_proof)
388			.await?)
389	}
390
391	async fn minimum_backing_votes(
392		&self,
393		at: Hash,
394		session_index: polkadot_primitives::SessionIndex,
395	) -> Result<u32, ApiError> {
396		Ok(self.rpc_client.parachain_host_minimum_backing_votes(at, session_index).await?)
397	}
398
399	async fn disabled_validators(
400		&self,
401		at: Hash,
402	) -> Result<Vec<polkadot_primitives::ValidatorIndex>, ApiError> {
403		Ok(self.rpc_client.parachain_host_disabled_validators(at).await?)
404	}
405
406	async fn async_backing_params(&self, at: Hash) -> Result<AsyncBackingParams, ApiError> {
407		Ok(self.rpc_client.parachain_host_async_backing_params(at).await?)
408	}
409
410	async fn para_backing_state(
411		&self,
412		at: Hash,
413		para_id: ParaId,
414	) -> Result<Option<BackingState>, ApiError> {
415		Ok(self.rpc_client.parachain_host_para_backing_state(at, para_id).await?)
416	}
417
418	/// Approval voting configuration parameters
419	async fn approval_voting_params(
420		&self,
421		at: Hash,
422		session_index: polkadot_primitives::SessionIndex,
423	) -> Result<ApprovalVotingParams, ApiError> {
424		Ok(self
425			.rpc_client
426			.parachain_host_staging_approval_voting_params(at, session_index)
427			.await?)
428	}
429
430	async fn node_features(&self, at: Hash) -> Result<NodeFeatures, ApiError> {
431		Ok(self.rpc_client.parachain_host_node_features(at).await?)
432	}
433
434	async fn claim_queue(
435		&self,
436		at: Hash,
437	) -> Result<BTreeMap<CoreIndex, VecDeque<ParaId>>, ApiError> {
438		Ok(self.rpc_client.parachain_host_claim_queue(at).await?)
439	}
440
441	async fn candidates_pending_availability(
442		&self,
443		at: Hash,
444		para_id: cumulus_primitives_core::ParaId,
445	) -> Result<Vec<polkadot_primitives::CommittedCandidateReceiptV2<Hash>>, sp_api::ApiError> {
446		Ok(self
447			.rpc_client
448			.parachain_host_candidates_pending_availability(at, para_id)
449			.await?)
450	}
451
452	async fn backing_constraints(
453		&self,
454		at: Hash,
455		para_id: ParaId,
456	) -> Result<Option<Constraints>, ApiError> {
457		Ok(self.rpc_client.parachain_host_backing_constraints(at, para_id).await?)
458	}
459
460	async fn scheduling_lookahead(&self, at: Hash) -> Result<u32, sp_api::ApiError> {
461		Ok(self.rpc_client.parachain_host_scheduling_lookahead(at).await?)
462	}
463
464	async fn validation_code_bomb_limit(&self, at: Hash) -> Result<u32, sp_api::ApiError> {
465		Ok(self.rpc_client.parachain_host_validation_code_bomb_limit(at).await?)
466	}
467
468	async fn para_ids(&self, at: Hash) -> Result<Vec<ParaId>, sp_api::ApiError> {
469		Ok(self.rpc_client.parachain_host_para_ids(at).await?)
470	}
471}
472
473#[async_trait::async_trait]
474impl AuthorityDiscovery<Block> for BlockChainRpcClient {
475	async fn authorities(
476		&self,
477		at: Hash,
478	) -> std::result::Result<Vec<polkadot_primitives::AuthorityDiscoveryId>, sp_api::ApiError> {
479		let result = self.rpc_client.authority_discovery_authorities(at).await?;
480		Ok(result)
481	}
482
483	async fn best_hash(&self) -> std::result::Result<Hash, AuthorityDiscoveryError> {
484		self.block_get_hash(None)
485			.await
486			.ok()
487			.flatten()
488			.ok_or_else(|| AuthorityDiscoveryError::BestBlockFetchingError)
489	}
490}
491
492impl BlockChainRpcClient {
493	pub async fn import_notification_stream(
494		&self,
495	) -> RelayChainResult<Pin<Box<dyn Stream<Item = Header> + Send>>> {
496		Ok(self.rpc_client.get_imported_heads_stream()?.boxed())
497	}
498
499	pub async fn finality_notification_stream(
500		&self,
501	) -> RelayChainResult<Pin<Box<dyn Stream<Item = Header> + Send>>> {
502		Ok(self.rpc_client.get_finalized_heads_stream()?.boxed())
503	}
504}
505
506// Implementation required by ChainApiSubsystem
507// but never called in our case.
508impl AuxStore for BlockChainRpcClient {
509	fn insert_aux<
510		'a,
511		'b: 'a,
512		'c: 'a,
513		I: IntoIterator<Item = &'a (&'c [u8], &'c [u8])>,
514		D: IntoIterator<Item = &'a &'b [u8]>,
515	>(
516		&self,
517		_insert: I,
518		_delete: D,
519	) -> sp_blockchain::Result<()> {
520		unimplemented!("Not supported on the RPC collator")
521	}
522
523	fn get_aux(&self, _key: &[u8]) -> sp_blockchain::Result<Option<Vec<u8>>> {
524		unimplemented!("Not supported on the RPC collator")
525	}
526}