diff --git a/Cargo.lock b/Cargo.lock index fef979bbe4..003e986fa6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2224,6 +2224,7 @@ dependencies = [ "sc-transaction-pool", "sc-transaction-pool-api", "sc-utils", + "serde", "sp-api", "sp-block-builder", "sp-blockchain", diff --git a/client/rpc-core/src/lib.rs b/client/rpc-core/src/lib.rs index 701c095e2f..c5728a8e7e 100644 --- a/client/rpc-core/src/lib.rs +++ b/client/rpc-core/src/lib.rs @@ -23,11 +23,13 @@ pub mod types; mod eth; mod eth_pubsub; mod net; +mod txpool; mod web3; pub use self::{ eth::{EthApiServer, EthFilterApiServer}, eth_pubsub::EthPubSubApiServer, net::NetApiServer, + txpool::TxPoolApiServer, web3::Web3ApiServer, }; diff --git a/client/rpc-core/src/txpool.rs b/client/rpc-core/src/txpool.rs new file mode 100644 index 0000000000..3cea401121 --- /dev/null +++ b/client/rpc-core/src/txpool.rs @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2015-2022 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! tx pool rpc interface + +use ethereum_types::U256; +use jsonrpsee::{core::RpcResult, proc_macros::rpc}; +// Frontier +use crate::types::*; + +/// TxPool rpc interface +#[rpc(server)] +pub trait TxPoolApi { + #[method(name = "txpool_content")] + fn content(&self) -> RpcResult>>; + + #[method(name = "txpool_inspect")] + fn inspect(&self) -> RpcResult>>; + + #[method(name = "txpool_status")] + fn status(&self) -> RpcResult>; +} diff --git a/client/rpc-core/src/types/filter.rs b/client/rpc-core/src/types/filter.rs index 6fa1adfc46..4d7dd09a9a 100644 --- a/client/rpc-core/src/types/filter.rs +++ b/client/rpc-core/src/types/filter.rs @@ -17,7 +17,7 @@ // along with this program. If not, see . use std::{ - collections::BTreeMap, + collections::{BTreeMap, HashSet}, sync::{Arc, Mutex}, }; @@ -460,6 +460,7 @@ pub struct FilterPoolItem { pub last_poll: BlockNumber, pub filter_type: FilterType, pub at_block: u64, + pub pending_transaction_hashes: HashSet, } /// On-memory stored filters created through the `eth_newFilter` RPC. diff --git a/client/rpc-core/src/types/mod.rs b/client/rpc-core/src/types/mod.rs index 4384415428..98b6e5b417 100644 --- a/client/rpc-core/src/types/mod.rs +++ b/client/rpc-core/src/types/mod.rs @@ -31,6 +31,7 @@ mod receipt; mod sync; mod transaction; mod transaction_request; +mod txpool; mod work; pub mod pubsub; @@ -55,5 +56,6 @@ pub use self::{ }, transaction::{LocalTransactionStatus, RichRawTransaction, Transaction}, transaction_request::{TransactionMessage, TransactionRequest}, + txpool::{Get, Summary, TransactionMap, TxPoolResult, TxPoolTransaction}, work::Work, }; diff --git a/client/rpc-core/src/types/txpool.rs b/client/rpc-core/src/types/txpool.rs new file mode 100644 index 0000000000..ed70f504ec --- /dev/null +++ b/client/rpc-core/src/types/txpool.rs @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2015-2022 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use std::collections::HashMap; + +use ethereum::{TransactionAction, TransactionV2 as EthereumTransaction}; +use ethereum_types::{H160, H256, U256}; +use serde::{Serialize, Serializer}; +// Frontier +use crate::types::Bytes; + +pub type TransactionMap = HashMap>; + +pub trait Get { + fn get(hash: H256, from_address: H160, txn: &EthereumTransaction) -> Self; +} + +#[derive(Debug, Serialize)] +pub struct TxPoolResult { + pub pending: T, + pub queued: T, +} + +#[derive(Clone, Debug)] +pub struct Summary { + pub to: Option, + pub value: U256, + pub gas: U256, + pub gas_price: U256, +} + +impl Serialize for Summary { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let res = format!( + "0x{:x}: {} wei + {} gas x {} wei", + self.to.unwrap_or_default(), + self.value, + self.gas, + self.gas_price + ); + serializer.serialize_str(&res) + } +} + +impl Get for Summary { + fn get(_hash: H256, _from_address: H160, txn: &EthereumTransaction) -> Self { + let (action, value, gas_price, gas_limit) = match txn { + EthereumTransaction::Legacy(t) => (t.action, t.value, t.gas_price, t.gas_limit), + EthereumTransaction::EIP2930(t) => (t.action, t.value, t.gas_price, t.gas_limit), + EthereumTransaction::EIP1559(t) => (t.action, t.value, t.max_fee_per_gas, t.gas_limit), + }; + Self { + to: match action { + TransactionAction::Call(to) => Some(to), + _ => None, + }, + value, + gas_price, + gas: gas_limit, + } + } +} + +#[derive(Debug, Default, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct TxPoolTransaction { + /// Hash + pub hash: H256, + /// Nonce + pub nonce: U256, + /// Block hash + #[serde(serialize_with = "block_hash_serialize")] + pub block_hash: Option, + /// Block number + pub block_number: Option, + /// Sender + pub from: H160, + /// Recipient + #[serde(serialize_with = "to_serialize")] + pub to: Option, + /// Transfered value + pub value: U256, + /// Gas Price + pub gas_price: U256, + /// Gas + pub gas: U256, + /// Data + pub input: Bytes, + /// Transaction Index + pub transaction_index: Option, +} + +fn block_hash_serialize(hash: &Option, serializer: S) -> Result +where + S: Serializer, +{ + serializer.serialize_str(&format!("0x{:x}", hash.unwrap_or_default())) +} + +fn to_serialize(hash: &Option, serializer: S) -> Result +where + S: Serializer, +{ + serializer.serialize_str(&format!("0x{:x}", hash.unwrap_or_default())) +} + +impl Get for TxPoolTransaction { + fn get(hash: H256, from_address: H160, txn: &EthereumTransaction) -> Self { + let (nonce, action, value, gas_price, gas_limit, input) = match txn { + EthereumTransaction::Legacy(t) => ( + t.nonce, + t.action, + t.value, + t.gas_price, + t.gas_limit, + t.input.clone(), + ), + EthereumTransaction::EIP2930(t) => ( + t.nonce, + t.action, + t.value, + t.gas_price, + t.gas_limit, + t.input.clone(), + ), + EthereumTransaction::EIP1559(t) => ( + t.nonce, + t.action, + t.value, + t.max_fee_per_gas, + t.gas_limit, + t.input.clone(), + ), + }; + Self { + hash, + nonce, + block_hash: None, + block_number: None, + from: from_address, + to: match action { + TransactionAction::Call(to) => Some(to), + _ => None, + }, + value, + gas_price, + gas: gas_limit, + input: Bytes(input), + transaction_index: None, + } + } +} diff --git a/client/rpc/Cargo.toml b/client/rpc/Cargo.toml index 820bdbb4ed..000f065a44 100644 --- a/client/rpc/Cargo.toml +++ b/client/rpc/Cargo.toml @@ -24,6 +24,7 @@ prometheus = { version = "0.13.1", default-features = false } rand = "0.8" rlp = { workspace = true } scale-codec = { package = "parity-scale-codec", workspace = true } +serde = { workspace = true } tokio = { version = "1.24", features = ["sync"] } # Substrate diff --git a/client/rpc/src/eth/filter.rs b/client/rpc/src/eth/filter.rs index fe3f1e2502..c0dd08ab8f 100644 --- a/client/rpc/src/eth/filter.rs +++ b/client/rpc/src/eth/filter.rs @@ -16,13 +16,14 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::{marker::PhantomData, sync::Arc, time}; +use std::{collections::HashSet, marker::PhantomData, sync::Arc, time}; use ethereum::BlockV2 as EthereumBlock; use ethereum_types::{H256, U256}; use jsonrpsee::core::{async_trait, RpcResult}; // Substrate use sc_client_api::backend::{Backend, StorageProvider}; +use sc_transaction_pool::ChainApi; use sp_api::ProvideRuntimeApi; use sp_blockchain::HeaderBackend; use sp_core::hashing::keccak_256; @@ -31,14 +32,14 @@ use sp_runtime::{ traits::{Block as BlockT, NumberFor, One, Saturating, UniqueSaturatedInto}, }; // Frontier +use crate::{eth::cache::EthBlockDataCacheTask, frontier_backend_client, internal_err, TxPool}; use fc_rpc_core::{types::*, EthFilterApiServer}; use fp_rpc::{EthereumRuntimeRPCApi, TransactionStatus}; -use crate::{eth::cache::EthBlockDataCacheTask, frontier_backend_client, internal_err}; - -pub struct EthFilter { +pub struct EthFilter { client: Arc, backend: Arc + Send + Sync>, + tx_pool: TxPool, filter_pool: FilterPool, max_stored_filters: usize, max_past_logs: u32, @@ -46,10 +47,11 @@ pub struct EthFilter { _marker: PhantomData, } -impl EthFilter { +impl EthFilter { pub fn new( client: Arc, backend: Arc + Send + Sync>, + tx_pool: TxPool, filter_pool: FilterPool, max_stored_filters: usize, max_past_logs: u32, @@ -58,6 +60,7 @@ impl EthFilter { Self { client, backend, + tx_pool, filter_pool, max_stored_filters, max_past_logs, @@ -67,10 +70,12 @@ impl EthFilter { } } -impl EthFilter +impl EthFilter where + A: ChainApi + 'static, B: BlockT, - C: HeaderBackend, + C: HeaderBackend + ProvideRuntimeApi + 'static, + C::Api: EthereumRuntimeRPCApi, { fn create_filter(&self, filter_type: FilterType) -> RpcResult { let block_number = @@ -90,6 +95,16 @@ where Some((k, _)) => *k, None => U256::zero(), }; + let pending_transaction_hashes = if let FilterType::PendingTransaction = filter_type { + self.tx_pool + .tx_pool_response()? + .ready + .into_iter() + .map(|tx| tx.hash()) + .collect() + } else { + HashSet::new() + }; // Assume `max_stored_filters` is always < U256::max. let key = last_key.checked_add(U256::one()).unwrap(); locked.insert( @@ -98,6 +113,7 @@ where last_poll: BlockNumber::Num(block_number), filter_type, at_block: block_number, + pending_transaction_hashes, }, ); Ok(key) @@ -109,12 +125,12 @@ where } #[async_trait] -impl EthFilterApiServer for EthFilter +impl EthFilterApiServer for EthFilter where + A: ChainApi + 'static, B: BlockT, - C: ProvideRuntimeApi, + C: HeaderBackend + ProvideRuntimeApi + StorageProvider + 'static, C::Api: EthereumRuntimeRPCApi, - C: HeaderBackend + StorageProvider + 'static, BE: Backend + 'static, { fn new_filter(&self, filter: Filter) -> RpcResult { @@ -126,7 +142,7 @@ where } fn new_pending_transaction_filter(&self) -> RpcResult { - Err(internal_err("Method not available.")) + self.create_filter(FilterType::PendingTransaction) } async fn filter_changes(&self, index: Index) -> RpcResult { @@ -143,6 +159,9 @@ where last: u64, next: u64, }, + PendingTransaction { + new_hashes: Vec, + }, Log { filter: Filter, from_number: NumberFor, @@ -171,11 +190,40 @@ where last_poll: BlockNumber::Num(next), filter_type: pool_item.filter_type.clone(), at_block: pool_item.at_block, + pending_transaction_hashes: HashSet::new(), }, ); FuturePath::::Block { last, next } } + FilterType::PendingTransaction => { + let previous_hashes = pool_item.pending_transaction_hashes; + let current_hashes: HashSet = self + .tx_pool + .tx_pool_response()? + .ready + .into_iter() + .map(|tx| tx.hash()) + .collect(); + + // Update filter `last_poll`. + locked.insert( + key, + FilterPoolItem { + last_poll: BlockNumber::Num(block_number + 1), + filter_type: pool_item.filter_type.clone(), + at_block: pool_item.at_block, + pending_transaction_hashes: current_hashes.clone(), + }, + ); + + let mew_hashes = current_hashes + .difference(&previous_hashes) + .collect::>(); + FuturePath::PendingTransaction { + new_hashes: mew_hashes.into_iter().copied().collect(), + } + } // For each event since last poll, get a vector of ethereum logs. FilterType::Log(filter) => { // Update filter `last_poll`. @@ -185,6 +233,7 @@ where last_poll: BlockNumber::Num(block_number + 1), filter_type: pool_item.filter_type.clone(), at_block: pool_item.at_block, + pending_transaction_hashes: HashSet::new(), }, ); @@ -222,8 +271,6 @@ where current_number, } } - // Should never reach here. - _ => FuturePath::Error(internal_err("Method not available.")), } } else { FuturePath::Error(internal_err(format!("Filter id {:?} does not exist.", key))) @@ -257,6 +304,7 @@ where } Ok(FilterChanges::Hashes(ethereum_hashes)) } + FuturePath::PendingTransaction { new_hashes } => Ok(FilterChanges::Hashes(new_hashes)), FuturePath::Log { filter, from_number, @@ -472,9 +520,8 @@ async fn filter_range_logs_indexed( ) -> RpcResult<()> where B: BlockT, - C: ProvideRuntimeApi, + C: HeaderBackend + ProvideRuntimeApi + StorageProvider + 'static, C::Api: EthereumRuntimeRPCApi, - C: HeaderBackend + StorageProvider + 'static, BE: Backend + 'static, { use std::time::Instant; diff --git a/client/rpc/src/lib.rs b/client/rpc/src/lib.rs index 2cc91cc820..c9e78b379c 100644 --- a/client/rpc/src/lib.rs +++ b/client/rpc/src/lib.rs @@ -30,6 +30,7 @@ mod eth; mod eth_pubsub; mod net; mod signer; +mod txpool; mod web3; pub use self::{ @@ -37,11 +38,14 @@ pub use self::{ eth_pubsub::{EthPubSub, EthereumSubIdProvider}, net::Net, signer::{EthDevSigner, EthSigner}, + txpool::TxPool, web3::Web3, }; + pub use ethereum::TransactionV2 as EthereumTransaction; pub use fc_rpc_core::{ - EthApiServer, EthFilterApiServer, EthPubSubApiServer, NetApiServer, Web3ApiServer, + EthApiServer, EthFilterApiServer, EthPubSubApiServer, NetApiServer, TxPoolApiServer, + Web3ApiServer, }; pub use fc_storage::{ OverrideHandle, RuntimeApiStorageOverride, SchemaV1Override, SchemaV2Override, diff --git a/client/rpc/src/txpool.rs b/client/rpc/src/txpool.rs new file mode 100644 index 0000000000..ab8e7c54a3 --- /dev/null +++ b/client/rpc/src/txpool.rs @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2020-2022 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use std::{collections::HashMap, marker::PhantomData, sync::Arc}; + +use ethereum::TransactionV2; +use ethereum_types::{H160, H256, U256}; +use jsonrpsee::core::RpcResult; +use serde::Serialize; +// substrate +use sc_transaction_pool::{ChainApi, Pool}; +use sc_transaction_pool_api::InPoolTransaction; +use sp_api::ProvideRuntimeApi; +use sp_blockchain::HeaderBackend; +use sp_core::hashing::keccak_256; +use sp_runtime::traits::Block as BlockT; +// Frontier +use crate::{internal_err, public_key}; +use fc_rpc_core::{ + types::{Get, Summary, TransactionMap, TxPoolResult, TxPoolTransaction}, + TxPoolApiServer, +}; +use fp_rpc::{EthereumRuntimeRPCApi, TxPoolResponse}; + +pub struct TxPool { + client: Arc, + graph: Arc>, + _marker: PhantomData, +} + +impl Clone for TxPool { + fn clone(&self) -> Self { + Self { + client: self.client.clone(), + graph: self.graph.clone(), + _marker: PhantomData, + } + } +} + +impl TxPool +where + A: ChainApi + 'static, + B: BlockT + Send + Sync + 'static, + C: Send + Sync + 'static, + C: HeaderBackend + ProvideRuntimeApi, + C::Api: EthereumRuntimeRPCApi, +{ + /// Use the transaction graph interface to get the extrinsics currently in the ready and future + /// queues. + fn map_build(&self) -> RpcResult>> + where + T: Get + Serialize, + { + // Get the pending and queued ethereum transactions. + let ethereum_txns = self.tx_pool_response()?; + + // Build the T response. + let mut pending = TransactionMap::::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_slice(keccak_256(&pk).as_slice())), + 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::::new(); + for txn in ethereum_txns.future.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_slice(keccak_256(&pk).as_slice())), + Err(_e) => H160::default(), + }; + queued + .entry(from_address) + .or_insert_with(HashMap::new) + .insert(nonce, T::get(hash, from_address, txn)); + } + Ok(TxPoolResult { pending, queued }) + } + + pub(crate) fn tx_pool_response(&self) -> RpcResult { + // Collect transactions in the ready validated pool. + let txs_ready = 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 + .graph + .validated_pool() + .futures() + .iter() + .map(|(_hash, 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)))?; + let future = api + .extrinsic_filter(best_block, txs_future) + .map_err(|err| internal_err(format!("fetch future transactions failed: {:?}", err)))?; + + Ok(TxPoolResponse { ready, future }) + } +} + +impl TxPool { + pub fn new(client: Arc, graph: Arc>) -> Self { + Self { + client, + graph, + _marker: PhantomData, + } + } +} + +impl TxPoolApiServer for TxPool +where + A: ChainApi + 'static, + B: BlockT + Send + Sync + 'static, + C: Send + Sync + 'static, + C: HeaderBackend + ProvideRuntimeApi, + C::Api: EthereumRuntimeRPCApi, +{ + fn content(&self) -> RpcResult>> { + self.map_build::() + } + + fn inspect(&self) -> RpcResult>> { + self.map_build::() + } + + fn status(&self) -> RpcResult> { + let status = self.graph.validated_pool().status(); + Ok(TxPoolResult { + pending: U256::from(status.ready), + queued: U256::from(status.future), + }) + } +} diff --git a/primitives/rpc/src/lib.rs b/primitives/rpc/src/lib.rs index f32b32408a..045402fdf5 100644 --- a/primitives/rpc/src/lib.rs +++ b/primitives/rpc/src/lib.rs @@ -40,6 +40,12 @@ pub struct TransactionStatus { pub logs_bloom: Bloom, } +#[derive(Eq, PartialEq, Clone, Encode, Decode, sp_runtime::RuntimeDebug)] +pub struct TxPoolResponse { + pub ready: Vec, + pub future: Vec, +} + pub trait RuntimeStorageOverride: Send + Sync { fn is_enabled() -> bool; diff --git a/template/node/src/eth.rs b/template/node/src/eth.rs index 8f28a4b637..765ba6c3ae 100644 --- a/template/node/src/eth.rs +++ b/template/node/src/eth.rs @@ -123,8 +123,8 @@ pub fn new_frontier_partial( /// A set of APIs that ethereum-compatible runtimes must implement. pub trait EthCompatRuntimeApiCollection: sp_api::ApiExt - + fp_rpc::EthereumRuntimeRPCApi + fp_rpc::ConvertTransactionRuntimeApi + + fp_rpc::EthereumRuntimeRPCApi where >::StateBackend: sp_api::StateBackend, { @@ -133,8 +133,8 @@ where impl EthCompatRuntimeApiCollection for Api where Api: sp_api::ApiExt - + fp_rpc::EthereumRuntimeRPCApi - + fp_rpc::ConvertTransactionRuntimeApi, + + fp_rpc::ConvertTransactionRuntimeApi + + fp_rpc::EthereumRuntimeRPCApi, >::StateBackend: sp_api::StateBackend, { } diff --git a/template/node/src/rpc/eth.rs b/template/node/src/rpc/eth.rs index 67db9a8161..2117cc6ba7 100644 --- a/template/node/src/rpc/eth.rs +++ b/template/node/src/rpc/eth.rs @@ -17,7 +17,7 @@ use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; use sp_core::H256; use sp_runtime::traits::Block as BlockT; // Frontier -pub use fc_rpc::{EthBlockDataCacheTask, EthConfig, OverrideHandle, StorageOverride}; +pub use fc_rpc::{EthBlockDataCacheTask, EthConfig, OverrideHandle, StorageOverride, TxPool}; pub use fc_rpc_core::types::{FeeHistoryCache, FeeHistoryCacheLimit, FilterPool}; pub use fc_storage::overrides_handle; use fp_rpc::{ConvertTransaction, ConvertTransactionRuntimeApi, EthereumRuntimeRPCApi}; @@ -99,7 +99,7 @@ pub fn create_eth>( where B: BlockT, C: CallApiAt + ProvideRuntimeApi, - C::Api: BlockBuilderApi + EthereumRuntimeRPCApi + ConvertTransactionRuntimeApi, + C::Api: BlockBuilderApi + ConvertTransactionRuntimeApi + EthereumRuntimeRPCApi, C: BlockchainEvents + 'static, C: HeaderBackend + HeaderMetadata + StorageProvider, BE: Backend + 'static, @@ -109,7 +109,7 @@ where { use fc_rpc::{ Eth, EthApiServer, EthDevSigner, EthFilter, EthFilterApiServer, EthPubSub, - EthPubSubApiServer, EthSigner, Net, NetApiServer, Web3, Web3ApiServer, + EthPubSubApiServer, EthSigner, Net, NetApiServer, TxPoolApiServer, Web3, Web3ApiServer, }; let EthDeps { @@ -141,7 +141,7 @@ where Eth::new( client.clone(), pool.clone(), - graph, + graph.clone(), converter, sync.clone(), signers, @@ -158,11 +158,13 @@ where .into_rpc(), )?; + let tx_pool = TxPool::new(client.clone(), graph); if let Some(filter_pool) = filter_pool { io.merge( EthFilter::new( client.clone(), frontier_backend, + tx_pool.clone(), filter_pool, 500_usize, // max stored filters max_past_logs, @@ -195,6 +197,7 @@ where )?; io.merge(Web3::new(client).into_rpc())?; + io.merge(tx_pool.into_rpc())?; Ok(io) } diff --git a/ts-tests/tests/test-filter-api.ts b/ts-tests/tests/test-filter-api.ts index f321a83621..f39387bd81 100644 --- a/ts-tests/tests/test-filter-api.ts +++ b/ts-tests/tests/test-filter-api.ts @@ -8,6 +8,7 @@ describeWithFrontier("Frontier RPC (EthFilterApi)", (context) => { const TEST_CONTRACT_BYTECODE = "0x608060405234801561001057600080fd5b50610041337fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61004660201b60201c565b610291565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156100e9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f45524332303a206d696e7420746f20746865207a65726f20616464726573730081525060200191505060405180910390fd5b6101028160025461020960201b610c7c1790919060201c565b60028190555061015d816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461020960201b610c7c1790919060201c565b6000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a35050565b600080828401905083811015610287576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b610e3a806102a06000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806370a082311161005b57806370a08231146101fd578063a457c2d714610255578063a9059cbb146102bb578063dd62ed3e1461032157610088565b8063095ea7b31461008d57806318160ddd146100f357806323b872dd146101115780633950935114610197575b600080fd5b6100d9600480360360408110156100a357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610399565b604051808215151515815260200191505060405180910390f35b6100fb6103b7565b6040518082815260200191505060405180910390f35b61017d6004803603606081101561012757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506103c1565b604051808215151515815260200191505060405180910390f35b6101e3600480360360408110156101ad57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061049a565b604051808215151515815260200191505060405180910390f35b61023f6004803603602081101561021357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061054d565b6040518082815260200191505060405180910390f35b6102a16004803603604081101561026b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610595565b604051808215151515815260200191505060405180910390f35b610307600480360360408110156102d157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610662565b604051808215151515815260200191505060405180910390f35b6103836004803603604081101561033757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610680565b6040518082815260200191505060405180910390f35b60006103ad6103a6610707565b848461070f565b6001905092915050565b6000600254905090565b60006103ce848484610906565b61048f846103da610707565b61048a85604051806060016040528060288152602001610d7060289139600160008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000610440610707565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610bbc9092919063ffffffff16565b61070f565b600190509392505050565b60006105436104a7610707565b8461053e85600160006104b8610707565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610c7c90919063ffffffff16565b61070f565b6001905092915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60006106586105a2610707565b8461065385604051806060016040528060258152602001610de160259139600160006105cc610707565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610bbc9092919063ffffffff16565b61070f565b6001905092915050565b600061067661066f610707565b8484610906565b6001905092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610795576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526024815260200180610dbd6024913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561081b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180610d286022913960400191505060405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040518082815260200191505060405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561098c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526025815260200180610d986025913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610a12576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180610d056023913960400191505060405180910390fd5b610a7d81604051806060016040528060268152602001610d4a602691396000808773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610bbc9092919063ffffffff16565b6000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610b10816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610c7c90919063ffffffff16565b6000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a3505050565b6000838311158290610c69576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610c2e578082015181840152602081019050610c13565b50505050905090810190601f168015610c5b5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b600080828401905083811015610cfa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b809150509291505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa265627a7a72315820c7a5ffabf642bda14700b2de42f8c57b36621af020441df825de45fd2b3e1c5c64736f6c63430005100032"; + var nonce = 0; async function sendTransaction(context) { const tx = await context.web3.eth.accounts.signTransaction( { @@ -16,10 +17,11 @@ describeWithFrontier("Frontier RPC (EthFilterApi)", (context) => { value: "0x00", gasPrice: "0x3B9ACA00", gas: "0x100000", + nonce: nonce, }, GENESIS_ACCOUNT_PRIVATE_KEY ); - + nonce = nonce + 1; await customRequest(context.web3, "eth_sendRawTransaction", [tx.rawTransaction]); return tx; } @@ -54,10 +56,8 @@ describeWithFrontier("Frontier RPC (EthFilterApi)", (context) => { }); step("should return unsupported error for Pending Transaction filter creation", async function () { - let r = await customRequest(context.web3, "eth_newPendingTransactionFilter", []); - expect(r.error).to.include({ - message: "Method not available.", - }); + let createFilter = await customRequest(context.web3, "eth_newPendingTransactionFilter", []); + expect(createFilter.result).to.be.eq("0x4"); }); step("should return responses for Block filter polling.", async function () { @@ -87,6 +87,31 @@ describeWithFrontier("Frontier RPC (EthFilterApi)", (context) => { expect(poll.result[1]).to.be.eq(block_b.hash); }); + step("should return responses for pending transaction polling.", async function () { + let poll = await customRequest(context.web3, "eth_getFilterChanges", ["0x4"]); + expect(poll.result.length).to.be.eq(0); + + // fist polling + let tx = await sendTransaction(context); + poll = await customRequest(context.web3, "eth_getFilterChanges", ["0x4"]); + expect(poll.result.length).to.be.eq(1); + expect(poll.result).contains(tx.transactionHash); + + // second polling + let tx1 = await sendTransaction(context); + let tx2 = await sendTransaction(context); + poll = await customRequest(context.web3, "eth_getFilterChanges", ["0x4"]); + expect(poll.result.length).to.be.eq(2); + expect(poll.result).contains(tx1.transactionHash); + expect(poll.result).contains(tx2.transactionHash); + + await createAndFinalizeBlock(context.web3); + + // the last polling after finalized block + poll = await customRequest(context.web3, "eth_getFilterChanges", ["0x4"]); + expect(poll.result.length).to.be.eq(0); + }); + step("should return responses for Log filter polling.", async function () { // Create contract. let tx = await sendTransaction(context); @@ -157,7 +182,7 @@ describeWithFrontier("Frontier RPC (EthFilterApi)", (context) => { // Should return error if does not exist. let r = await customRequest(context.web3, "eth_uninstallFilter", [filterId]); expect(r.error).to.include({ - message: "Filter id 6 does not exist.", + message: "Filter id 7 does not exist.", }); }); @@ -174,7 +199,7 @@ describeWithFrontier("Frontier RPC (EthFilterApi)", (context) => { let r = await customRequest(context.web3, "eth_getFilterChanges", [filterId]); expect(r.error).to.include({ - message: "Filter id 6 does not exist.", + message: "Filter id 7 does not exist.", }); }); diff --git a/ts-tests/tests/txpool.ts b/ts-tests/tests/txpool.ts new file mode 100644 index 0000000000..102368ec0a --- /dev/null +++ b/ts-tests/tests/txpool.ts @@ -0,0 +1,59 @@ +import { expect } from "chai"; +import { step } from "mocha-steps"; + +import { GENESIS_ACCOUNT, GENESIS_ACCOUNT_PRIVATE_KEY } from "./config"; +import { describeWithFrontier, customRequest } from "./util"; + +describeWithFrontier("Frontier RPC (TxPoolApi)", (context) => { + const TEST_CONTRACT_BYTECODE = + "0x608060405234801561001057600080fd5b50610041337fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61004660201b60201c565b610291565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156100e9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f45524332303a206d696e7420746f20746865207a65726f20616464726573730081525060200191505060405180910390fd5b6101028160025461020960201b610c7c1790919060201c565b60028190555061015d816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461020960201b610c7c1790919060201c565b6000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a35050565b600080828401905083811015610287576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b610e3a806102a06000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806370a082311161005b57806370a08231146101fd578063a457c2d714610255578063a9059cbb146102bb578063dd62ed3e1461032157610088565b8063095ea7b31461008d57806318160ddd146100f357806323b872dd146101115780633950935114610197575b600080fd5b6100d9600480360360408110156100a357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610399565b604051808215151515815260200191505060405180910390f35b6100fb6103b7565b6040518082815260200191505060405180910390f35b61017d6004803603606081101561012757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506103c1565b604051808215151515815260200191505060405180910390f35b6101e3600480360360408110156101ad57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061049a565b604051808215151515815260200191505060405180910390f35b61023f6004803603602081101561021357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061054d565b6040518082815260200191505060405180910390f35b6102a16004803603604081101561026b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610595565b604051808215151515815260200191505060405180910390f35b610307600480360360408110156102d157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610662565b604051808215151515815260200191505060405180910390f35b6103836004803603604081101561033757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610680565b6040518082815260200191505060405180910390f35b60006103ad6103a6610707565b848461070f565b6001905092915050565b6000600254905090565b60006103ce848484610906565b61048f846103da610707565b61048a85604051806060016040528060288152602001610d7060289139600160008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000610440610707565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610bbc9092919063ffffffff16565b61070f565b600190509392505050565b60006105436104a7610707565b8461053e85600160006104b8610707565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610c7c90919063ffffffff16565b61070f565b6001905092915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60006106586105a2610707565b8461065385604051806060016040528060258152602001610de160259139600160006105cc610707565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610bbc9092919063ffffffff16565b61070f565b6001905092915050565b600061067661066f610707565b8484610906565b6001905092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610795576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526024815260200180610dbd6024913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561081b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180610d286022913960400191505060405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040518082815260200191505060405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561098c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526025815260200180610d986025913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610a12576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180610d056023913960400191505060405180910390fd5b610a7d81604051806060016040528060268152602001610d4a602691396000808773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610bbc9092919063ffffffff16565b6000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610b10816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610c7c90919063ffffffff16565b6000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a3505050565b6000838311158290610c69576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610c2e578082015181840152602081019050610c13565b50505050905090810190601f168015610c5b5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b600080828401905083811015610cfa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b809150509291505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa265627a7a72315820c7a5ffabf642bda14700b2de42f8c57b36621af020441df825de45fd2b3e1c5c64736f6c63430005100032"; + + var nonce = 0; + let pending_tx; + let future_tx; + async function sendTransaction(context, nonce) { + const tx = await context.web3.eth.accounts.signTransaction( + { + from: GENESIS_ACCOUNT, + data: TEST_CONTRACT_BYTECODE, + value: "0x00", + gasPrice: "0x3B9ACA00", + gas: "0x100000", + nonce: nonce, + }, + GENESIS_ACCOUNT_PRIVATE_KEY + ); + await customRequest(context.web3, "eth_sendRawTransaction", [tx.rawTransaction]); + return tx; + } + + step("txpool_status should return correct result", async function () { + let txpoolStatus = await customRequest(context.web3, "txpool_status", []); + expect(txpoolStatus.result.pending).to.be.equal("0x0"); + expect(txpoolStatus.result.queued).to.be.equal("0x0"); + + pending_tx = await sendTransaction(context, nonce); + future_tx = await sendTransaction(context, nonce + 3); + txpoolStatus = await customRequest(context.web3, "txpool_status", []); + expect(txpoolStatus.result.pending).to.be.equal("0x1"); + expect(txpoolStatus.result.queued).to.be.equal("0x1"); + }); + + step("txpool_content should return correct result", async function () { + let txpoolContent = await customRequest(context.web3, "txpool_content", []); + expect(txpoolContent.result.pending[GENESIS_ACCOUNT]["0x0"].nonce).to.be.equal("0x0"); + expect(txpoolContent.result.pending[GENESIS_ACCOUNT]["0x0"].hash).to.be.equal(pending_tx.transactionHash); + expect(txpoolContent.result.queued[GENESIS_ACCOUNT]["0x3"].nonce).to.be.equal("0x3"); + expect(txpoolContent.result.queued[GENESIS_ACCOUNT]["0x3"].hash).to.be.equal(future_tx.transactionHash); + }); + + step("txpool_inspect should return correct result", async function () { + let txpoolInspect = await customRequest(context.web3, "txpool_inspect", []); + expect(txpoolInspect.result.pending[GENESIS_ACCOUNT]["0x0"]).to.be.equal( + "0x0000000000000000000000000000000000000000: 0 wei + 1048576 gas x 1000000000 wei" + ); + expect(txpoolInspect.result.queued[GENESIS_ACCOUNT]["0x3"]).to.be.equal( + "0x0000000000000000000000000000000000000000: 0 wei + 1048576 gas x 1000000000 wei" + ); + }); +});