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"
+ );
+ });
+});