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}