moonbeam_finality_rpc/
lib.rs1use fc_rpc::frontier_backend_client::{self, is_canon};
17
18use jsonrpsee::types::error::ErrorObject;
19use jsonrpsee::{core::RpcResult, proc_macros::rpc};
20use sp_blockchain::HeaderBackend;
21use sp_core::H256;
22use sp_runtime::traits::Block;
23use std::ops::Deref;
24use std::{marker::PhantomData, sync::Arc};
25
26#[rpc(server)]
28#[async_trait::async_trait]
29pub trait MoonbeamFinalityApi {
30 #[method(name = "moon_isBlockFinalized")]
33 async fn is_block_finalized(&self, block_hash: H256) -> RpcResult<bool>;
34
35 #[method(name = "moon_isTxFinalized")]
38 async fn is_tx_finalized(&self, tx_hash: H256) -> RpcResult<bool>;
39
40 #[method(name = "moon_getEthSyncBlockRange")]
42 async fn get_frontier_sync_block_range(&self) -> RpcResult<(H256, H256)>;
43}
44
45pub struct MoonbeamFinality<B: Block, C> {
46 pub backend: Arc<dyn fc_api::Backend<B>>,
47 pub client: Arc<C>,
48 _phdata: PhantomData<B>,
49}
50
51impl<B: Block, C> MoonbeamFinality<B, C> {
52 pub fn new(client: Arc<C>, backend: Arc<dyn fc_api::Backend<B>>) -> Self {
53 Self {
54 backend,
55 client,
56 _phdata: Default::default(),
57 }
58 }
59}
60
61#[async_trait::async_trait]
62impl<B, C> MoonbeamFinalityApiServer for MoonbeamFinality<B, C>
63where
64 B: Block<Hash = H256>,
65 C: HeaderBackend<B> + Send + Sync + 'static,
66{
67 async fn is_block_finalized(&self, raw_hash: H256) -> RpcResult<bool> {
68 let client = self.client.clone();
69 is_block_finalized_inner::<B, C>(self.backend.as_ref(), &client, raw_hash).await
70 }
71
72 async fn is_tx_finalized(&self, tx_hash: H256) -> RpcResult<bool> {
73 let client = self.client.clone();
74
75 if let Some((ethereum_block_hash, _ethereum_index)) =
76 frontier_backend_client::load_transactions::<B, C>(
77 &client,
78 self.backend.as_ref(),
79 tx_hash,
80 true,
81 )
82 .await?
83 {
84 is_block_finalized_inner::<B, C>(self.backend.as_ref(), &client, ethereum_block_hash)
85 .await
86 } else {
87 Ok(false)
88 }
89 }
90
91 async fn get_frontier_sync_block_range(&self) -> RpcResult<(H256, H256)> {
92 match (
93 self.backend.deref().first_block_hash().await,
94 self.backend.deref().latest_block_hash().await,
95 ) {
96 (Ok(first), Ok(last)) => Ok((first, last)),
97 (Err(e), _) => Err(ErrorObject::owned(
98 jsonrpsee::types::error::UNKNOWN_ERROR_CODE,
99 "No synced block",
100 Some(e),
101 )),
102 (_, Err(e)) => Err(ErrorObject::owned(
103 jsonrpsee::types::error::UNKNOWN_ERROR_CODE,
104 "No synced block",
105 Some(e),
106 )),
107 }
108 }
109}
110
111async fn is_block_finalized_inner<B: Block<Hash = H256>, C: HeaderBackend<B> + 'static>(
112 backend: &(dyn fc_api::Backend<B>),
113 client: &C,
114 raw_hash: H256,
115) -> RpcResult<bool> {
116 let substrate_hash =
117 match frontier_backend_client::load_hash::<B, C>(client, backend, raw_hash).await? {
118 Some(hash) => hash,
120 None => raw_hash,
122 };
123
124 if !is_canon(client, substrate_hash) {
126 return Ok(false);
127 }
128
129 let query_height = client
132 .number(substrate_hash)
133 .expect("No sp_blockchain::Error should be thrown when looking up hash")
134 .expect("Block is already known to be canon, so it must be in the chain");
135 let finalized_height = client.info().finalized_number;
136
137 Ok(query_height <= finalized_height)
138}