44//!
55//! ## Implementing runtime APIs
66//!
7- //! Your runtime should implement the [SlotApi ] and [GetGenesisUtxo ] runtime APIs. For example, if
8- //! your chain uses Aura for consensus, they may be implemented similar to this:
7+ //! Your runtime should implement the [GetGenesisUtxo ] and [GetEpochDurationApi ] runtime APIs.
8+ //! For example, if your chain uses Aura for consensus, they may be implemented similar to this:
99//! ```rust,ignore
10- //! impl sidechain_slots::SlotApi<Block> for Runtime {
11- //! fn slot_config() -> sidechain_slots::ScSlotConfig {
12- //! sidechain_slots::ScSlotConfig {
13- //! slots_per_epoch: Sidechain::slots_per_epoch(),
14- //! slot_duration: SlotDuration::from(Aura::slot_duration())
15- //! }
16- //! }
17- //! }
1810//! impl sp_sidechain::GetGenesisUtxo<Block> for Runtime {
1911//! fn genesis_utxo() -> UtxoId {
2012//! Sidechain::genesis_utxo()
2113//! }
2214//! }
15+ //!
16+ //! impl sp_sidechain::GetEpochDurationApi<Block> for Runtime {
17+ //! fn get_epoch_duration_millis() -> u64 {
18+ //! BLOCKS_PER_EPOCH * MILLISECS_PER_BLOCK
19+ //! }
20+ //! }
2321//! ```
2422//!
2523//! ## Adding to the RPC stack
3028//! # use jsonrpsee::RpcModule;
3129//! # use pallet_sidechain_rpc::{*, types::GetBestHash};
3230//! # use sidechain_domain::mainchain_epoch::MainchainEpochConfig;
33- //! # use sidechain_slots::SlotApi;
34- //! # use sp_api::ProvideRuntimeApi;
31+ //! # use sp_api::{ CallApiAt, ProvideRuntimeApi };
3532//! # use sp_runtime::traits::Block as BlockT;
36- //! # use sp_sidechain::GetGenesisUtxo;
33+ //! # use sp_sidechain::{ GetEpochDurationApi, GetGenesisUtxo } ;
3734//! # use std::sync::Arc;
3835//! # use time_source::TimeSource;
3936//! fn create_rpc<B: BlockT, C: Send + Sync + 'static>(
4239//! data_source: Arc<dyn SidechainRpcDataSource + Send + Sync>,
4340//! ) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
4441//! where
45- //! C: ProvideRuntimeApi<B> + GetBestHash<B>,
46- //! C::Api: SlotApi <B> + GetGenesisUtxo <B>
42+ //! C: ProvideRuntimeApi<B> + GetBestHash<B> + CallApiAt<B> ,
43+ //! C::Api: GetGenesisUtxo <B> + GetEpochDurationApi <B>
4744//! {
4845//!
4946//! let mut module = RpcModule::new(());
6663//! data source is provided by the Partner Chain toolkit in the `partner_chains_db_sync_data_sources`
6764//! crate.
6865//!
66+ //! ## Legacy compatibility mode
67+ //!
68+ //! In previous versions of the Partner Chains toolkit, the RPC services in this crate relied on now
69+ //! deprecated runtime api `sp_sidechain::SlotApi` to compute Partner Chain epochs and current slot.
70+ //! For Partner Chains that started before this dependency was removed, a compatibility mode is
71+ //! provided behind the `legacy-slotapi-compat` feature flag. Enabling this flag will cause the nodes to
72+ //! use this runtime API when present and optionally include information about current Patner Chain
73+ //! slot in `sidechain.slot` field of `sidechain_getStatus` response.
74+ //!
6975//! [GetGenesisUtxo]: sp_sidechain::GetGenesisUtxo
70- //! [SlotApi ]: sidechain_slots::SlotApi
76+ //! [GetEpochDurationApi ]: sp_sidechain::GetEpochDurationApi
7177#![ deny( missing_docs) ]
7278use derive_new:: new;
7379use jsonrpsee:: {
7480 core:: { RpcResult , async_trait} ,
7581 proc_macros:: rpc,
7682 types:: { ErrorObject , ErrorObjectOwned , error:: ErrorCode } ,
7783} ;
84+ #[ cfg( feature = "legacy-slotapi-compat" ) ]
85+ use legacy_compat:: slots:: * ;
7886use sidechain_domain:: mainchain_epoch:: { MainchainEpochConfig , MainchainEpochDerivation } ;
7987use sidechain_domain:: { MainchainBlock , UtxoId } ;
80- use sidechain_slots:: SlotApi ;
8188use sp_api:: ProvideRuntimeApi ;
82- use sp_core:: offchain:: Timestamp ;
89+ use sp_core:: offchain:: { Duration , Timestamp } ;
8390use sp_runtime:: traits:: Block as BlockT ;
84- use sp_sidechain:: GetGenesisUtxo ;
91+ use sp_sidechain:: { GetEpochDurationApi , GetGenesisUtxo } ;
8592use std:: sync:: Arc ;
8693use time_source:: * ;
8794use types:: * ;
8895
8996/// Response types returned by RPC endpoints for Sidechain pallet
9097pub mod types;
9198
99+ #[ cfg( feature = "legacy-slotapi-compat" ) ]
100+ mod legacy_compat;
101+
92102#[ cfg( test) ]
93103mod tests;
94104
@@ -142,37 +152,89 @@ impl<C, B> SidechainRpc<C, B> {
142152 }
143153}
144154
155+ /// Runtime client that can serve data to sidechain RPC
156+ trait SidechainRpcClient < Block : BlockT > {
157+ /// Returns Partner Chain epoch duration
158+ fn get_epoch_duration (
159+ & self ,
160+ best_block : Block :: Hash ,
161+ ) -> Result < Duration , Box < dyn std:: error:: Error + Send + Sync > > ;
162+
163+ /// Returns the Partner Chain's genesis UTXO
164+ fn get_genesis_utxo (
165+ & self ,
166+ best_block : Block :: Hash ,
167+ ) -> Result < UtxoId , Box < dyn std:: error:: Error + Send + Sync > > ;
168+
169+ #[ cfg( feature = "legacy-slotapi-compat" ) ]
170+ /// Returns slot duration
171+ fn get_maybe_slot_duration ( & self , best_block : Block :: Hash ) -> Option < u64 > ;
172+ }
173+
174+ impl < Block , Client > SidechainRpcClient < Block > for Client
175+ where
176+ Block : BlockT ,
177+ Client : sp_api:: CallApiAt < Block > + Send + Sync + ' static ,
178+ Client : ProvideRuntimeApi < Block > ,
179+ Client :: Api : GetEpochDurationApi < Block > ,
180+ Client :: Api : GetGenesisUtxo < Block > ,
181+ {
182+ fn get_epoch_duration (
183+ & self ,
184+ best_block : Block :: Hash ,
185+ ) -> Result < Duration , Box < dyn std:: error:: Error + Send + Sync > > {
186+ #[ cfg( feature = "legacy-slotapi-compat" ) ]
187+ if let Some ( slot_config) = self . get_sc_slot_config ( best_block) {
188+ return Ok ( Duration :: from_millis (
189+ u64:: from ( slot_config. slots_per_epoch ) * slot_config. slot_duration_millis ,
190+ ) ) ;
191+ }
192+
193+ Ok ( Duration :: from_millis ( self . runtime_api ( ) . get_epoch_duration_millis ( best_block) ?) )
194+ }
195+
196+ fn get_genesis_utxo (
197+ & self ,
198+ best_block : Block :: Hash ,
199+ ) -> Result < UtxoId , Box < dyn std:: error:: Error + Send + Sync > > {
200+ Ok ( self . runtime_api ( ) . genesis_utxo ( best_block) ?)
201+ }
202+
203+ #[ cfg( feature = "legacy-slotapi-compat" ) ]
204+ fn get_maybe_slot_duration ( & self , best_block : Block :: Hash ) -> Option < u64 > {
205+ let slot_config = self . get_sc_slot_config ( best_block) ?;
206+ Some ( slot_config. slot_duration_millis )
207+ }
208+ }
209+
145210#[ async_trait]
146211impl < C , Block > SidechainRpcApiServer for SidechainRpc < C , Block >
147212where
148213 Block : BlockT ,
149214 C : Send + Sync + ' static ,
150- C : ProvideRuntimeApi < Block > ,
215+ C : SidechainRpcClient < Block > ,
151216 C : GetBestHash < Block > ,
152- C :: Api : SlotApi < Block > + GetGenesisUtxo < Block > ,
153217{
154218 fn get_params ( & self ) -> RpcResult < GetParamsOutput > {
155- let api = self . client . runtime_api ( ) ;
156219 let best_block = self . client . best_hash ( ) ;
157220
158- let genesis_utxo = api . genesis_utxo ( best_block) . map_err ( error_object_from) ?;
221+ let genesis_utxo = self . client . get_genesis_utxo ( best_block) . map_err ( error_object_from) ?;
159222
160223 Ok ( GetParamsOutput { genesis_utxo } )
161224 }
162225
163226 async fn get_status ( & self ) -> RpcResult < GetStatusResponse > {
164- let api = self . client . runtime_api ( ) ;
165227 let best_block = self . client . best_hash ( ) ;
166228
167- let slot_config = api. slot_config ( best_block) . map_err ( error_object_from) ?;
168-
169229 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 ) ?;
230+
231+ let sc_epoch_duration: Duration =
232+ self . client . get_epoch_duration ( best_block) . map_err ( error_object_from) ?;
233+
234+ let current_sidechain_epoch = current_timestamp. unix_millis ( ) / sc_epoch_duration. millis ( ) ;
235+
236+ let next_sidechain_epoch_timestamp =
237+ Timestamp :: from ( ( current_sidechain_epoch + 1 ) * sc_epoch_duration. millis ( ) ) ;
176238
177239 let latest_mainchain_block =
178240 self . data_source . get_latest_block_info ( ) . await . map_err ( |err| {
@@ -188,8 +250,10 @@ where
188250
189251 Ok ( GetStatusResponse {
190252 sidechain : SidechainData {
191- epoch : current_sidechain_epoch. 0 ,
192- slot : current_sidechain_slot. into ( ) ,
253+ epoch : current_sidechain_epoch,
254+ #[ cfg( feature = "legacy-slotapi-compat" ) ]
255+ slot : ( self . client . get_maybe_slot_duration ( best_block) )
256+ . map ( |duration| current_timestamp. unix_millis ( ) / duration) ,
193257 next_epoch_timestamp : next_sidechain_epoch_timestamp,
194258 } ,
195259 mainchain : MainchainData {
0 commit comments