1#![deny(unused_crate_dependencies)]
42
43use std::sync::Arc;
44
45use jsonrpsee::{
46	core::async_trait,
47	proc_macros::rpc,
48	types::{ErrorObject, ErrorObjectOwned},
49};
50
51use sc_client_api::StorageData;
52use sc_consensus_babe::{BabeWorkerHandle, Error as BabeError};
53use sp_blockchain::HeaderBackend;
54use sp_runtime::traits::{Block as BlockT, NumberFor};
55
56type SharedAuthoritySet<TBl> =
57	sc_consensus_grandpa::SharedAuthoritySet<<TBl as BlockT>::Hash, NumberFor<TBl>>;
58
59#[derive(Debug, thiserror::Error)]
61#[allow(missing_docs)]
62pub enum Error<Block: BlockT> {
63	#[error(transparent)]
64	Blockchain(#[from] sp_blockchain::Error),
65
66	#[error("Failed to load the block weight for block {0:?}")]
67	LoadingBlockWeightFailed(Block::Hash),
68
69	#[error("Failed to load the BABE epoch data: {0}")]
70	LoadingEpochDataFailed(BabeError<Block>),
71
72	#[error("JsonRpc error: {0}")]
73	JsonRpc(String),
74
75	#[error(
76		"The light sync state extension is not provided by the chain spec. \
77		Read the `sc-sync-state-rpc` crate docs on how to do this!"
78	)]
79	LightSyncStateExtensionNotFound,
80}
81
82impl<Block: BlockT> From<Error<Block>> for ErrorObjectOwned {
83	fn from(error: Error<Block>) -> Self {
84		let message = match error {
85			Error::JsonRpc(s) => s,
86			_ => error.to_string(),
87		};
88		ErrorObject::owned(1, message, None::<()>)
89	}
90}
91
92fn serialize_encoded<S: serde::Serializer, T: codec::Encode>(
94	val: &T,
95	s: S,
96) -> Result<S::Ok, S::Error> {
97	let encoded = StorageData(val.encode());
98	serde::Serialize::serialize(&encoded, s)
99}
100
101pub type LightSyncStateExtension = Option<serde_json::Value>;
106
107#[derive(serde::Serialize, Clone)]
109#[serde(rename_all = "camelCase")]
110#[serde(deny_unknown_fields)]
111pub struct LightSyncState<Block: BlockT> {
112	#[serde(serialize_with = "serialize_encoded")]
114	pub finalized_block_header: <Block as BlockT>::Header,
115	#[serde(serialize_with = "serialize_encoded")]
117	pub babe_epoch_changes: sc_consensus_epochs::EpochChangesFor<Block, sc_consensus_babe::Epoch>,
118	pub babe_finalized_block_weight: sc_consensus_babe::BabeBlockWeight,
120	#[serde(serialize_with = "serialize_encoded")]
122	pub grandpa_authority_set:
123		sc_consensus_grandpa::AuthoritySet<<Block as BlockT>::Hash, NumberFor<Block>>,
124}
125
126#[rpc(client, server)]
128pub trait SyncStateApi<B: BlockT> {
129	#[method(name = "sync_state_genSyncSpec")]
131	async fn system_gen_sync_spec(&self, raw: bool) -> Result<serde_json::Value, Error<B>>;
132}
133
134pub struct SyncState<Block: BlockT, Client> {
136	chain_spec: Box<dyn sc_chain_spec::ChainSpec>,
137	client: Arc<Client>,
138	shared_authority_set: SharedAuthoritySet<Block>,
139	babe_worker_handle: BabeWorkerHandle<Block>,
140}
141
142impl<Block, Client> SyncState<Block, Client>
143where
144	Block: BlockT,
145	Client: HeaderBackend<Block> + sc_client_api::AuxStore + 'static,
146{
147	pub fn new(
149		chain_spec: Box<dyn sc_chain_spec::ChainSpec>,
150		client: Arc<Client>,
151		shared_authority_set: SharedAuthoritySet<Block>,
152		babe_worker_handle: BabeWorkerHandle<Block>,
153	) -> Result<Self, Error<Block>> {
154		if sc_chain_spec::get_extension::<LightSyncStateExtension>(chain_spec.extensions())
155			.is_some()
156		{
157			Ok(Self { chain_spec, client, shared_authority_set, babe_worker_handle })
158		} else {
159			Err(Error::<Block>::LightSyncStateExtensionNotFound)
160		}
161	}
162
163	async fn build_sync_state(&self) -> Result<LightSyncState<Block>, Error<Block>> {
164		let epoch_changes = self
165			.babe_worker_handle
166			.epoch_data()
167			.await
168			.map_err(Error::LoadingEpochDataFailed)?;
169
170		let finalized_hash = self.client.info().finalized_hash;
171		let finalized_header = self
172			.client
173			.header(finalized_hash)?
174			.ok_or_else(|| sp_blockchain::Error::MissingHeader(finalized_hash.to_string()))?;
175
176		let finalized_block_weight =
177			sc_consensus_babe::aux_schema::load_block_weight(&*self.client, finalized_hash)?
178				.ok_or(Error::LoadingBlockWeightFailed(finalized_hash))?;
179
180		Ok(LightSyncState {
181			finalized_block_header: finalized_header,
182			babe_epoch_changes: epoch_changes,
183			babe_finalized_block_weight: finalized_block_weight,
184			grandpa_authority_set: self.shared_authority_set.clone_inner(),
185		})
186	}
187}
188
189#[async_trait]
190impl<Block, Backend> SyncStateApiServer<Block> for SyncState<Block, Backend>
191where
192	Block: BlockT,
193	Backend: HeaderBackend<Block> + sc_client_api::AuxStore + 'static,
194{
195	async fn system_gen_sync_spec(&self, raw: bool) -> Result<serde_json::Value, Error<Block>> {
196		let current_sync_state = self.build_sync_state().await?;
197		let mut chain_spec = self.chain_spec.cloned_box();
198
199		let extension = sc_chain_spec::get_extension_mut::<LightSyncStateExtension>(
200			chain_spec.extensions_mut(),
201		)
202		.ok_or(Error::<Block>::LightSyncStateExtensionNotFound)?;
203
204		let val = serde_json::to_value(¤t_sync_state)
205			.map_err(|e| Error::<Block>::JsonRpc(e.to_string()))?;
206		*extension = Some(val);
207
208		let json_str = chain_spec.as_json(raw).map_err(|e| Error::<Block>::JsonRpc(e))?;
209		serde_json::from_str(&json_str).map_err(|e| Error::<Block>::JsonRpc(e.to_string()))
210	}
211}