Skip to content

Commit

Permalink
Add bevm-finality-rpc finalized tool (#697)
Browse files Browse the repository at this point in the history
Co-authored-by: icodezjb <[email protected]>
  • Loading branch information
icodezjb and icodezjb authored Feb 21, 2024
1 parent a8cbf0e commit a5421a1
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 0 deletions.
18 changes: 18 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ cli = { package = "chainx-cli", path = "cli", features = ["wasmtime"] }
[workspace]
members = [
"cli",
"client/rpc/finality",
"executor",
"primitives",
"primitives/assets-registrar",
Expand Down
21 changes: 21 additions & 0 deletions client/rpc/finality/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "bevm-finality-rpc"
description = "An experimental RPC to check for block and transaction finality in the bevm chain"
version = "5.2.2"
authors = ["The ChainX Authors"]
edition = "2021"

[dependencies]
futures = { version = "0.3", features = [ "compat" ] }
jsonrpc-core = "18.0.0"
jsonrpc-derive = "18.0.0"
parity-scale-codec = "3.0.0"
tokio = { version = "1.12.0", features = [ "sync", "time" ] }

fc-db = { git = "https://github.com/chainx-org/frontier", branch = "polkadot-v0.9.18-btc-fix2" }
fc-rpc = { git = "https://github.com/chainx-org/frontier", branch = "polkadot-v0.9.18-btc-fix2" }
sp-api = { git = "https://github.com/chainx-org/substrate", branch = "polkadot-v0.9.18-fix2" }
sp-blockchain = { git = "https://github.com/chainx-org/substrate", branch = "polkadot-v0.9.18-fix2" }
sp-core = { git = "https://github.com/chainx-org/substrate", branch = "polkadot-v0.9.18-fix2" }
sp-runtime = { git = "https://github.com/chainx-org/substrate", branch = "polkadot-v0.9.18-fix2" }

16 changes: 16 additions & 0 deletions client/rpc/finality/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# rpc

```json
"bevm": {
"isBlockFinalized": {
"description": "Returns whether an Ethereum block is finalized",
"params": [{ "name": "blockHash", "type": "Hash" }],
"type": "bool"
},
"isTxFinalized": {
"description": "Returns whether an Ethereum transaction is finalized",
"params": [{ "name": "txHash", "type": "Hash" }],
"type": "bool"
}
}
```
105 changes: 105 additions & 0 deletions client/rpc/finality/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright 2019-2023 ChainX Project Authors. Licensed under GPL-3.0.

use fc_rpc::frontier_backend_client::{self, is_canon};
use futures::{future::BoxFuture, FutureExt as _};
use jsonrpc_core::Result as RpcResult;
use jsonrpc_derive::rpc;
use sp_core::H256;
use std::{marker::PhantomData, sync::Arc};
//TODO ideally we wouldn't depend on BlockId here. Can we change frontier
// so it's load_hash helper returns an H256 instead of wrapping it in a BlockId?
use fc_db::Backend as FrontierBackend;
use sp_api::BlockId;
use sp_blockchain::HeaderBackend;
use sp_runtime::traits::Block;

/// An RPC endpoint to check for finality of blocks and transactions in Bevm
#[rpc(server)]
pub trait BevmFinalityApi {
/// Reports whether a Substrate or Ethereum block is finalized.
/// Returns false if the block is not found.
#[rpc(name = "bevm_isBlockFinalized")]
fn is_block_finalized(&self, block_hash: H256) -> BoxFuture<'static, RpcResult<bool>>;

/// Reports whether an Ethereum transaction is finalized.
/// Returns false if the transaction is not found
#[rpc(name = "bevm_isTxFinalized")]
fn is_tx_finalized(&self, tx_hash: H256) -> BoxFuture<'static, RpcResult<bool>>;
}

pub struct BevmFinality<B: Block, C> {
pub backend: Arc<FrontierBackend<B>>,
pub client: Arc<C>,
_phdata: PhantomData<B>,
}

impl<B: Block, C> BevmFinality<B, C> {
pub fn new(client: Arc<C>, backend: Arc<FrontierBackend<B>>) -> Self {
Self {
backend,
client,
_phdata: Default::default(),
}
}
}

impl<B, C> BevmFinalityApi for BevmFinality<B, C>
where
B: Block<Hash = H256>,
C: HeaderBackend<B> + Send + Sync + 'static,
{
fn is_block_finalized(&self, raw_hash: H256) -> BoxFuture<'static, RpcResult<bool>> {
let backend = self.backend.clone();
let client = self.client.clone();
async move { is_block_finalized_inner::<B, C>(&backend, &client, raw_hash) }.boxed()
}

fn is_tx_finalized(&self, tx_hash: H256) -> BoxFuture<'static, RpcResult<bool>> {
let backend = self.backend.clone();
let client = self.client.clone();
async move {
if let Some((ethereum_block_hash, _ethereum_index)) =
frontier_backend_client::load_transactions::<B, C>(
&client,
backend.as_ref(),
tx_hash,
true,
)?
{
is_block_finalized_inner::<B, C>(&backend, &client, ethereum_block_hash)
} else {
Ok(false)
}
}
.boxed()
}
}

fn is_block_finalized_inner<B: Block<Hash = H256>, C: HeaderBackend<B> + 'static>(
backend: &FrontierBackend<B>,
client: &C,
raw_hash: H256,
) -> RpcResult<bool> {
let substrate_hash = match frontier_backend_client::load_hash::<B>(backend, raw_hash)? {
// If we find this hash in the frontier data base, we know it is an eth hash
Some(BlockId::Hash(hash)) => hash,
Some(BlockId::Number(_)) => panic!("is_canon test only works with hashes."),
// Otherwise, we assume this is a Substrate hash.
None => raw_hash,
};

// First check whether the block is in the best chain
if !is_canon(client, substrate_hash) {
return Ok(false);
}

// At this point we know the block in question is in the current best chain.
// It's just a question of whether it is in the finalized prefix or not
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)
}
3 changes: 3 additions & 0 deletions rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ substrate-frame-rpc-system = { git = "https://github.com/chainx-org/substrate",
chainx-primitives = { path = "../primitives" }
xp-runtime = { path = "../primitives/runtime" }

# Finality-rpc
bevm-finality-rpc = { path = "../client/rpc/finality" }

# ChainX pallets
xpallet-assets-rpc = { path = "../xpallets/assets/rpc" }
xpallet-assets-rpc-runtime-api = { path = "../xpallets/assets/rpc/runtime-api" }
Expand Down
6 changes: 6 additions & 0 deletions rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ where
B::State: sc_client_api::backend::StateBackend<sp_runtime::traits::HashFor<Block>>,
A: ChainApi<Block = Block> + 'static,
{
use bevm_finality_rpc::{BevmFinality, BevmFinalityApi};
use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApi};
use substrate_frame_rpc_system::{FullSystem, SystemApi};
use xpallet_assets_rpc::{Assets, XAssetsApi};
Expand Down Expand Up @@ -338,6 +339,11 @@ where
fee_history_cache,
)));

io.extend_with(BevmFinalityApi::to_delegate(BevmFinality::new(
client.clone(),
backend.clone(),
)));

if let Some(filter_pool) = filter_pool {
io.extend_with(EthFilterApiServer::to_delegate(EthFilterApi::new(
client.clone(),
Expand Down

0 comments on commit a5421a1

Please sign in to comment.