Skip to content

Commit

Permalink
chore: improve txpool rpc and add some docs (polkadot-evm#1227)
Browse files Browse the repository at this point in the history
  • Loading branch information
koushiro authored Oct 23, 2023
1 parent d5426d0 commit 9c50b61
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 45 deletions.
28 changes: 28 additions & 0 deletions client/rpc-core/src/txpool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,40 @@ use crate::types::*;
/// TxPool rpc interface
#[rpc(server)]
pub trait TxPoolApi {
/// The content inspection property can be queried to list the exact details of all the
/// transactions currently pending for inclusion in the next block(s), as well as the ones that
/// are being scheduled for future execution only.
///
/// The result is an object with two fields pending and queued. Each of these fields are
/// associative arrays, in which each entry maps an origin-address to a batch of scheduled
/// transactions. These batches themselves are maps associating nonces with actual transactions.
///
/// For details, see [txpool_content](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-txpool#txpool-content)
#[method(name = "txpool_content")]
fn content(&self) -> RpcResult<TxPoolResult<TransactionMap<TxPoolTransaction>>>;

/// The inspect inspection property can be queried to list a textual summary of all the
/// transactions currently pending for inclusion in the next block(s), as well as the ones that
/// are being scheduled for future execution only. This is a method specifically tailored to
/// developers to quickly see the transactions in the pool and find any potential issues.
///
/// The result is an object with two fields pending and queued. Each of these fields are
/// associative arrays, in which each entry maps an origin-address to a batch of scheduled
/// transactions. These batches themselves are maps associating nonces with transactions
/// summary strings.
///
/// For details, see [txpool_inspect](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-txpool#txpool-content)
#[method(name = "txpool_inspect")]
fn inspect(&self) -> RpcResult<TxPoolResult<TransactionMap<Summary>>>;

/// The status inspection property can be queried for the number of transactions currently
/// pending for inclusion in the next block(s), as well as the ones that are being scheduled
/// for future execution only.
///
/// The result is an object with two fields pending and queued, each of which is a counter
/// representing the number of transactions in that particular state.
///
/// For details, see [txpool_status](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-txpool#txpool-status)
#[method(name = "txpool_status")]
fn status(&self) -> RpcResult<TxPoolResult<U256>>;
}
9 changes: 9 additions & 0 deletions client/rpc-core/src/types/txpool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,31 @@ use serde::{Serialize, Serializer};

use crate::types::Bytes;

/// The entry maps an origin-address to a batch of scheduled transactions.
/// These batches themselves are maps associating nonces with actual transactions.
pub type TransactionMap<T> = HashMap<H160, HashMap<U256, T>>;

pub trait Get {
fn get(hash: H256, from_address: H160, txn: &EthereumTransaction) -> Self;
}

/// The result type of `txpool` API.
#[derive(Debug, Serialize)]
pub struct TxPoolResult<T: Serialize> {
pub pending: T,
pub queued: T,
}

/// The textual summary of all the transactions currently pending for inclusion in the next block(s).
#[derive(Clone, Debug)]
pub struct Summary {
/// Recipient
pub to: Option<H160>,
/// Transferred value
pub value: U256,
/// Gas
pub gas: U256,
/// Gas Price
pub gas_price: U256,
}

Expand Down Expand Up @@ -79,6 +87,7 @@ impl Get for Summary {
}
}

/// The exact details of all the transactions currently pending for inclusion in the next block(s)
#[derive(Debug, Default, Clone, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TxPoolTransaction {
Expand Down
71 changes: 32 additions & 39 deletions client/rpc/src/txpool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,15 @@ use fc_rpc_core::{
types::{Get, Summary, TransactionMap, TxPoolResult, TxPoolTransaction},
TxPoolApiServer,
};
use fp_rpc::{EthereumRuntimeRPCApi, TxPoolResponse};
use fp_rpc::EthereumRuntimeRPCApi;

use crate::{internal_err, public_key};

struct TxPoolTransactions {
ready: Vec<TransactionV2>,
future: Vec<TransactionV2>,
}

