pallet_sidechain_rpc/
lib.rs1#![deny(missing_docs)]
72use derive_new::new;
73use jsonrpsee::{
74 core::{RpcResult, async_trait},
75 proc_macros::rpc,
76 types::{ErrorObject, ErrorObjectOwned, error::ErrorCode},
77};
78use sidechain_domain::mainchain_epoch::{MainchainEpochConfig, MainchainEpochDerivation};
79use sidechain_domain::{MainchainBlock, UtxoId};
80use sidechain_slots::SlotApi;
81use sp_api::ProvideRuntimeApi;
82use sp_core::offchain::Timestamp;
83use sp_runtime::traits::Block as BlockT;
84use sp_sidechain::GetGenesisUtxo;
85use std::sync::Arc;
86use time_source::*;
87use types::*;
88
89pub mod types;
91
92#[cfg(test)]
93mod tests;
94
95#[cfg(any(test))]
96mod mock;
97
98#[derive(serde::Serialize, serde::Deserialize, Clone, PartialEq, Debug)]
100pub struct GetParamsOutput {
101 pub genesis_utxo: UtxoId,
103}
104
105#[rpc(client, server, namespace = "sidechain")]
107pub trait SidechainRpcApi {
108 #[method(name = "getParams")]
113 fn get_params(&self) -> RpcResult<GetParamsOutput>;
114
115 #[method(name = "getStatus")]
117 async fn get_status(&self) -> RpcResult<GetStatusResponse>;
118}
119
120#[async_trait]
122pub trait SidechainRpcDataSource {
123 async fn get_latest_block_info(
125 &self,
126 ) -> Result<MainchainBlock, Box<dyn std::error::Error + Send + Sync>>;
127}
128
129#[derive(new)]
131pub struct SidechainRpc<C, Block> {
132 client: Arc<C>,
133 mc_epoch_config: MainchainEpochConfig,
134 data_source: Arc<dyn SidechainRpcDataSource + Send + Sync>,
135 time_source: Arc<dyn TimeSource + Send + Sync>,
136 _marker: std::marker::PhantomData<Block>,
137}
138
139impl<C, B> SidechainRpc<C, B> {
140 fn get_current_timestamp(&self) -> Timestamp {
141 Timestamp::from_unix_millis(self.time_source.get_current_time_millis())
142 }
143}
144
145#[async_trait]
146impl<C, Block> SidechainRpcApiServer for SidechainRpc<C, Block>
147where
148 Block: BlockT,
149 C: Send + Sync + 'static,
150 C: ProvideRuntimeApi<Block>,
151 C: GetBestHash<Block>,
152 C::Api: SlotApi<Block> + GetGenesisUtxo<Block>,
153{
154 fn get_params(&self) -> RpcResult<GetParamsOutput> {
155 let api = self.client.runtime_api();
156 let best_block = self.client.best_hash();
157
158 let genesis_utxo = api.genesis_utxo(best_block).map_err(error_object_from)?;
159
160 Ok(GetParamsOutput { genesis_utxo })
161 }
162
163 async fn get_status(&self) -> RpcResult<GetStatusResponse> {
164 let api = self.client.runtime_api();
165 let best_block = self.client.best_hash();
166
167 let slot_config = api.slot_config(best_block).map_err(error_object_from)?;
168
169 let current_timestamp = self.get_current_timestamp();
170 let current_sidechain_slot =
171 slot_config.slot_from_timestamp(current_timestamp.unix_millis());
172 let current_sidechain_epoch = slot_config.epoch_number(current_sidechain_slot);
173 let next_sidechain_epoch_timestamp = slot_config
174 .epoch_start_time(current_sidechain_epoch.next())
175 .ok_or(GetStatusRpcError::CannotConvertSidechainSlotToTimestamp)?;
176
177 let latest_mainchain_block =
178 self.data_source.get_latest_block_info().await.map_err(|err| {
179 ErrorObject::owned(
180 ErrorCode::InternalError.code(),
181 format!("Internal error: GetLatestBlockResponse error '{:?}", err),
182 None::<u8>,
183 )
184 })?;
185 let next_mainchain_epoch_timestamp = self
186 .mc_epoch_config
187 .mainchain_epoch_to_timestamp(latest_mainchain_block.epoch.next());
188
189 Ok(GetStatusResponse {
190 sidechain: SidechainData {
191 epoch: current_sidechain_epoch.0,
192 slot: current_sidechain_slot.into(),
193 next_epoch_timestamp: next_sidechain_epoch_timestamp,
194 },
195 mainchain: MainchainData {
196 epoch: latest_mainchain_block.epoch.0,
197 slot: latest_mainchain_block.slot.0,
198 next_epoch_timestamp: next_mainchain_epoch_timestamp,
199 },
200 })
201 }
202}
203
204fn error_object_from<T: std::fmt::Debug>(err: T) -> ErrorObjectOwned {
205 ErrorObject::owned::<u8>(-1, format!("{err:?}"), None)
206}