use fc_rpc::frontier_backend_client::{self, is_canon};
use jsonrpsee::types::error::ErrorObject;
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use sp_blockchain::HeaderBackend;
use sp_core::H256;
use sp_runtime::traits::Block;
use std::ops::Deref;
use std::{marker::PhantomData, sync::Arc};
#[rpc(server)]
#[async_trait::async_trait]
pub trait MoonbeamFinalityApi {
#[method(name = "moon_isBlockFinalized")]
async fn is_block_finalized(&self, block_hash: H256) -> RpcResult<bool>;
#[method(name = "moon_isTxFinalized")]
async fn is_tx_finalized(&self, tx_hash: H256) -> RpcResult<bool>;
#[method(name = "moon_getEthSyncBlockRange")]
async fn get_frontier_sync_block_range(&self) -> RpcResult<(H256, H256)>;
}
pub struct MoonbeamFinality<B: Block, C> {
pub backend: Arc<dyn fc_api::Backend<B>>,
pub client: Arc<C>,
_phdata: PhantomData<B>,
}
impl<B: Block, C> MoonbeamFinality<B, C> {
pub fn new(client: Arc<C>, backend: Arc<dyn fc_api::Backend<B>>) -> Self {
Self {
backend,
client,
_phdata: Default::default(),
}
}
}
#[async_trait::async_trait]
impl<B, C> MoonbeamFinalityApiServer for MoonbeamFinality<B, C>
where
B: Block<Hash = H256>,
C: HeaderBackend<B> + Send + Sync + 'static,
{
async fn is_block_finalized(&self, raw_hash: H256) -> RpcResult<bool> {
let client = self.client.clone();
is_block_finalized_inner::<B, C>(self.backend.as_ref(), &client, raw_hash).await
}
async fn is_tx_finalized(&self, tx_hash: H256) -> RpcResult<bool> {
let client = self.client.clone();
if let Some((ethereum_block_hash, _ethereum_index)) =
frontier_backend_client::load_transactions::<B, C>(
&client,
self.backend.as_ref(),
tx_hash,
true,
)
.await?
{
is_block_finalized_inner::<B, C>(self.backend.as_ref(), &client, ethereum_block_hash)
.await
} else {
Ok(false)
}
}
async fn get_frontier_sync_block_range(&self) -> RpcResult<(H256, H256)> {
match (
self.backend.deref().first_block_hash().await,
self.backend.deref().latest_block_hash().await,
) {
(Ok(first), Ok(last)) => Ok((first, last)),
(Err(e), _) => Err(ErrorObject::owned(
jsonrpsee::types::error::UNKNOWN_ERROR_CODE,
"No synced block",
Some(e),
)),
(_, Err(e)) => Err(ErrorObject::owned(
jsonrpsee::types::error::UNKNOWN_ERROR_CODE,
"No synced block",
Some(e),
)),
}
}
}
async fn is_block_finalized_inner<B: Block<Hash = H256>, C: HeaderBackend<B> + 'static>(
backend: &(dyn fc_api::Backend<B>),
client: &C,
raw_hash: H256,
) -> RpcResult<bool> {
let substrate_hash =
match frontier_backend_client::load_hash::<B, C>(client, backend, raw_hash).await? {
Some(hash) => hash,
None => raw_hash,
};
if !is_canon(client, substrate_hash) {
return Ok(false);
}
let query_height = client
.number(substrate_hash)
.expect("No sp_blockchain::Error should be thrown when looking up hash")
.expect("Block is already known to be canon, so it must be in the chain");
let finalized_height = client.info().finalized_number;
Ok(query_height <= finalized_height)
}