pub struct TxPool<B, C, A: ChainApi> {
client: Arc<C>,
graph: Arc<Pool<A>>,
Expand All @@ -62,35 +67,22 @@ where
C: HeaderBackend<B> + 'static,
A: ChainApi<Block = B> + 'static,
{
/// Use the transaction graph interface to get the extrinsics currently in the ready and future
/// queues.
fn map_build<T>(&self) -> RpcResult<TxPoolResult<TransactionMap<T>>>
where
T: Get + Serialize,
{
// Get the pending and queued ethereum transactions.
let ethereum_txns = self.tx_pool_response()?;
let txns = self.collect_txpool_transactions()?;
let pending = Self::build_txn_map::<'_, T>(txns.ready.iter());
let queued = Self::build_txn_map::<'_, T>(txns.future.iter());
Ok(TxPoolResult { pending, queued })
}

// Build the T response.
let mut pending = TransactionMap::<T>::new();
for txn in ethereum_txns.ready.iter() {
let hash = txn.hash();
let nonce = match txn {
TransactionV2::Legacy(t) => t.nonce,
TransactionV2::EIP2930(t) => t.nonce,
TransactionV2::EIP1559(t) => t.nonce,
};
let from_address = match public_key(txn) {
Ok(pk) => H160::from(H256::from(keccak_256(&pk))),
Err(_e) => H160::default(),
};
pending
.entry(from_address)
.or_insert_with(HashMap::new)
.insert(nonce, T::get(hash, from_address, txn));
}
let mut queued = TransactionMap::<T>::new();
for txn in ethereum_txns.future.iter() {
fn build_txn_map<'a, T>(txns: impl Iterator<Item = &'a TransactionV2>) -> TransactionMap<T>
where
T: Get + Serialize,
{
let mut result = TransactionMap::<T>::new();
for txn in txns {
let hash = txn.hash();
let nonce = match txn {
TransactionV2::Legacy(t) => t.nonce,
Expand All @@ -99,45 +91,46 @@ where
};
let from_address = match public_key(txn) {
Ok(pk) => H160::from(H256::from(keccak_256(&pk))),
Err(_e) => H160::default(),
Err(_) => H160::default(),
};
queued
result
.entry(from_address)
.or_insert_with(HashMap::new)
.insert(nonce, T::get(hash, from_address, txn));
}
Ok(TxPoolResult { pending, queued })
result
}

pub(crate) fn tx_pool_response(&self) -> RpcResult<TxPoolResponse> {
// Collect transactions in the ready validated pool.
let txs_ready = self
/// Collect the extrinsics currently in the ready and future queues.
fn collect_txpool_transactions(&self) -> RpcResult<TxPoolTransactions> {
// Collect extrinsics in the ready validated pool.
let ready_extrinsics = self
.graph
.validated_pool()
.ready()
.map(|in_pool_tx| in_pool_tx.data().clone())
.collect();

// Collect transactions in the future validated pool.
let txs_future = self
// Collect extrinsics in the future validated pool.
let future_extrinsics = self
.graph
.validated_pool()
.futures()
.iter()
.map(|(_hash, extrinsic)| extrinsic.clone())
.map(|(_, extrinsic)| extrinsic.clone())
.collect();

// Use the runtime to match the (here) opaque extrinsics against ethereum transactions.
let best_block = self.client.info().best_hash;
let api = self.client.runtime_api();
let ready = api
.extrinsic_filter(best_block, txs_ready)
.map_err(|err| internal_err(format!("fetch ready transactions failed: {:?}", err)))?;
.extrinsic_filter(best_block, ready_extrinsics)
.map_err(|err| internal_err(format!("fetch ready transactions failed: {err}")))?;
let future = api
.extrinsic_filter(best_block, txs_future)
.map_err(|err| internal_err(format!("fetch future transactions failed: {:?}", err)))?;
.extrinsic_filter(best_block, future_extrinsics)
.map_err(|err| internal_err(format!("fetch future transactions failed: {err}")))?;

Ok(TxPoolResponse { ready, future })
Ok(TxPoolTransactions { ready, future })
}
}

Expand Down
6 changes: 0 additions & 6 deletions primitives/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,6 @@ pub struct TransactionStatus {
pub logs_bloom: Bloom,
}

#[derive(Eq, PartialEq, Clone, Encode, Decode, RuntimeDebug)]
pub struct TxPoolResponse {
pub ready: Vec<ethereum::TransactionV2>,
pub future: Vec<ethereum::TransactionV2>,
}

pub trait RuntimeStorageOverride<B: BlockT, C>: Send + Sync {
fn is_enabled() -> bool;

Expand Down

0 comments on commit 9c50b61

Please sign in to comment.