sc-transcation-pool refactor (#9228)

* Use TransactionPool trait

* sc-transaction-pool-primitives

* sc-transaction-pool-api

* TP

* bye sc_transaction_graph

* fix line widths

* fix import errors

* fix import errors

* fix import errors 🤦🏾‍♂️

* fix import errors 🤦🏾‍♂️🤦🏾‍♂️🤦🏾‍♂️

* remove sp-keyring
This commit is contained in:
Seun Lanlege
2021-07-08 14:33:34 +01:00
committed by GitHub
parent 721a3b9e9c
commit 2ae9d36758
65 changed files with 384 additions and 388 deletions
+1 -1
View File
@@ -39,7 +39,7 @@ sp-runtime = { version = "3.0.0", default-features = false, path = "../../primit
sp-state-machine = { version = "0.9.0", path = "../../primitives/state-machine" }
sp-trie = { version = "3.0.0", path = "../../primitives/trie" }
sp-storage = { version = "3.0.0", path = "../../primitives/storage" }
sp-transaction-pool = { version = "3.0.0", path = "../../primitives/transaction-pool" }
sc-transaction-pool-api = { version = "3.0.0", path = "../transaction-pool/api" }
prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.9.0", path = "../../utils/prometheus" }
[dev-dependencies]
+3 -2
View File
@@ -31,6 +31,7 @@ use crate::blockchain::Info;
use crate::notifications::StorageEventStream;
use sp_utils::mpsc::TracingUnboundedReceiver;
use sp_blockchain;
use sc_transaction_pool_api::ChainEvent;
/// Type that implements `futures::Stream` of block import events.
pub type ImportNotifications<Block> = TracingUnboundedReceiver<BlockImportNotification<Block>>;
@@ -278,7 +279,7 @@ pub struct FinalityNotification<Block: BlockT> {
pub header: Block::Header,
}
impl<B: BlockT> TryFrom<BlockImportNotification<B>> for sp_transaction_pool::ChainEvent<B> {
impl<B: BlockT> TryFrom<BlockImportNotification<B>> for ChainEvent<B> {
type Error = ();
fn try_from(n: BlockImportNotification<B>) -> Result<Self, ()> {
@@ -293,7 +294,7 @@ impl<B: BlockT> TryFrom<BlockImportNotification<B>> for sp_transaction_pool::Cha
}
}
impl<B: BlockT> From<FinalityNotification<B>> for sp_transaction_pool::ChainEvent<B> {
impl<B: BlockT> From<FinalityNotification<B>> for ChainEvent<B> {
fn from(n: FinalityNotification<B>) -> Self {
Self::Finalized {
hash: n.hash,
@@ -37,6 +37,7 @@ use sp_state_machine::{ExecutionManager, DefaultHandler};
pub use sp_state_machine::ExecutionStrategy;
use sp_externalities::Extensions;
use parking_lot::RwLock;
use sc_transaction_pool_api::OffchainSubmitTransaction;
/// Execution strategies settings.
#[derive(Debug, Clone)]
@@ -104,7 +105,7 @@ pub struct ExecutionExtensions<Block: traits::Block> {
// extension to be a `Weak` reference.
// That's also the reason why it's being registered lazily instead of
// during initialization.
transaction_pool: RwLock<Option<Weak<dyn sp_transaction_pool::OffchainSubmitTransaction<Block>>>>,
transaction_pool: RwLock<Option<Weak<dyn OffchainSubmitTransaction<Block>>>>,
extensions_factory: RwLock<Box<dyn ExtensionsFactory>>,
}
@@ -150,7 +151,7 @@ impl<Block: traits::Block> ExecutionExtensions<Block> {
/// Register transaction pool extension.
pub fn register_transaction_pool<T>(&self, pool: &Arc<T>)
where T: sp_transaction_pool::OffchainSubmitTransaction<Block> + 'static
where T: OffchainSubmitTransaction<Block> + 'static
{
*self.transaction_pool.write() = Some(Arc::downgrade(&pool) as _);
}
@@ -235,7 +236,7 @@ impl<Block: traits::Block> ExecutionExtensions<Block> {
/// A wrapper type to pass `BlockId` to the actual transaction pool.
struct TransactionPoolAdapter<Block: traits::Block> {
at: BlockId<Block>,
pool: Arc<dyn sp_transaction_pool::OffchainSubmitTransaction<Block>>,
pool: Arc<dyn OffchainSubmitTransaction<Block>>,
}
impl<Block: traits::Block> offchain::TransactionPool for TransactionPoolAdapter<Block> {
+1 -1
View File
@@ -26,7 +26,7 @@ sc-client-api = { version = "3.0.0", path = "../api" }
sp-consensus = { version = "0.9.0", path = "../../primitives/consensus/common" }
sp-inherents = { version = "3.0.0", path = "../../primitives/inherents" }
sc-telemetry = { version = "3.0.0", path = "../telemetry" }
sp-transaction-pool = { version = "3.0.0", path = "../../primitives/transaction-pool" }
sc-transaction-pool-api = { version = "3.0.0", path = "../../client/transaction-pool/api" }
sc-block-builder = { version = "0.9.0", path = "../block-builder" }
sc-proposer-metrics = { version = "0.9.0", path = "../proposer-metrics" }
@@ -31,7 +31,7 @@ use sp_runtime::{
generic::BlockId,
traits::{Block as BlockT, Hash as HashT, Header as HeaderT, DigestFor, BlakeTwo256},
};
use sp_transaction_pool::{TransactionPool, InPoolTransaction};
use sc_transaction_pool_api::{TransactionPool, InPoolTransaction};
use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_INFO};
use sc_block_builder::{BlockBuilderApi, BlockBuilderProvider};
use sp_api::{ProvideRuntimeApi, ApiExt};
@@ -472,7 +472,7 @@ mod tests {
use substrate_test_runtime_client::{
prelude::*, TestClientBuilder, runtime::{Extrinsic, Transfer}, TestClientBuilderExt,
};
use sp_transaction_pool::{ChainEvent, MaintainedTransactionPool, TransactionSource};
use sc_transaction_pool_api::{ChainEvent, MaintainedTransactionPool, TransactionSource};
use sc_transaction_pool::BasicPool;
use sp_api::Core;
use sp_blockchain::HeaderBackend;
@@ -40,7 +40,7 @@ sp-core = { path = "../../../primitives/core", version = "3.0.0"}
sp-keystore = { path = "../../../primitives/keystore", version = "0.9.0"}
sp-keyring = { path = "../../../primitives/keyring", version = "3.0.0"}
sp-api = { path = "../../../primitives/api", version = "3.0.0"}
sp-transaction-pool = { path = "../../../primitives/transaction-pool", version = "3.0.0"}
sc-transaction-pool-api = { path = "../../../client/transaction-pool/api", version = "3.0.0"}
sp-timestamp = { path = "../../../primitives/timestamp", version = "3.0.0"}
prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus", version = "0.9.0"}
@@ -29,7 +29,6 @@ use sp_blockchain::HeaderBackend;
use sp_inherents::CreateInherentDataProviders;
use sp_runtime::{traits::Block as BlockT, Justifications, ConsensusEngineId};
use sc_client_api::backend::{Backend as ClientBackend, Finalizer};
use sc_transaction_pool::{ChainApi, Pool};
use std::{sync::Arc, marker::PhantomData};
use prometheus_endpoint::Registry;
@@ -48,6 +47,7 @@ pub use self::{
rpc::{EngineCommand, CreatedBlock},
};
use sp_api::{ProvideRuntimeApi, TransactionFor};
use sc_transaction_pool_api::TransactionPool;
/// The `ConsensusEngineId` of Manual Seal.
pub const MANUAL_SEAL_ENGINE_ID: ConsensusEngineId = [b'm', b'a', b'n', b'l'];
@@ -94,7 +94,7 @@ pub fn import_queue<Block, Transaction>(
}
/// Params required to start the instant sealing authorship task.
pub struct ManualSealParams<B: BlockT, BI, E, C: ProvideRuntimeApi<B>, A: ChainApi, SC, CS, CIDP> {
pub struct ManualSealParams<B: BlockT, BI, E, C: ProvideRuntimeApi<B>, TP, SC, CS, CIDP> {
/// Block import instance for well. importing blocks.
pub block_import: BI,
@@ -105,7 +105,7 @@ pub struct ManualSealParams<B: BlockT, BI, E, C: ProvideRuntimeApi<B>, A: ChainA
pub client: Arc<C>,
/// Shared reference to the transaction pool.
pub pool: Arc<Pool<A>>,
pub pool: Arc<TP>,
/// Stream<Item = EngineCommands>, Basically the receiving end of a channel for sending commands to
/// the authorship task.
@@ -122,7 +122,7 @@ pub struct ManualSealParams<B: BlockT, BI, E, C: ProvideRuntimeApi<B>, A: ChainA
}
/// Params required to start the manual sealing authorship task.
pub struct InstantSealParams<B: BlockT, BI, E, C: ProvideRuntimeApi<B>, A: ChainApi, SC, CIDP> {
pub struct InstantSealParams<B: BlockT, BI, E, C: ProvideRuntimeApi<B>, TP, SC, CIDP> {
/// Block import instance for well. importing blocks.
pub block_import: BI,
@@ -133,7 +133,7 @@ pub struct InstantSealParams<B: BlockT, BI, E, C: ProvideRuntimeApi<B>, A: Chain
pub client: Arc<C>,
/// Shared reference to the transaction pool.
pub pool: Arc<Pool<A>>,
pub pool: Arc<TP>,
/// SelectChain strategy.
pub select_chain: SC,
@@ -146,7 +146,7 @@ pub struct InstantSealParams<B: BlockT, BI, E, C: ProvideRuntimeApi<B>, A: Chain
}
/// Creates the background authorship task for the manual seal engine.
pub async fn run_manual_seal<B, BI, CB, E, C, A, SC, CS, CIDP>(
pub async fn run_manual_seal<B, BI, CB, E, C, TP, SC, CS, CIDP>(
ManualSealParams {
mut block_import,
mut env,
@@ -156,10 +156,9 @@ pub async fn run_manual_seal<B, BI, CB, E, C, A, SC, CS, CIDP>(
select_chain,
consensus_data_provider,
create_inherent_data_providers,
}: ManualSealParams<B, BI, E, C, A, SC, CS, CIDP>
}: ManualSealParams<B, BI, E, C, TP, SC, CS, CIDP>
)
where
A: ChainApi<Block=B> + 'static,
B: BlockT + 'static,
BI: BlockImport<B, Error = sp_consensus::Error, Transaction = sp_api::TransactionFor<C, B>>
+ Send + Sync + 'static,
@@ -170,6 +169,7 @@ pub async fn run_manual_seal<B, BI, CB, E, C, A, SC, CS, CIDP>(
CS: Stream<Item=EngineCommand<<B as BlockT>::Hash>> + Unpin + 'static,
SC: SelectChain<B> + 'static,
TransactionFor<C, B>: 'static,
TP: TransactionPool<Block = B>,
CIDP: CreateInherentDataProviders<B, ()>,
{
while let Some(command) = commands_stream.next().await {
@@ -215,7 +215,7 @@ pub async fn run_manual_seal<B, BI, CB, E, C, A, SC, CS, CIDP>(
/// runs the background authorship task for the instant seal engine.
/// instant-seal creates a new block for every transaction imported into
/// the transaction pool.
pub async fn run_instant_seal<B, BI, CB, E, C, A, SC, CIDP>(
pub async fn run_instant_seal<B, BI, CB, E, C, TP, SC, CIDP>(
InstantSealParams {
block_import,
env,
@@ -224,10 +224,9 @@ pub async fn run_instant_seal<B, BI, CB, E, C, A, SC, CIDP>(
select_chain,
consensus_data_provider,
create_inherent_data_providers,
}: InstantSealParams<B, BI, E, C, A, SC, CIDP>
}: InstantSealParams<B, BI, E, C, TP, SC, CIDP>
)
where
A: ChainApi<Block=B> + 'static,
B: BlockT + 'static,
BI: BlockImport<B, Error = sp_consensus::Error, Transaction = sp_api::TransactionFor<C, B>>
+ Send + Sync + 'static,
@@ -237,12 +236,12 @@ pub async fn run_instant_seal<B, BI, CB, E, C, A, SC, CIDP>(
E::Proposer: Proposer<B, Transaction = TransactionFor<C, B>>,
SC: SelectChain<B> + 'static,
TransactionFor<C, B>: 'static,
TP: TransactionPool<Block = B>,
CIDP: CreateInherentDataProviders<B, ()>,
{
// instant-seal creates blocks as soon as transactions are imported
// into the transaction pool.
let commands_stream = pool.validated_pool()
.import_notification_stream()
let commands_stream = pool.import_notification_stream()
.map(|_| {
EngineCommand::SealNewBlock {
create_empty: false,
@@ -277,7 +276,7 @@ mod tests {
};
use sc_transaction_pool::{BasicPool, RevalidationType, Options};
use substrate_test_runtime_transaction_pool::{TestApi, uxt};
use sp_transaction_pool::{TransactionPool, MaintainedTransactionPool, TransactionSource};
use sc_transaction_pool_api::{TransactionPool, MaintainedTransactionPool, TransactionSource};
use sp_runtime::generic::BlockId;
use sp_consensus::ImportedAux;
use sc_basic_authorship::ProposerFactory;
@@ -331,7 +330,7 @@ mod tests {
block_import: client.clone(),
env,
client: client.clone(),
pool: pool.pool().clone(),
pool: pool.clone(),
commands_stream,
select_chain,
create_inherent_data_providers: |_, _| async { Ok(()) },
@@ -395,7 +394,7 @@ mod tests {
block_import: client.clone(),
env,
client: client.clone(),
pool: pool.pool().clone(),
pool: pool.clone(),
commands_stream,
select_chain,
consensus_data_provider: None,
@@ -476,7 +475,7 @@ mod tests {
block_import: client.clone(),
env,
client: client.clone(),
pool: pool.pool().clone(),
pool: pool.clone(),
commands_stream,
select_chain,
consensus_data_provider: None,
@@ -522,7 +521,7 @@ mod tests {
assert!(pool.submit_one(&BlockId::Number(1), SOURCE, uxt(Alice, 1)).await.is_ok());
let header = client.header(&BlockId::Number(1)).expect("db error").expect("imported above");
pool.maintain(sp_transaction_pool::ChainEvent::NewBestBlock {
pool.maintain(sc_transaction_pool_api::ChainEvent::NewBestBlock {
hash: header.hash(),
tree_route: None,
}).await;
@@ -25,7 +25,6 @@ use sp_runtime::{
generic::BlockId,
};
use futures::prelude::*;
use sc_transaction_pool::{ChainApi, Pool};
use sp_consensus::{
self, BlockImport, Environment, Proposer, ForkChoiceStrategy,
BlockImportParams, BlockOrigin, ImportResult, SelectChain, StateAction,
@@ -35,12 +34,13 @@ use std::collections::HashMap;
use std::time::Duration;
use sp_inherents::{CreateInherentDataProviders, InherentDataProvider};
use sp_api::{ProvideRuntimeApi, TransactionFor};
use sc_transaction_pool_api::TransactionPool;
/// max duration for creating a proposal in secs
pub const MAX_PROPOSAL_DURATION: u64 = 10;
/// params for sealing a new block
pub struct SealBlockParams<'a, B: BlockT, BI, SC, C: ProvideRuntimeApi<B>, E, P: ChainApi, CIDP> {
pub struct SealBlockParams<'a, B: BlockT, BI, SC, C: ProvideRuntimeApi<B>, E, TP, CIDP> {
/// if true, empty blocks(without extrinsics) will be created.
/// otherwise, will return Error::EmptyTransactionPool.
pub create_empty: bool,
@@ -51,7 +51,7 @@ pub struct SealBlockParams<'a, B: BlockT, BI, SC, C: ProvideRuntimeApi<B>, E, P:
/// sender to report errors/success to the rpc.
pub sender: rpc::Sender<CreatedBlock<<B as BlockT>::Hash>>,
/// transaction pool
pub pool: Arc<Pool<P>>,
pub pool: Arc<TP>,
/// header backend
pub client: Arc<C>,
/// Environment trait object for creating a proposer
@@ -67,7 +67,7 @@ pub struct SealBlockParams<'a, B: BlockT, BI, SC, C: ProvideRuntimeApi<B>, E, P:
}
/// seals a new block with the given params
pub async fn seal_block<B, BI, SC, C, E, P, CIDP>(
pub async fn seal_block<B, BI, SC, C, E, TP, CIDP>(
SealBlockParams {
create_empty,
finalize,
@@ -80,7 +80,7 @@ pub async fn seal_block<B, BI, SC, C, E, P, CIDP>(
create_inherent_data_providers,
consensus_data_provider: digest_provider,
mut sender,
}: SealBlockParams<'_, B, BI, SC, C, E, P, CIDP>,
}: SealBlockParams<'_, B, BI, SC, C, E, TP, CIDP>,
) where
B: BlockT,
BI: BlockImport<B, Error = sp_consensus::Error, Transaction = sp_api::TransactionFor<C, B>>
@@ -90,13 +90,13 @@ pub async fn seal_block<B, BI, SC, C, E, P, CIDP>(
C: HeaderBackend<B> + ProvideRuntimeApi<B>,
E: Environment<B>,
E::Proposer: Proposer<B, Transaction = TransactionFor<C, B>>,
P: ChainApi<Block = B>,
TP: TransactionPool<Block = B>,
SC: SelectChain<B>,
TransactionFor<C, B>: 'static,
CIDP: CreateInherentDataProviders<B, ()>,
{
let future = async {
if pool.validated_pool().status().ready == 0 && !create_empty {
if pool.status().ready == 0 && !create_empty {
return Err(Error::EmptyTransactionPool);
}
+1 -1
View File
@@ -22,5 +22,5 @@ sc-client-api = { version = "3.0.0", path = "../api" }
sc-network = { version = "0.9.0", path = "../network" }
sp-blockchain = { version = "3.0.0", path = "../../primitives/blockchain" }
sp-runtime = { version = "3.0.0", path = "../../primitives/runtime" }
sp-transaction-pool = { version = "3.0.0", path = "../../primitives/transaction-pool" }
sc-transaction-pool-api = { version = "3.0.0", path = "../transaction-pool/api" }
wasm-timer = "0.2"
+1 -1
View File
@@ -27,7 +27,7 @@ use sc_client_api::{BlockchainEvents, UsageProvider};
use sc_network::NetworkService;
use sp_blockchain::HeaderMetadata;
use sp_runtime::traits::{Block as BlockT, Header};
use sp_transaction_pool::TransactionPool;
use sc_transaction_pool_api::TransactionPool;
use std::{fmt::Display, sync::Arc, time::Duration, collections::VecDeque};
mod display;
+1 -1
View File
@@ -41,7 +41,7 @@ hyper-rustls = "0.21.0"
sc-client-db = { version = "0.9.0", default-features = true, path = "../db" }
sc-block-builder = { version = "0.9.0", path = "../block-builder" }
sc-transaction-pool = { version = "3.0.0", path = "../transaction-pool" }
sp-transaction-pool = { version = "3.0.0", path = "../../primitives/transaction-pool" }
sc-transaction-pool-api = { version = "3.0.0", path = "../transaction-pool/api" }
sp-tracing = { version = "3.0.0", path = "../../primitives/tracing" }
sp-consensus = { version = "0.9.0", path = "../../primitives/consensus/common" }
substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" }
+3 -3
View File
@@ -236,7 +236,7 @@ mod tests {
DefaultTestClientBuilderExt, ClientBlockImportExt,
};
use sc_transaction_pool::{BasicPool, FullChainApi};
use sp_transaction_pool::{TransactionPool, InPoolTransaction};
use sc_transaction_pool_api::{TransactionPool, InPoolTransaction};
use sp_consensus::BlockOrigin;
use sc_client_api::Backend as _;
use sc_block_builder::BlockBuilderProvider as _;
@@ -268,13 +268,13 @@ mod tests {
Arc<BasicPool<FullChainApi<TestClient, Block>, Block>>
);
impl sp_transaction_pool::OffchainSubmitTransaction<Block> for TestPool {
impl sc_transaction_pool_api::OffchainSubmitTransaction<Block> for TestPool {
fn submit_at(
&self,
at: &BlockId<Block>,
extrinsic: <Block as traits::Block>::Extrinsic,
) -> Result<(), ()> {
let source = sp_transaction_pool::TransactionSource::Local;
let source = sc_transaction_pool_api::TransactionSource::Local;
futures::executor::block_on(self.0.submit_one(&at, source, extrinsic))
.map(|_| ())
.map_err(|_| ())
+1 -1
View File
@@ -28,6 +28,6 @@ sp-runtime = { path = "../../primitives/runtime" , version = "3.0.0"}
sc-chain-spec = { path = "../chain-spec" , version = "3.0.0"}
serde = { version = "1.0.101", features = ["derive"] }
serde_json = "1.0.41"
sp-transaction-pool = { version = "3.0.0", path = "../../primitives/transaction-pool" }
sc-transaction-pool-api = { version = "3.0.0", path = "../transaction-pool/api" }
sp-rpc = { version = "3.0.0", path = "../../primitives/rpc" }
sp-tracing = { version = "3.0.0", path = "../../primitives/tracing" }
+2 -2
View File
@@ -37,7 +37,7 @@ pub enum Error {
Client(Box<dyn std::error::Error + Send>),
/// Transaction pool error,
#[display(fmt="Transaction pool error: {}", _0)]
Pool(sp_transaction_pool::error::Error),
Pool(sc_transaction_pool_api::error::Error),
/// Verification error
#[display(fmt="Extrinsic verification error: {}", _0)]
#[from(ignore)]
@@ -105,7 +105,7 @@ const POOL_UNACTIONABLE: i64 = POOL_INVALID_TX + 8;
impl From<Error> for rpc::Error {
fn from(e: Error) -> Self {
use sp_transaction_pool::error::{Error as PoolError};
use sc_transaction_pool_api::error::{Error as PoolError};
match e {
Error::BadFormat(e) => rpc::Error {
+2 -2
View File
@@ -24,7 +24,7 @@ pub mod hash;
use jsonrpc_derive::rpc;
use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId};
use sp_core::Bytes;
use sp_transaction_pool::TransactionStatus;
use sc_transaction_pool_api::TransactionStatus;
use self::error::{FutureResult, Result};
pub use self::gen_client::Client as AuthorClient;
@@ -78,7 +78,7 @@ pub trait AuthorApi<Hash, BlockHash> {
/// Submit an extrinsic to watch.
///
/// See [`TransactionStatus`](sp_transaction_pool::TransactionStatus) for details on transaction
/// See [`TransactionStatus`](sc_transaction_pool_api::TransactionStatus) for details on transaction
/// life cycle.
#[pubsub(
subscription = "author_extrinsicUpdate",
+1 -1
View File
@@ -35,13 +35,13 @@ sc-chain-spec = { version = "3.0.0", path = "../chain-spec" }
sc-executor = { version = "0.9.0", path = "../executor" }
sc-block-builder = { version = "0.9.0", path = "../block-builder" }
sc-keystore = { version = "3.0.0", path = "../keystore" }
sp-transaction-pool = { version = "3.0.0", path = "../../primitives/transaction-pool" }
sp-blockchain = { version = "3.0.0", path = "../../primitives/blockchain" }
sc-tracing = { version = "3.0.0", path = "../tracing" }
hash-db = { version = "0.15.2", default-features = false }
parking_lot = "0.11.1"
lazy_static = { version = "1.4.0", optional = true }
sp-tracing = { version = "3.0.0", path = "../../primitives/tracing" }
sc-transaction-pool-api = { version = "3.0.0", path = "../transaction-pool/api" }
[dev-dependencies]
assert_matches = "1.3.0"
+1 -1
View File
@@ -36,7 +36,7 @@ use sp_core::Bytes;
use sp_keystore::{SyncCryptoStorePtr, SyncCryptoStore};
use sp_api::ProvideRuntimeApi;
use sp_runtime::generic;
use sp_transaction_pool::{
use sc_transaction_pool_api::{
TransactionPool, InPoolTransaction, TransactionStatus, TransactionSource,
BlockHash, TxHash, TransactionFor, error::IntoPoolError,
};
+1
View File
@@ -66,6 +66,7 @@ codec = { package = "parity-scale-codec", version = "2.0.0" }
sc-executor = { version = "0.9.0", path = "../executor" }
sc-transaction-pool = { version = "3.0.0", path = "../transaction-pool" }
sp-transaction-pool = { version = "3.0.0", path = "../../primitives/transaction-pool" }
sc-transaction-pool-api = { version = "3.0.0", path = "../transaction-pool/api" }
sp-transaction-storage-proof = { version = "3.0.0", path = "../../primitives/transaction-storage-proof" }
sc-rpc-server = { version = "3.0.0", path = "../rpc-servers" }
sc-rpc = { version = "3.0.0", path = "../rpc" }
+1 -1
View File
@@ -60,7 +60,7 @@ use sc_telemetry::{
TelemetryHandle,
SUBSTRATE_INFO,
};
use sp_transaction_pool::MaintainedTransactionPool;
use sc_transaction_pool_api::MaintainedTransactionPool;
use prometheus_endpoint::Registry;
use sc_client_db::{Backend, DatabaseSettings};
use sp_core::traits::{
+5 -5
View File
@@ -64,7 +64,7 @@ pub use sc_chain_spec::{
ChainSpec, GenericChainSpec, Properties, RuntimeGenesis, Extension as ChainSpecExtension,
NoExtension, ChainType,
};
pub use sp_transaction_pool::{TransactionPool, InPoolTransaction, error::IntoPoolError};
pub use sc_transaction_pool_api::{TransactionPool, InPoolTransaction, error::IntoPoolError};
pub use sc_transaction_pool::Options as TransactionPoolOptions;
pub use sc_rpc::Metadata as RpcMetadata;
pub use sc_executor::NativeExecutionDispatch;
@@ -456,7 +456,7 @@ where
Pool: TransactionPool<Block=B, Hash=H, Error=E>,
B: BlockT,
H: std::hash::Hash + Eq + sp_runtime::traits::Member + sp_runtime::traits::MaybeSerialize,
E: IntoPoolError + From<sp_transaction_pool::error::Error>,
E: IntoPoolError + From<sc_transaction_pool_api::error::Error>,
{
pool.ready()
.filter(|t| t.is_propagable())
@@ -475,7 +475,7 @@ where
Pool: 'static + TransactionPool<Block=B, Hash=H, Error=E>,
B: BlockT,
H: std::hash::Hash + Eq + sp_runtime::traits::Member + sp_runtime::traits::MaybeSerialize,
E: 'static + IntoPoolError + From<sp_transaction_pool::error::Error>,
E: 'static + IntoPoolError + From<sc_transaction_pool_api::error::Error>,
{
fn transactions(&self) -> Vec<(H, B::Extrinsic)> {
transactions_to_propagate(&*self.pool)
@@ -505,12 +505,12 @@ where
let best_block_id = BlockId::hash(self.client.info().best_hash);
let import_future = self.pool.submit_one(&best_block_id, sp_transaction_pool::TransactionSource::External, uxt);
let import_future = self.pool.submit_one(&best_block_id, sc_transaction_pool_api::TransactionSource::External, uxt);
Box::pin(async move {
match import_future.await {
Ok(_) => TransactionImport::NewGood,
Err(e) => match e.into_pool_error() {
Ok(sp_transaction_pool::error::Error::AlreadyImported(_)) => TransactionImport::KnownGood,
Ok(sc_transaction_pool_api::error::Error::AlreadyImported(_)) => TransactionImport::KnownGood,
Ok(e) => {
debug!("Error adding transaction to the pool: {:?}", e);
TransactionImport::Bad
+1 -1
View File
@@ -24,7 +24,7 @@ use prometheus_endpoint::{register, Gauge, U64, Registry, PrometheusError, Opts,
use sc_telemetry::{telemetry, TelemetryHandle, SUBSTRATE_INFO};
use sp_api::ProvideRuntimeApi;
use sp_runtime::traits::{NumberFor, Block, SaturatedConversion, UniqueSaturatedInto};
use sp_transaction_pool::{PoolStatus, MaintainedTransactionPool};
use sc_transaction_pool_api::{PoolStatus, MaintainedTransactionPool};
use sp_utils::metrics::register_globals;
use sc_client_api::{ClientInfo, UsageProvider};
use sc_network::{config::Role, NetworkStatus, NetworkService};
+1 -1
View File
@@ -33,7 +33,7 @@ sc-network = { version = "0.9.0", path = "../../network" }
sp-consensus = { version = "0.9.0", path = "../../../primitives/consensus/common" }
sp-runtime = { version = "3.0.0", path = "../../../primitives/runtime" }
sp-core = { version = "3.0.0", path = "../../../primitives/core" }
sp-transaction-pool = { version = "3.0.0", path = "../../../primitives/transaction-pool" }
sc-transaction-pool-api = { version = "3.0.0", path = "../../../client/transaction-pool/api" }
substrate-test-runtime = { version = "2.0.0", path = "../../../test-utils/runtime" }
substrate-test-runtime-client = { version = "2.0.0", path = "../../../test-utils/runtime/client" }
sc-client-api = { version = "3.0.0", path = "../../api" }
+2 -2
View File
@@ -47,7 +47,7 @@ use sp_blockchain::HeaderBackend;
use sc_network::{multiaddr, Multiaddr};
use sc_network::config::{NetworkConfiguration, TransportConfig};
use sp_runtime::{generic::BlockId, traits::Block as BlockT};
use sp_transaction_pool::TransactionPool;
use sc_transaction_pool_api::TransactionPool;
use sc_client_api::{Backend, CallExecutor};
use parking_lot::Mutex;
@@ -575,7 +575,7 @@ pub fn sync<G, E, Fb, F, Lb, L, B, ExF, U>(
let first_user_data = &network.full_nodes[0].2;
let best_block = BlockId::number(first_service.client().info().best_number);
let extrinsic = extrinsic_factory(&first_service, first_user_data);
let source = sp_transaction_pool::TransactionSource::External;
let source = sc_transaction_pool_api::TransactionSource::External;
futures::executor::block_on(
first_service.transaction_pool().submit_one(&best_block, source, extrinsic)
+15 -2
View File
@@ -22,21 +22,34 @@ parity-util-mem = { version = "0.10.0", default-features = false, features = ["p
parking_lot = "0.11.1"
prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus", version = "0.9.0"}
sc-client-api = { version = "3.0.0", path = "../api" }
sc-transaction-graph = { version = "3.0.0", path = "./graph" }
sp-api = { version = "3.0.0", path = "../../primitives/api" }
sp-core = { version = "3.0.0", path = "../../primitives/core" }
sp-runtime = { version = "3.0.0", path = "../../primitives/runtime" }
sp-tracing = { version = "3.0.0", path = "../../primitives/tracing" }
sp-transaction-pool = { version = "3.0.0", path = "../../primitives/transaction-pool" }
sc-transaction-pool-api = { version = "3.0.0", path = "./api" }
sp-blockchain = { version = "3.0.0", path = "../../primitives/blockchain" }
sp-utils = { version = "3.0.0", path = "../../primitives/utils" }
wasm-timer = "0.2"
derive_more = "0.99.2"
serde = { version = "1.0.101", features = ["derive"] }
linked-hash-map = "0.5.2"
retain_mut = "0.1.3"
[dev-dependencies]
assert_matches = "1.3.0"
hex = "0.4"
sp-keyring = { version = "3.0.0", path = "../../primitives/keyring" }
sp-consensus = { version = "0.9.0", path = "../../primitives/consensus/common" }
substrate-test-runtime-transaction-pool = { version = "2.0.0", path = "../../test-utils/runtime/transaction-pool" }
substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" }
sc-block-builder = { version = "0.9.0", path = "../block-builder" }
codec = { package = "parity-scale-codec", version = "2.0.0" }
substrate-test-runtime = { version = "2.0.0", path = "../../test-utils/runtime" }
criterion = "0.3"
[[bench]]
name = "basics"
harness = false
[features]
test-helpers = []
@@ -0,0 +1,20 @@
[package]
name = "sc-transaction-pool-api"
version = "3.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
homepage = "https://substrate.dev"
repository = "https://github.com/paritytech/substrate/"
description = "Transaction pool client facing API."
[dependencies]
futures = { version = "0.3.1" }
log = { version = "0.4.8" }
serde = { version = "1.0.101", features = ["derive"] }
thiserror = { version = "1.0.21" }
sp-runtime = { version = "3.0.0", default-features = false, path = "../../../primitives/runtime" }
codec = { package = "parity-scale-codec", version = "2.0.0" }
derive_more = { version = "0.99.11" }
sp-blockchain = { version = "3.0.0", path = "../../../primitives/blockchain" }
@@ -0,0 +1,86 @@
// This file is part of Substrate.
// Copyright (C) 2018-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Transaction pool errors.
use sp_runtime::transaction_validity::{
TransactionPriority as Priority, InvalidTransaction, UnknownTransaction,
};
/// Transaction pool result.
pub type Result<T> = std::result::Result<T, Error>;
/// Transaction pool error type.
#[derive(Debug, thiserror::Error, derive_more::From)]
#[allow(missing_docs)]
pub enum Error {
#[error("Unknown transaction validity: {0:?}")]
UnknownTransaction(UnknownTransaction),
#[error("Invalid transaction validity: {0:?}")]
InvalidTransaction(InvalidTransaction),
/// The transaction validity returned no "provides" tag.
///
/// Such transactions are not accepted to the pool, since we use those tags
/// to define identity of transactions (occupance of the same "slot").
#[error("Transaction does not provide any tags, so the pool can't identify it")]
NoTagsProvided,
#[error("Transaction temporarily Banned")]
TemporarilyBanned,
#[error("[{0:?}] Already imported")]
AlreadyImported(Box<dyn std::any::Any + Send>),
#[error("Too low priority ({} > {})", old, new)]
TooLowPriority {
/// Transaction already in the pool.
old: Priority,
/// Transaction entering the pool.
new: Priority
},
#[error("Transaction with cyclic dependency")]
CycleDetected,
#[error("Transaction couldn't enter the pool because of the limit")]
ImmediatelyDropped,
#[error("Transaction cannot be propagated and the local node does not author blocks")]
Unactionable,
#[from(ignore)]
#[error("{0}")]
InvalidBlockId(String),
#[error("The pool is not accepting future transactions")]
RejectedFutureTransaction,
}
/// Transaction pool error conversion.
pub trait IntoPoolError: std::error::Error + Send + Sized {
/// Try to extract original `Error`
///
/// This implementation is optional and used only to
/// provide more descriptive error messages for end users
/// of RPC API.
fn into_pool_error(self) -> std::result::Result<Error, Self> { Err(self) }
}
impl IntoPoolError for Error {
fn into_pool_error(self) -> std::result::Result<Error, Self> { Ok(self) }
}
@@ -0,0 +1,338 @@
// This file is part of Substrate.
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Transaction pool client facing API.
#![warn(missing_docs)]
pub mod error;
use std::{
collections::HashMap,
hash::Hash,
sync::Arc,
pin::Pin,
};
use futures::{Future, Stream};
use serde::{Deserialize, Serialize};
use sp_runtime::{
generic::BlockId,
traits::{Block as BlockT, Member, NumberFor},
};
pub use sp_runtime::transaction_validity::{
TransactionLongevity, TransactionPriority, TransactionTag, TransactionSource,
};
/// Transaction pool status.
#[derive(Debug)]
pub struct PoolStatus {
/// Number of transactions in the ready queue.
pub ready: usize,
/// Sum of bytes of ready transaction encodings.
pub ready_bytes: usize,
/// Number of transactions in the future queue.
pub future: usize,
/// Sum of bytes of ready transaction encodings.
pub future_bytes: usize,
}
impl PoolStatus {
/// Returns true if the are no transactions in the pool.
pub fn is_empty(&self) -> bool {
self.ready == 0 && self.future == 0
}
}
/// Possible transaction status events.
///
/// This events are being emitted by `TransactionPool` watchers,
/// which are also exposed over RPC.
///
/// The status events can be grouped based on their kinds as:
/// 1. Entering/Moving within the pool:
/// - `Future`
/// - `Ready`
/// 2. Inside `Ready` queue:
/// - `Broadcast`
/// 3. Leaving the pool:
/// - `InBlock`
/// - `Invalid`
/// - `Usurped`
/// - `Dropped`
/// 4. Re-entering the pool:
/// - `Retracted`
/// 5. Block finalized:
/// - `Finalized`
/// - `FinalityTimeout`
///
/// The events will always be received in the order described above, however
/// there might be cases where transactions alternate between `Future` and `Ready`
/// pool, and are `Broadcast` in the meantime.
///
/// There is also only single event causing the transaction to leave the pool.
/// I.e. only one of the listed ones should be triggered.
///
/// Note that there are conditions that may cause transactions to reappear in the pool.
/// 1. Due to possible forks, the transaction that ends up being in included
/// in one block, may later re-enter the pool or be marked as invalid.
/// 2. Transaction `Dropped` at one point, may later re-enter the pool if some other
/// transactions are removed.
/// 3. `Invalid` transaction may become valid at some point in the future.
/// (Note that runtimes are encouraged to use `UnknownValidity` to inform the pool about
/// such case).
/// 4. `Retracted` transactions might be included in some next block.
///
/// The stream is considered finished only when either `Finalized` or `FinalityTimeout`
/// event is triggered. You are however free to unsubscribe from notifications at any point.
/// The first one will be emitted when the block, in which transaction was included gets
/// finalized. The `FinalityTimeout` event will be emitted when the block did not reach finality
/// within 512 blocks. This either indicates that finality is not available for your chain,
/// or that finality gadget is lagging behind. If you choose to wait for finality longer, you can
/// re-subscribe for a particular transaction hash manually again.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum TransactionStatus<Hash, BlockHash> {
/// Transaction is part of the future queue.
Future,
/// Transaction is part of the ready queue.
Ready,
/// The transaction has been broadcast to the given peers.
Broadcast(Vec<String>),
/// Transaction has been included in block with given hash.
InBlock(BlockHash),
/// The block this transaction was included in has been retracted.
Retracted(BlockHash),
/// Maximum number of finality watchers has been reached,
/// old watchers are being removed.
FinalityTimeout(BlockHash),
/// Transaction has been finalized by a finality-gadget, e.g GRANDPA
Finalized(BlockHash),
/// Transaction has been replaced in the pool, by another transaction
/// that provides the same tags. (e.g. same (sender, nonce)).
Usurped(Hash),
/// Transaction has been dropped from the pool because of the limit.
Dropped,
/// Transaction is no longer valid in the current state.
Invalid,
}
/// The stream of transaction events.
pub type TransactionStatusStream<Hash, BlockHash> = dyn Stream<Item=TransactionStatus<Hash, BlockHash>> + Send + Unpin;
/// The import notification event stream.
pub type ImportNotificationStream<H> = futures::channel::mpsc::Receiver<H>;
/// Transaction hash type for a pool.
pub type TxHash<P> = <P as TransactionPool>::Hash;
/// Block hash type for a pool.
pub type BlockHash<P> = <<P as TransactionPool>::Block as BlockT>::Hash;
/// Transaction type for a pool.
pub type TransactionFor<P> = <<P as TransactionPool>::Block as BlockT>::Extrinsic;
/// Type of transactions event stream for a pool.
pub type TransactionStatusStreamFor<P> = TransactionStatusStream<TxHash<P>, BlockHash<P>>;
/// Transaction type for a local pool.
pub type LocalTransactionFor<P> = <<P as LocalTransactionPool>::Block as BlockT>::Extrinsic;
/// Typical future type used in transaction pool api.
pub type PoolFuture<T, E> = std::pin::Pin<Box<dyn Future<Output=Result<T, E>> + Send>>;
/// In-pool transaction interface.
///
/// The pool is container of transactions that are implementing this trait.
/// See `sp_runtime::ValidTransaction` for details about every field.
pub trait InPoolTransaction {
/// Transaction type.
type Transaction;
/// Transaction hash type.
type Hash;
/// Get the reference to the transaction data.
fn data(&self) -> &Self::Transaction;
/// Get hash of the transaction.
fn hash(&self) -> &Self::Hash;
/// Get priority of the transaction.
fn priority(&self) -> &TransactionPriority;
/// Get longevity of the transaction.
fn longevity(&self) -> &TransactionLongevity;
/// Get transaction dependencies.
fn requires(&self) -> &[TransactionTag];
/// Get tags that transaction provides.
fn provides(&self) -> &[TransactionTag];
/// Return a flag indicating if the transaction should be propagated to other peers.
fn is_propagable(&self) -> bool;
}
/// Transaction pool interface.
pub trait TransactionPool: Send + Sync {
/// Block type.
type Block: BlockT;
/// Transaction hash type.
type Hash: Hash + Eq + Member + Serialize;
/// In-pool transaction type.
type InPoolTransaction: InPoolTransaction<
Transaction = TransactionFor<Self>,
Hash = TxHash<Self>
>;
/// Error type.
type Error: From<crate::error::Error> + crate::error::IntoPoolError;
// *** RPC
/// Returns a future that imports a bunch of unverified transactions to the pool.
fn submit_at(
&self,
at: &BlockId<Self::Block>,
source: TransactionSource,
xts: Vec<TransactionFor<Self>>,
) -> PoolFuture<Vec<Result<TxHash<Self>, Self::Error>>, Self::Error>;
/// Returns a future that imports one unverified transaction to the pool.
fn submit_one(
&self,
at: &BlockId<Self::Block>,
source: TransactionSource,
xt: TransactionFor<Self>,
) -> PoolFuture<TxHash<Self>, Self::Error>;
/// Returns a future that import a single transaction and starts to watch their progress in the pool.
fn submit_and_watch(
&self,
at: &BlockId<Self::Block>,
source: TransactionSource,
xt: TransactionFor<Self>,
) -> PoolFuture<Box<TransactionStatusStreamFor<Self>>, Self::Error>;
// *** Block production / Networking
/// Get an iterator for ready transactions ordered by priority.
///
/// Guarantees to return only when transaction pool got updated at `at` block.
/// Guarantees to return immediately when `None` is passed.
fn ready_at(&self, at: NumberFor<Self::Block>)
-> Pin<Box<dyn Future<Output=Box<dyn Iterator<Item=Arc<Self::InPoolTransaction>> + Send>> + Send>>;
/// Get an iterator for ready transactions ordered by priority.
fn ready(&self) -> Box<dyn Iterator<Item=Arc<Self::InPoolTransaction>> + Send>;
// *** Block production
/// Remove transactions identified by given hashes (and dependent transactions) from the pool.
fn remove_invalid(&self, hashes: &[TxHash<Self>]) -> Vec<Arc<Self::InPoolTransaction>>;
// *** logging
/// Returns pool status.
fn status(&self) -> PoolStatus;
// *** logging / RPC / networking
/// Return an event stream of transactions imported to the pool.
fn import_notification_stream(&self) -> ImportNotificationStream<TxHash<Self>>;
// *** networking
/// Notify the pool about transactions broadcast.
fn on_broadcasted(&self, propagations: HashMap<TxHash<Self>, Vec<String>>);
/// Returns transaction hash
fn hash_of(&self, xt: &TransactionFor<Self>) -> TxHash<Self>;
/// Return specific ready transaction by hash, if there is one.
fn ready_transaction(&self, hash: &TxHash<Self>) -> Option<Arc<Self::InPoolTransaction>>;
}
/// Events that the transaction pool listens for.
pub enum ChainEvent<B: BlockT> {
/// New best block have been added to the chain
NewBestBlock {
/// Hash of the block.
hash: B::Hash,
/// Tree route from old best to new best parent that was calculated on import.
///
/// If `None`, no re-org happened on import.
tree_route: Option<Arc<sp_blockchain::TreeRoute<B>>>,
},
/// An existing block has been finalized.
Finalized {
/// Hash of just finalized block
hash: B::Hash,
},
}
/// Trait for transaction pool maintenance.
pub trait MaintainedTransactionPool: TransactionPool {
/// Perform maintenance
fn maintain(&self, event: ChainEvent<Self::Block>) -> Pin<Box<dyn Future<Output=()> + Send>>;
}
/// Transaction pool interface for submitting local transactions that exposes a
/// blocking interface for submission.
pub trait LocalTransactionPool: Send + Sync {
/// Block type.
type Block: BlockT;
/// Transaction hash type.
type Hash: Hash + Eq + Member + Serialize;
/// Error type.
type Error: From<crate::error::Error> + crate::error::IntoPoolError;
/// Submits the given local unverified transaction to the pool blocking the
/// current thread for any necessary pre-verification.
/// NOTE: It MUST NOT be used for transactions that originate from the
/// network or RPC, since the validation is performed with
/// `TransactionSource::Local`.
fn submit_local(
&self,
at: &BlockId<Self::Block>,
xt: LocalTransactionFor<Self>,
) -> Result<Self::Hash, Self::Error>;
}
/// An abstraction for transaction pool.
///
/// This trait is used by offchain calls to be able to submit transactions.
/// The main use case is for offchain workers, to feed back the results of computations,
/// but since the transaction pool access is a separate `ExternalitiesExtension` it can
/// be also used in context of other offchain calls. For one may generate and submit
/// a transaction for some misbehavior reports (say equivocation).
pub trait OffchainSubmitTransaction<Block: BlockT>: Send + Sync {
/// Submit transaction.
///
/// The transaction will end up in the pool and be propagated to others.
fn submit_at(
&self,
at: &BlockId<Block>,
extrinsic: Block::Extrinsic,
) -> Result<(), ()>;
}
impl<TPool: LocalTransactionPool> OffchainSubmitTransaction<TPool::Block> for TPool {
fn submit_at(
&self,
at: &BlockId<TPool::Block>,
extrinsic: <TPool::Block as BlockT>::Extrinsic,
) -> Result<(), ()> {
log::debug!(
target: "txpool",
"(offchain call) Submitting a transaction to the pool: {:?}",
extrinsic
);
let result = self.submit_local(&at, extrinsic);
result.map(|_| ()).map_err(|e| {
log::warn!(
target: "txpool",
"(offchain call) Error submitting a transaction to the pool: {:?}",
e
)
})
}
}
@@ -19,7 +19,7 @@
use criterion::{criterion_group, criterion_main, Criterion};
use futures::{future::{ready, Ready}, executor::block_on};
use sc_transaction_graph::*;
use sc_transaction_pool::{*, test_helpers::*};
use codec::Encode;
use substrate_test_runtime::{Block, Extrinsic, Transfer, H256, AccountId};
use sp_runtime::{
@@ -51,15 +51,15 @@ fn to_tag(nonce: u64, from: AccountId) -> Tag {
impl ChainApi for TestApi {
type Block = Block;
type Error = sp_transaction_pool::error::Error;
type ValidationFuture = Ready<sp_transaction_pool::error::Result<TransactionValidity>>;
type BodyFuture = Ready<sp_transaction_pool::error::Result<Option<Vec<Extrinsic>>>>;
type Error = sc_transaction_pool_api::error::Error;
type ValidationFuture = Ready<sc_transaction_pool_api::error::Result<TransactionValidity>>;
type BodyFuture = Ready<sc_transaction_pool_api::error::Result<Option<Vec<Extrinsic>>>>;
fn validate_transaction(
&self,
at: &BlockId<Self::Block>,
_source: TransactionSource,
uxt: ExtrinsicFor<Self>,
uxt: test_helpers::ExtrinsicFor<Self>,
) -> Self::ValidationFuture {
let nonce = uxt.transfer().nonce;
let from = uxt.transfer().from.clone();
@@ -89,7 +89,7 @@ impl ChainApi for TestApi {
fn block_id_to_number(
&self,
at: &BlockId<Self::Block>,
) -> Result<Option<NumberFor<Self>>, Self::Error> {
) -> Result<Option<test_helpers::NumberFor<Self>>, Self::Error> {
Ok(match at {
BlockId::Number(num) => Some(*num),
BlockId::Hash(_) => None,
@@ -99,14 +99,14 @@ impl ChainApi for TestApi {
fn block_id_to_hash(
&self,
at: &BlockId<Self::Block>,
) -> Result<Option<BlockHash<Self>>, Self::Error> {
) -> Result<Option<test_helpers::BlockHash<Self>>, Self::Error> {
Ok(match at {
BlockId::Number(num) => Some(H256::from_low_u64_be(*num)).into(),
BlockId::Hash(_) => None,
})
}
fn hash_and_length(&self, uxt: &ExtrinsicFor<Self>) -> (H256, usize) {
fn hash_and_length(&self, uxt: &test_helpers::ExtrinsicFor<Self>) -> (H256, usize) {
let encoded = uxt.encode();
(blake2_256(&encoded).into(), encoded.len())
}
@@ -1,8 +0,0 @@
Generic Transaction Pool
The pool is based on dependency graph between transactions
and their priority.
The pool is able to return an iterator that traverses transaction
graph in the correct order taking into account priorities and dependencies.
License: GPL-3.0-or-later WITH Classpath-exception-2.0
+15 -15
View File
@@ -37,7 +37,7 @@ use sp_api::{ProvideRuntimeApi, ApiExt};
use prometheus_endpoint::Registry as PrometheusRegistry;
use sp_core::traits::SpawnEssentialNamed;
use crate::{metrics::{ApiMetrics, ApiMetricsExt}, error::{self, Error}};
use crate::{metrics::{ApiMetrics, ApiMetricsExt}, error::{self, Error}, graph};
/// The transaction pool logic for full client.
pub struct FullChainApi<Client, Block> {
@@ -103,7 +103,7 @@ impl<Client, Block> FullChainApi<Client, Block> {
}
}
impl<Client, Block> sc_transaction_graph::ChainApi for FullChainApi<Client, Block>
impl<Client, Block> graph::ChainApi for FullChainApi<Client, Block>
where
Block: BlockT,
Client: ProvideRuntimeApi<Block> + BlockBackend<Block> + BlockIdTo<Block> + HeaderBackend<Block>,
@@ -125,7 +125,7 @@ where
&self,
at: &BlockId<Self::Block>,
source: TransactionSource,
uxt: sc_transaction_graph::ExtrinsicFor<Self>,
uxt: graph::ExtrinsicFor<Self>,
) -> Self::ValidationFuture {
let (tx, rx) = oneshot::channel();
let client = self.client.clone();
@@ -158,21 +158,21 @@ where
fn block_id_to_number(
&self,
at: &BlockId<Self::Block>,
) -> error::Result<Option<sc_transaction_graph::NumberFor<Self>>> {
) -> error::Result<Option<graph::NumberFor<Self>>> {
self.client.to_number(at).map_err(|e| Error::BlockIdConversion(format!("{:?}", e)))
}
fn block_id_to_hash(
&self,
at: &BlockId<Self::Block>,
) -> error::Result<Option<sc_transaction_graph::BlockHash<Self>>> {
) -> error::Result<Option<graph::BlockHash<Self>>> {
self.client.to_hash(at).map_err(|e| Error::BlockIdConversion(format!("{:?}", e)))
}
fn hash_and_length(
&self,
ex: &sc_transaction_graph::ExtrinsicFor<Self>,
) -> (sc_transaction_graph::ExtrinsicHash<Self>, usize) {
ex: &graph::ExtrinsicFor<Self>,
) -> (graph::ExtrinsicHash<Self>, usize) {
ex.using_encoded(|x| {
(<traits::HashFor::<Block> as traits::Hash>::hash(x), x.len())
})
@@ -192,7 +192,7 @@ fn validate_transaction_blocking<Client, Block>(
client: &Client,
at: &BlockId<Block>,
source: TransactionSource,
uxt: sc_transaction_graph::ExtrinsicFor<FullChainApi<Client, Block>>,
uxt: graph::ExtrinsicFor<FullChainApi<Client, Block>>,
) -> error::Result<TransactionValidity>
where
Block: BlockT,
@@ -269,7 +269,7 @@ where
&self,
at: &BlockId<Block>,
source: TransactionSource,
uxt: sc_transaction_graph::ExtrinsicFor<Self>,
uxt: graph::ExtrinsicFor<Self>,
) -> error::Result<TransactionValidity> {
validate_transaction_blocking(&*self.client, at, source, uxt)
}
@@ -293,7 +293,7 @@ impl<Client, F, Block> LightChainApi<Client, F, Block> {
}
}
impl<Client, F, Block> sc_transaction_graph::ChainApi for
impl<Client, F, Block> graph::ChainApi for
LightChainApi<Client, F, Block> where
Block: BlockT,
Client: HeaderBackend<Block> + 'static,
@@ -315,7 +315,7 @@ impl<Client, F, Block> sc_transaction_graph::ChainApi for
&self,
at: &BlockId<Self::Block>,
source: TransactionSource,
uxt: sc_transaction_graph::ExtrinsicFor<Self>,
uxt: graph::ExtrinsicFor<Self>,
) -> Self::ValidationFuture {
let header_hash = self.client.expect_block_hash_from_id(at);
let header_and_hash = header_hash
@@ -349,21 +349,21 @@ impl<Client, F, Block> sc_transaction_graph::ChainApi for
fn block_id_to_number(
&self,
at: &BlockId<Self::Block>,
) -> error::Result<Option<sc_transaction_graph::NumberFor<Self>>> {
) -> error::Result<Option<graph::NumberFor<Self>>> {
Ok(self.client.block_number_from_id(at)?)
}
fn block_id_to_hash(
&self,
at: &BlockId<Self::Block>,
) -> error::Result<Option<sc_transaction_graph::BlockHash<Self>>> {
) -> error::Result<Option<graph::BlockHash<Self>>> {
Ok(self.client.block_hash_from_id(at)?)
}
fn hash_and_length(
&self,
ex: &sc_transaction_graph::ExtrinsicFor<Self>,
) -> (sc_transaction_graph::ExtrinsicHash<Self>, usize) {
ex: &graph::ExtrinsicFor<Self>,
) -> (graph::ExtrinsicHash<Self>, usize) {
ex.using_encoded(|x| {
(<<Block::Header as HeaderT>::Hashing as HashT>::hash(x), x.len())
})
@@ -18,7 +18,7 @@
//! Transaction pool error.
use sp_transaction_pool::error::Error as TxPoolError;
use sc_transaction_pool_api::error::Error as TxPoolError;
/// Transaction pool result.
pub type Result<T> = std::result::Result<T, Error>;
@@ -41,7 +41,7 @@ pub enum Error {
}
impl sp_transaction_pool::error::IntoPoolError for Error {
impl sc_transaction_pool_api::error::IntoPoolError for Error {
fn into_pool_error(self) -> std::result::Result<TxPoolError, Self> {
match self {
Error::Pool(e) => Ok(e),
@@ -37,10 +37,12 @@ use sp_runtime::transaction_validity::{
TransactionPriority as Priority,
TransactionSource as Source,
};
use sp_transaction_pool::{error, PoolStatus, InPoolTransaction};
use sc_transaction_pool_api::{error, PoolStatus, InPoolTransaction};
use crate::future::{FutureTransactions, WaitingTransaction};
use crate::ready::ReadyTransactions;
use super::{
future::{FutureTransactions, WaitingTransaction},
ready::ReadyTransactions,
};
/// Successful import result.
#[derive(Debug, PartialEq, Eq)]
@@ -29,7 +29,7 @@ use sp_runtime::transaction_validity::{
};
use wasm_timer::Instant;
use crate::base_pool::Transaction;
use super::base_pool::Transaction;
#[cfg_attr(not(target_os = "unknown"), derive(parity_util_mem::MallocSizeOf))]
/// Transaction with partially satisfied dependencies.
@@ -26,7 +26,7 @@ use serde::Serialize;
use log::{debug, trace};
use sp_runtime::traits;
use crate::{watcher, ChainApi, ExtrinsicHash, BlockHash};
use super::{watcher, ChainApi, ExtrinsicHash, BlockHash};
/// Extrinsic pool default listener.
pub struct Listener<H: hash::Hash + Eq, C: ChainApi> {
@@ -38,7 +38,8 @@ pub mod base_pool;
pub mod watcher;
pub use self::base_pool::Transaction;
pub use validated_pool::{IsValidator, ValidatedTransaction};
pub use self::pool::{
BlockHash, ChainApi, EventStream, ExtrinsicFor, ExtrinsicHash, IsValidator, NumberFor, Options,
Pool, TransactionFor, ValidatedTransaction,
BlockHash, ChainApi, EventStream, ExtrinsicFor, ExtrinsicHash,
NumberFor, Options, Pool, TransactionFor,
};
@@ -29,13 +29,14 @@ use sp_runtime::{
TransactionValidity, TransactionTag as Tag, TransactionValidityError, TransactionSource,
},
};
use sp_transaction_pool::error;
use sc_transaction_pool_api::error;
use wasm_timer::Instant;
use futures::channel::mpsc::Receiver;
use crate::{base_pool as base, watcher::Watcher};
use crate::validated_pool::ValidatedPool;
pub use crate::validated_pool::{IsValidator, ValidatedTransaction};
use super::{
base_pool as base, watcher::Watcher,
validated_pool::{IsValidator, ValidatedTransaction, ValidatedPool},
};
/// Modification notification event stream type;
pub type EventStream<H> = Receiver<H>;
@@ -462,7 +463,7 @@ mod tests {
use parking_lot::Mutex;
use futures::executor::block_on;
use super::*;
use sp_transaction_pool::TransactionStatus;
use sc_transaction_pool_api::TransactionStatus;
use sp_runtime::{
traits::Hash,
transaction_validity::{ValidTransaction, InvalidTransaction, TransactionSource},
@@ -471,7 +472,7 @@ mod tests {
use substrate_test_runtime::{Block, Extrinsic, Transfer, H256, AccountId, Hashing};
use assert_matches::assert_matches;
use wasm_timer::Instant;
use crate::base_pool::Limit;
use super::super::base_pool::Limit;
const INVALID_NONCE: u64 = 254;
const SOURCE: TransactionSource = TransactionSource::External;
@@ -29,9 +29,9 @@ use sp_runtime::traits::Member;
use sp_runtime::transaction_validity::{
TransactionTag as Tag,
};
use sp_transaction_pool::error;
use sc_transaction_pool_api::error;
use crate::{
use super::{
base_pool::Transaction,
future::WaitingTransaction,
tracked_map::{self, ReadOnlyTrackedMap, TrackedMap},
@@ -149,7 +149,8 @@ impl<Hash: hash::Hash + Member + Serialize, Ex> ReadyTransactions<Hash, Ex> {
///
/// Transactions are returned in order:
/// 1. First by the dependencies:
/// - never return transaction that requires a tag, which was not provided by one of the previously returned transactions
/// - never return transaction that requires a tag, which was not provided by one of the previously
/// returned transactions
/// 2. Then by priority:
/// - If there are two transactions with all requirements satisfied the one with higher priority goes first.
/// 3. Then by the ttl that's left
@@ -30,7 +30,7 @@ use std::{
use parking_lot::RwLock;
use wasm_timer::Instant;
use crate::base_pool::Transaction;
use super::base_pool::Transaction;
/// Expected size of the banned extrinsics cache.
const EXPECTED_SIZE: usize = 2048;
@@ -29,17 +29,15 @@ use sp_runtime::{
traits::{self, SaturatedConversion},
transaction_validity::{TransactionTag as Tag, ValidTransaction, TransactionSource},
};
use sp_transaction_pool::{error, PoolStatus};
use sc_transaction_pool_api::{error, PoolStatus};
use wasm_timer::Instant;
use futures::channel::mpsc::{channel, Sender};
use retain_mut::RetainMut;
use crate::base_pool::{self as base, PruneStatus};
use crate::listener::Listener;
use crate::rotator::PoolRotator;
use crate::watcher::Watcher;
use crate::pool::{
EventStream, Options, ChainApi, BlockHash, ExtrinsicHash, ExtrinsicFor, TransactionFor,
use super::{
base_pool::{self as base, PruneStatus}, watcher::Watcher,
listener::Listener, rotator::PoolRotator,
pool::{EventStream, Options, ChainApi, BlockHash, ExtrinsicHash, ExtrinsicFor, TransactionFor},
};
/// Pre-validated transaction. Validated pool only accepts transactions wrapped in this enum.
@@ -211,7 +209,11 @@ impl<B: ChainApi> ValidatedPool<B> {
Ok(()) => true,
Err(e) => {
if e.is_full() {
log::warn!(target: "txpool", "[{:?}] Trying to notify an import but the channel is full", hash);
log::warn!(
target: "txpool",
"[{:?}] Trying to notify an import but the channel is full",
hash,
);
true
} else {
false
@@ -548,7 +550,7 @@ impl<B: ChainApi> ValidatedPool<B> {
}
/// Get rotator reference.
#[cfg(test)]
#[cfg(feature = "test-helpers")]
pub fn rotator(&self) -> &PoolRotator<ExtrinsicHash<B>> {
&self.rotator
}
@@ -19,7 +19,7 @@
//! Extrinsics status updates.
use futures::Stream;
use sp_transaction_pool::TransactionStatus;
use sc_transaction_pool_api::TransactionStatus;
use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedSender, TracingUnboundedReceiver};
/// Extrinsic watcher.
+42 -30
View File
@@ -23,17 +23,23 @@
#![warn(unused_extern_crates)]
mod api;
mod graph;
mod revalidation;
mod metrics;
pub mod error;
#[cfg(test)]
pub mod testing;
/// Common types for testing the transaction pool
#[cfg(feature = "test-helpers")]
pub mod test_helpers {
pub use super::{
graph::{ChainApi, Pool, NumberFor, BlockHash, ExtrinsicFor},
revalidation::RevalidationQueue,
};
}
pub use sc_transaction_graph::{ChainApi, Options, Pool};
pub use graph::{Options, Transaction};
pub use crate::api::{FullChainApi, LightChainApi};
use std::{collections::{HashMap, HashSet}, sync::Arc, pin::Pin, convert::TryInto};
use futures::{prelude::*, future::{self, ready}, channel::oneshot};
use parking_lot::Mutex;
@@ -43,23 +49,23 @@ use sp_runtime::{
traits::{Block as BlockT, NumberFor, AtLeast32Bit, Extrinsic, Zero, Header as HeaderT},
};
use sp_core::traits::SpawnEssentialNamed;
use sp_transaction_pool::{
use sc_transaction_pool_api::{
TransactionPool, PoolStatus, ImportNotificationStream, TxHash, TransactionFor,
TransactionStatusStreamFor, MaintainedTransactionPool, PoolFuture, ChainEvent,
TransactionSource,
};
use sc_transaction_graph::{IsValidator, ExtrinsicHash};
use graph::{IsValidator, ExtrinsicHash};
use wasm_timer::Instant;
use prometheus_endpoint::Registry as PrometheusRegistry;
use crate::metrics::MetricsLink as PrometheusMetrics;
type BoxedReadyIterator<Hash, Data> = Box<
dyn Iterator<Item=Arc<sc_transaction_graph::base_pool::Transaction<Hash, Data>>> + Send
dyn Iterator<Item=Arc<graph::base_pool::Transaction<Hash, Data>>> + Send
>;
type ReadyIteratorFor<PoolApi> = BoxedReadyIterator<
sc_transaction_graph::ExtrinsicHash<PoolApi>, sc_transaction_graph::ExtrinsicFor<PoolApi>
graph::ExtrinsicHash<PoolApi>, graph::ExtrinsicFor<PoolApi>
>;
type PolledIterator<PoolApi> = Pin<Box<dyn Future<Output=ReadyIteratorFor<PoolApi>> + Send>>;
@@ -73,9 +79,9 @@ pub type LightPool<Block, Client, Fetcher> = BasicPool<LightChainApi<Client, Fet
pub struct BasicPool<PoolApi, Block>
where
Block: BlockT,
PoolApi: ChainApi<Block=Block>,
PoolApi: graph::ChainApi<Block=Block>,
{
pool: Arc<sc_transaction_graph::Pool<PoolApi>>,
pool: Arc<graph::Pool<PoolApi>>,
api: Arc<PoolApi>,
revalidation_strategy: Arc<Mutex<RevalidationStrategy<NumberFor<Block>>>>,
revalidation_queue: Arc<revalidation::RevalidationQueue<PoolApi>>,
@@ -134,7 +140,7 @@ impl<T, Block: BlockT> ReadyPoll<T, Block> {
#[cfg(not(target_os = "unknown"))]
impl<PoolApi, Block> parity_util_mem::MallocSizeOf for BasicPool<PoolApi, Block>
where
PoolApi: ChainApi<Block=Block>,
PoolApi: graph::ChainApi<Block=Block>,
Block: BlockT,
{
fn size_of(&self, ops: &mut parity_util_mem::MallocSizeOfOps) -> usize {
@@ -163,14 +169,14 @@ pub enum RevalidationType {
impl<PoolApi, Block> BasicPool<PoolApi, Block>
where
Block: BlockT,
PoolApi: ChainApi<Block=Block> + 'static,
PoolApi: graph::ChainApi<Block=Block> + 'static,
{
/// Create new basic transaction pool with provided api, for tests.
#[cfg(test)]
#[cfg(feature = "test-helpers")]
pub fn new_test(
pool_api: Arc<PoolApi>,
) -> (Self, Pin<Box<dyn Future<Output=()> + Send>>, intervalier::BackSignalControl) {
let pool = Arc::new(sc_transaction_graph::Pool::new(Default::default(), true.into(), pool_api.clone()));
let pool = Arc::new(graph::Pool::new(Default::default(), true.into(), pool_api.clone()));
let (revalidation_queue, background_task, notifier) =
revalidation::RevalidationQueue::new_test(pool_api.clone(), pool.clone());
(
@@ -190,7 +196,7 @@ impl<PoolApi, Block> BasicPool<PoolApi, Block>
/// Create new basic transaction pool with provided api and custom
/// revalidation type.
pub fn with_revalidation_type(
options: sc_transaction_graph::Options,
options: graph::Options,
is_validator: IsValidator,
pool_api: Arc<PoolApi>,
prometheus: Option<&PrometheusRegistry>,
@@ -198,7 +204,7 @@ impl<PoolApi, Block> BasicPool<PoolApi, Block>
spawner: impl SpawnEssentialNamed,
best_block_number: NumberFor<Block>,
) -> Self {
let pool = Arc::new(sc_transaction_graph::Pool::new(options, is_validator, pool_api.clone()));
let pool = Arc::new(graph::Pool::new(options, is_validator, pool_api.clone()));
let (revalidation_queue, background_task) = match revalidation_type {
RevalidationType::Light => (
revalidation::RevalidationQueue::new(pool_api.clone(), pool.clone()),
@@ -233,19 +239,25 @@ impl<PoolApi, Block> BasicPool<PoolApi, Block>
}
/// Gets shared reference to the underlying pool.
pub fn pool(&self) -> &Arc<sc_transaction_graph::Pool<PoolApi>> {
pub fn pool(&self) -> &Arc<graph::Pool<PoolApi>> {
&self.pool
}
/// Get access to the underlying api
#[cfg(feature = "test-helpers")]
pub fn api(&self) -> &PoolApi {
&self.api
}
}
impl<PoolApi, Block> TransactionPool for BasicPool<PoolApi, Block>
where
Block: BlockT,
PoolApi: 'static + ChainApi<Block=Block>,
PoolApi: 'static + graph::ChainApi<Block=Block>,
{
type Block = PoolApi::Block;
type Hash = sc_transaction_graph::ExtrinsicHash<PoolApi>;
type InPoolTransaction = sc_transaction_graph::base_pool::Transaction<
type Hash = graph::ExtrinsicHash<PoolApi>;
type InPoolTransaction = graph::base_pool::Transaction<
TxHash<Self>, TransactionFor<Self>
>;
type Error = PoolApi::Error;
@@ -361,7 +373,7 @@ where
{
/// Create new basic transaction pool for a light node with the provided api.
pub fn new_light(
options: sc_transaction_graph::Options,
options: graph::Options,
prometheus: Option<&PrometheusRegistry>,
spawner: impl SpawnEssentialNamed,
client: Arc<Client>,
@@ -396,7 +408,7 @@ where
{
/// Create new basic transaction pool for a full node with the provided api.
pub fn new_full(
options: sc_transaction_graph::Options,
options: graph::Options,
is_validator: IsValidator,
prometheus: Option<&PrometheusRegistry>,
spawner: impl SpawnEssentialNamed,
@@ -420,7 +432,7 @@ where
}
}
impl<Block, Client> sp_transaction_pool::LocalTransactionPool
impl<Block, Client> sc_transaction_pool_api::LocalTransactionPool
for BasicPool<FullChainApi<Client, Block>, Block>
where
Block: BlockT,
@@ -432,15 +444,15 @@ where
Client::Api: sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block>,
{
type Block = Block;
type Hash = sc_transaction_graph::ExtrinsicHash<FullChainApi<Client, Block>>;
type Error = <FullChainApi<Client, Block> as ChainApi>::Error;
type Hash = graph::ExtrinsicHash<FullChainApi<Client, Block>>;
type Error = <FullChainApi<Client, Block> as graph::ChainApi>::Error;
fn submit_local(
&self,
at: &BlockId<Self::Block>,
xt: sp_transaction_pool::LocalTransactionFor<Self>,
xt: sc_transaction_pool_api::LocalTransactionFor<Self>,
) -> Result<Self::Hash, Self::Error> {
use sc_transaction_graph::ValidatedTransaction;
use graph::{ValidatedTransaction, ChainApi};
use sp_runtime::traits::SaturatedConversion;
use sp_runtime::transaction_validity::TransactionValidityError;
@@ -558,10 +570,10 @@ impl<N: Clone + Copy + AtLeast32Bit> RevalidationStatus<N> {
}
/// Prune the known txs for the given block.
async fn prune_known_txs_for_block<Block: BlockT, Api: ChainApi<Block = Block>>(
async fn prune_known_txs_for_block<Block: BlockT, Api: graph::ChainApi<Block = Block>>(
block_id: BlockId<Block>,
api: &Api,
pool: &sc_transaction_graph::Pool<Api>,
pool: &graph::Pool<Api>,
) -> Vec<ExtrinsicHash<Api>> {
let extrinsics = api.block_body(&block_id).await
.unwrap_or_else(|e| {
@@ -598,7 +610,7 @@ async fn prune_known_txs_for_block<Block: BlockT, Api: ChainApi<Block = Block>>(
impl<PoolApi, Block> MaintainedTransactionPool for BasicPool<PoolApi, Block>
where
Block: BlockT,
PoolApi: 'static + ChainApi<Block=Block>,
PoolApi: 'static + graph::ChainApi<Block=Block>,
{
fn maintain(&self, event: ChainEvent<Self::Block>) -> Pin<Box<dyn Future<Output=()> + Send>> {
match event {
@@ -20,7 +20,7 @@
use std::{sync::Arc, pin::Pin, collections::{HashMap, HashSet, BTreeMap}};
use sc_transaction_graph::{ChainApi, Pool, ExtrinsicHash, NumberFor, ValidatedTransaction};
use crate::graph::{ChainApi, Pool, ExtrinsicHash, NumberFor, ValidatedTransaction};
use sp_runtime::traits::{Zero, SaturatedConversion};
use sp_runtime::generic::BlockId;
use sp_runtime::transaction_validity::TransactionValidityError;
@@ -29,9 +29,9 @@ use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedSender, TracingUnbounded
use futures::prelude::*;
use std::time::Duration;
#[cfg(not(test))]
#[cfg(not(feature = "test-helpers"))]
const BACKGROUND_REVALIDATION_INTERVAL: Duration = Duration::from_millis(200);
#[cfg(test)]
#[cfg(feature = "test-helpers")]
pub const BACKGROUND_REVALIDATION_INTERVAL: Duration = Duration::from_millis(1);
const MIN_BACKGROUND_REVALIDATION_BATCH_SIZE: usize = 20;
@@ -225,7 +225,7 @@ impl<Api: ChainApi> RevalidationWorker<Api> {
batch_revalidate(this.pool.clone(), this.api.clone(), this.best_block, next_batch).await;
#[cfg(test)]
#[cfg(feature = "test-helpers")]
{
use intervalier::Guard;
// only trigger test events if something was processed
@@ -293,6 +293,7 @@ where
}
}
/// New revalidation queue with background worker.
pub fn new_with_interval<R: intervalier::IntoStream>(
api: Arc<Api>,
pool: Arc<Pool<Api>>,
@@ -320,7 +321,7 @@ where
}
/// New revalidation queue with background worker and test signal.
#[cfg(test)]
#[cfg(feature = "test-helpers")]
pub fn new_test(api: Arc<Api>, pool: Arc<Pool<Api>>) ->
(Self, Pin<Box<dyn Future<Output=()> + Send>>, intervalier::BackSignalControl)
{
@@ -361,35 +362,5 @@ where
#[cfg(test)]
mod tests {
use super::*;
use sc_transaction_graph::Pool;
use sp_transaction_pool::TransactionSource;
use substrate_test_runtime_transaction_pool::{TestApi, uxt};
use futures::executor::block_on;
use substrate_test_runtime_client::AccountKeyring::*;
fn setup() -> (Arc<TestApi>, Pool<TestApi>) {
let test_api = Arc::new(TestApi::empty());
let pool = Pool::new(Default::default(), true.into(), test_api.clone());
(test_api, pool)
}
#[test]
fn smoky() {
let (api, pool) = setup();
let pool = Arc::new(pool);
let queue = Arc::new(RevalidationQueue::new(api.clone(), pool.clone()));
let uxt = uxt(Alice, 0);
let uxt_hash = block_on(
pool.submit_one(&BlockId::number(0), TransactionSource::External, uxt.clone())
).expect("Should be valid");
block_on(queue.revalidate_later(0, vec![uxt_hash]));
// revalidated in sync offload 2nd time
assert_eq!(api.validation_requests().len(), 2);
// number of ready
assert_eq!(pool.validated_pool().status().ready, 1);
}
}
@@ -1,21 +0,0 @@
// This file is part of Substrate.
// Copyright (C) 2020-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// 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 <https://www.gnu.org/licenses/>.
//! Tests for top-level transaction pool api
mod pool;
@@ -16,11 +16,11 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::*;
use sp_transaction_pool::TransactionStatus;
//! Tests for top-level transaction pool api
use sc_transaction_pool_api::{TransactionStatus, ChainEvent, MaintainedTransactionPool, TransactionPool};
use futures::executor::{block_on, block_on_stream};
use sp_runtime::{
generic::BlockId,
generic::BlockId, traits::Block as _,
transaction_validity::{ValidTransaction, TransactionSource, InvalidTransaction},
};
use substrate_test_runtime_client::{
@@ -30,10 +30,11 @@ use substrate_test_runtime_client::{
use substrate_test_runtime_transaction_pool::{TestApi, uxt};
use futures::{prelude::*, task::Poll};
use codec::Encode;
use std::collections::BTreeSet;
use std::{collections::BTreeSet, sync::Arc, convert::TryInto};
use sc_client_api::client::BlockchainEvents;
use sc_block_builder::BlockBuilderProvider;
use sp_consensus::BlockOrigin;
use sc_transaction_pool::{*, test_helpers::*};
fn pool() -> Pool<TestApi> {
Pool::new(Default::default(), true.into(), TestApi::with_alice_nonce(209).into())
@@ -142,10 +143,10 @@ fn only_prune_on_new_best() {
let _ = block_on(
pool.submit_and_watch(&BlockId::number(0), SOURCE, uxt.clone())
).expect("1. Imported");
pool.api.push_block(1, vec![uxt.clone()], true);
pool.api().push_block(1, vec![uxt.clone()], true);
assert_eq!(pool.status().ready, 1);
let header = pool.api.push_block(2, vec![uxt], true);
let header = pool.api().push_block(2, vec![uxt], true);
let event = ChainEvent::NewBestBlock {
hash: header.hash(),
tree_route: None,
@@ -220,7 +221,7 @@ fn should_prune_old_during_maintenance() {
block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt.clone())).expect("1. Imported");
assert_eq!(pool.status().ready, 1);
let header = pool.api.push_block(1, vec![xt.clone()], true);
let header = pool.api().push_block(1, vec![xt.clone()], true);
block_on(pool.maintain(block_event(header)));
assert_eq!(pool.status().ready, 0);
@@ -235,16 +236,16 @@ fn should_revalidate_during_maintenance() {
block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt1.clone())).expect("1. Imported");
block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt2.clone())).expect("2. Imported");
assert_eq!(pool.status().ready, 2);
assert_eq!(pool.api.validation_requests().len(), 2);
assert_eq!(pool.api().validation_requests().len(), 2);
let header = pool.api.push_block(1, vec![xt1.clone()], true);
let header = pool.api().push_block(1, vec![xt1.clone()], true);
block_on(pool.maintain(block_event(header)));
assert_eq!(pool.status().ready, 1);
block_on(notifier.next());
// test that pool revalidated transaction that left ready and not included in the block
assert_eq!(pool.api.validation_requests().len(), 3);
assert_eq!(pool.api().validation_requests().len(), 3);
}
#[test]
@@ -256,10 +257,10 @@ fn should_resubmit_from_retracted_during_maintenance() {
block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt.clone())).expect("1. Imported");
assert_eq!(pool.status().ready, 1);
let header = pool.api.push_block(1, vec![], true);
let fork_header = pool.api.push_block(1, vec![], false);
let header = pool.api().push_block(1, vec![], true);
let fork_header = pool.api().push_block(1, vec![], false);
let event = block_event_with_retracted(header, fork_header.hash(), &*pool.api);
let event = block_event_with_retracted(header, fork_header.hash(), &*pool.api());
block_on(pool.maintain(event));
assert_eq!(pool.status().ready, 1);
@@ -275,10 +276,10 @@ fn should_not_resubmit_from_retracted_during_maintenance_if_tx_is_also_in_enacte
block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt.clone())).expect("1. Imported");
assert_eq!(pool.status().ready, 1);
let header = pool.api.push_block(1, vec![xt.clone()], true);
let fork_header = pool.api.push_block(1, vec![xt], false);
let header = pool.api().push_block(1, vec![xt.clone()], true);
let fork_header = pool.api().push_block(1, vec![xt], false);
let event = block_event_with_retracted(header, fork_header.hash(), &*pool.api);
let event = block_event_with_retracted(header, fork_header.hash(), &*pool.api());
block_on(pool.maintain(event));
assert_eq!(pool.status().ready, 0);
@@ -293,11 +294,11 @@ fn should_not_retain_invalid_hashes_from_retracted() {
block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt.clone())).expect("1. Imported");
assert_eq!(pool.status().ready, 1);
let header = pool.api.push_block(1, vec![], true);
let fork_header = pool.api.push_block(1, vec![xt.clone()], false);
pool.api.add_invalid(&xt);
let header = pool.api().push_block(1, vec![], true);
let fork_header = pool.api().push_block(1, vec![xt.clone()], false);
pool.api().add_invalid(&xt);
let event = block_event_with_retracted(header, fork_header.hash(), &*pool.api);
let event = block_event_with_retracted(header, fork_header.hash(), &*pool.api());
block_on(pool.maintain(event));
block_on(notifier.next());
@@ -317,20 +318,20 @@ fn should_revalidate_across_many_blocks() {
block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt2.clone())).expect("1. Imported");
assert_eq!(pool.status().ready, 2);
let header = pool.api.push_block(1, vec![], true);
let header = pool.api().push_block(1, vec![], true);
block_on(pool.maintain(block_event(header)));
block_on(notifier.next());
block_on(pool.submit_one(&BlockId::number(1), SOURCE, xt3.clone())).expect("1. Imported");
assert_eq!(pool.status().ready, 3);
let header = pool.api.push_block(2, vec![xt1.clone()], true);
let header = pool.api().push_block(2, vec![xt1.clone()], true);
block_on(pool.maintain(block_event(header)));
block_on(notifier.next());
assert_eq!(pool.status().ready, 2);
// xt1 and xt2 validated twice, then xt3 once, then xt2 and xt3 again
assert_eq!(pool.api.validation_requests().len(), 7);
assert_eq!(pool.api().validation_requests().len(), 7);
}
@@ -366,11 +367,11 @@ fn should_push_watchers_during_maintenance() {
assert_eq!(pool.status().ready, 5);
// when
pool.api.add_invalid(&tx3);
pool.api.add_invalid(&tx4);
pool.api().add_invalid(&tx3);
pool.api().add_invalid(&tx4);
// clear timer events if any
let header = pool.api.push_block(1, vec![], true);
let header = pool.api().push_block(1, vec![], true);
block_on(pool.maintain(block_event(header)));
block_on(notifier.next());
@@ -388,7 +389,7 @@ fn should_push_watchers_during_maintenance() {
);
// when
let header = pool.api.push_block(2, vec![tx0, tx1, tx2], true);
let header = pool.api().push_block(2, vec![tx0, tx1, tx2], true);
let header_hash = header.hash();
block_on(pool.maintain(block_event(header)));
@@ -442,9 +443,9 @@ fn finalization() {
let watcher = block_on(
pool.submit_and_watch(&BlockId::number(1), SOURCE, xt.clone())
).expect("1. Imported");
pool.api.push_block(2, vec![xt.clone()], true);
pool.api().push_block(2, vec![xt.clone()], true);
let header = pool.api.chain().read().block_by_number.get(&2).unwrap()[0].0.header().clone();
let header = pool.api().chain().read().block_by_number.get(&2).unwrap()[0].0.header().clone();
let event = ChainEvent::NewBestBlock {
hash: header.hash(),
tree_route: None,
@@ -474,10 +475,10 @@ fn fork_aware_finalization() {
let from_dave = uxt(Dave, 2);
let from_bob = uxt(Bob, 1);
let from_charlie = uxt(Charlie, 1);
pool.api.increment_nonce(Alice.into());
pool.api.increment_nonce(Dave.into());
pool.api.increment_nonce(Charlie.into());
pool.api.increment_nonce(Bob.into());
pool.api().increment_nonce(Alice.into());
pool.api().increment_nonce(Dave.into());
pool.api().increment_nonce(Charlie.into());
pool.api().increment_nonce(Bob.into());
let from_dave_watcher;
let from_bob_watcher;
@@ -491,7 +492,7 @@ fn fork_aware_finalization() {
let watcher = block_on(
pool.submit_and_watch(&BlockId::number(1), SOURCE, from_alice.clone())
).expect("1. Imported");
let header = pool.api.push_block(2, vec![from_alice.clone()], true);
let header = pool.api().push_block(2, vec![from_alice.clone()], true);
canon_watchers.push((watcher, header.hash()));
assert_eq!(pool.status().ready, 1);
@@ -508,7 +509,7 @@ fn fork_aware_finalization() {
// block C2
{
let header = pool.api.push_block_with_parent(b1, vec![from_dave.clone()], true);
let header = pool.api().push_block_with_parent(b1, vec![from_dave.clone()], true);
from_dave_watcher = block_on(
pool.submit_and_watch(&BlockId::number(1), SOURCE, from_dave.clone())
).expect("1. Imported");
@@ -528,7 +529,7 @@ fn fork_aware_finalization() {
pool.submit_and_watch(&BlockId::number(1), SOURCE, from_bob.clone())
).expect("1. Imported");
assert_eq!(pool.status().ready, 1);
let header = pool.api.push_block_with_parent(c2, vec![from_bob.clone()], true);
let header = pool.api().push_block_with_parent(c2, vec![from_bob.clone()], true);
let event = ChainEvent::NewBestBlock {
hash: header.hash(),
@@ -545,10 +546,10 @@ fn fork_aware_finalization() {
pool.submit_and_watch(&BlockId::number(1), SOURCE, from_charlie.clone())
).expect("1.Imported");
assert_eq!(pool.status().ready, 1);
let header = pool.api.push_block(3, vec![from_charlie.clone()], true);
let header = pool.api().push_block(3, vec![from_charlie.clone()], true);
canon_watchers.push((watcher, header.hash()));
let event = block_event_with_retracted(header.clone(), d2, &*pool.api);
let event = block_event_with_retracted(header.clone(), d2, &*pool.api());
block_on(pool.maintain(event));
assert_eq!(pool.status().ready, 2);
@@ -563,7 +564,7 @@ fn fork_aware_finalization() {
pool.submit_and_watch(&BlockId::number(1), SOURCE, xt.clone())
).expect("1. Imported");
assert_eq!(pool.status().ready, 3);
let header = pool.api.push_block(4, vec![xt.clone()], true);
let header = pool.api().push_block(4, vec![xt.clone()], true);
canon_watchers.push((w, header.hash()));
let event = ChainEvent::NewBestBlock {
@@ -581,7 +582,7 @@ fn fork_aware_finalization() {
// block e1
{
let header = pool.api.push_block(5, vec![from_dave, from_bob], true);
let header = pool.api().push_block(5, vec![from_dave, from_bob], true);
e1 = header.hash();
let event = ChainEvent::NewBestBlock {
hash: header.hash(),
@@ -636,7 +637,7 @@ fn prune_and_retract_tx_at_same_time() {
let (pool, _background, _) = BasicPool::new_test(api.into());
let from_alice = uxt(Alice, 1);
pool.api.increment_nonce(Alice.into());
pool.api().increment_nonce(Alice.into());
let watcher = block_on(
pool.submit_and_watch(&BlockId::number(1), SOURCE, from_alice.clone())
@@ -644,7 +645,7 @@ fn prune_and_retract_tx_at_same_time() {
// Block B1
let b1 = {
let header = pool.api.push_block(2, vec![from_alice.clone()], true);
let header = pool.api().push_block(2, vec![from_alice.clone()], true);
assert_eq!(pool.status().ready, 1);
let event = ChainEvent::NewBestBlock {
@@ -658,10 +659,10 @@ fn prune_and_retract_tx_at_same_time() {
// Block B2
let b2 = {
let header = pool.api.push_block(2, vec![from_alice.clone()], false);
let header = pool.api().push_block(2, vec![from_alice.clone()], false);
assert_eq!(pool.status().ready, 0);
let event = block_event_with_retracted(header.clone(), b1, &*pool.api);
let event = block_event_with_retracted(header.clone(), b1, &*pool.api());
block_on(pool.maintain(event));
assert_eq!(pool.status().ready, 0);
@@ -708,8 +709,8 @@ fn resubmit_tx_of_fork_that_is_not_part_of_retracted() {
let tx0 = uxt(Alice, 1);
let tx1 = uxt(Dave, 2);
pool.api.increment_nonce(Alice.into());
pool.api.increment_nonce(Dave.into());
pool.api().increment_nonce(Alice.into());
pool.api().increment_nonce(Dave.into());
let d0;
@@ -718,7 +719,7 @@ fn resubmit_tx_of_fork_that_is_not_part_of_retracted() {
let _ = block_on(
pool.submit_and_watch(&BlockId::number(1), SOURCE, tx0.clone())
).expect("1. Imported");
let header = pool.api.push_block(2, vec![tx0.clone()], true);
let header = pool.api().push_block(2, vec![tx0.clone()], true);
assert_eq!(pool.status().ready, 1);
let event = ChainEvent::NewBestBlock {
@@ -735,14 +736,14 @@ fn resubmit_tx_of_fork_that_is_not_part_of_retracted() {
let _ = block_on(
pool.submit_and_watch(&BlockId::number(1), SOURCE, tx1.clone())
).expect("1. Imported");
pool.api.push_block(2, vec![tx1.clone()], false);
pool.api().push_block(2, vec![tx1.clone()], false);
assert_eq!(pool.status().ready, 1);
}
// Block D2
{
let header = pool.api.push_block(2, vec![], false);
let event = block_event_with_retracted(header, d0, &*pool.api);
let header = pool.api().push_block(2, vec![], false);
let event = block_event_with_retracted(header, d0, &*pool.api());
block_on(pool.maintain(event));
assert_eq!(pool.status().ready, 2);
}
@@ -765,19 +766,19 @@ fn resubmit_from_retracted_fork() {
let tx4 = uxt(Ferdie, 2);
let tx5 = uxt(One, 3);
pool.api.increment_nonce(Alice.into());
pool.api.increment_nonce(Dave.into());
pool.api.increment_nonce(Bob.into());
pool.api.increment_nonce(Eve.into());
pool.api.increment_nonce(Ferdie.into());
pool.api.increment_nonce(One.into());
pool.api().increment_nonce(Alice.into());
pool.api().increment_nonce(Dave.into());
pool.api().increment_nonce(Bob.into());
pool.api().increment_nonce(Eve.into());
pool.api().increment_nonce(Ferdie.into());
pool.api().increment_nonce(One.into());
// Block D0
{
let _ = block_on(
pool.submit_and_watch(&BlockId::number(1), SOURCE, tx0.clone())
).expect("1. Imported");
let header = pool.api.push_block(2, vec![tx0.clone()], true);
let header = pool.api().push_block(2, vec![tx0.clone()], true);
assert_eq!(pool.status().ready, 1);
block_on(pool.maintain(block_event(header)));
@@ -789,7 +790,7 @@ fn resubmit_from_retracted_fork() {
let _ = block_on(
pool.submit_and_watch(&BlockId::number(1), SOURCE, tx1.clone())
).expect("1. Imported");
let header = pool.api.push_block(3, vec![tx1.clone()], true);
let header = pool.api().push_block(3, vec![tx1.clone()], true);
block_on(pool.maintain(block_event(header)));
assert_eq!(pool.status().ready, 0);
}
@@ -799,7 +800,7 @@ fn resubmit_from_retracted_fork() {
let _ = block_on(
pool.submit_and_watch(&BlockId::number(1), SOURCE, tx2.clone())
).expect("1. Imported");
let header = pool.api.push_block(4, vec![tx2.clone()], true);
let header = pool.api().push_block(4, vec![tx2.clone()], true);
block_on(pool.maintain(block_event(header.clone())));
assert_eq!(pool.status().ready, 0);
header.hash()
@@ -810,7 +811,7 @@ fn resubmit_from_retracted_fork() {
let _ = block_on(
pool.submit_and_watch(&BlockId::number(1), SOURCE, tx3.clone())
).expect("1. Imported");
let header = pool.api.push_block(2, vec![tx3.clone()], true);
let header = pool.api().push_block(2, vec![tx3.clone()], true);
assert_eq!(pool.status().ready, 1);
header.hash()
};
@@ -820,7 +821,7 @@ fn resubmit_from_retracted_fork() {
let _ = block_on(
pool.submit_and_watch(&BlockId::number(1), SOURCE, tx4.clone())
).expect("1. Imported");
let header = pool.api.push_block_with_parent(d1.clone(), vec![tx4.clone()], true);
let header = pool.api().push_block_with_parent(d1.clone(), vec![tx4.clone()], true);
assert_eq!(pool.status().ready, 2);
header.hash()
};
@@ -830,7 +831,7 @@ fn resubmit_from_retracted_fork() {
let _ = block_on(
pool.submit_and_watch(&BlockId::number(1), SOURCE, tx5.clone())
).expect("1. Imported");
let header = pool.api.push_block_with_parent(e1.clone(), vec![tx5.clone()], true);
let header = pool.api().push_block_with_parent(e1.clone(), vec![tx5.clone()], true);
// Don't announce the block event to the pool directly, because we will
// re-org to this block.
assert_eq!(pool.status().ready, 3);
@@ -841,7 +842,7 @@ fn resubmit_from_retracted_fork() {
let expected_ready = vec![tx3, tx4, tx5].iter().map(Encode::encode).collect::<BTreeSet<_>>();
assert_eq!(expected_ready, ready);
let event = block_event_with_retracted(f1_header, f0, &*pool.api);
let event = block_event_with_retracted(f1_header, f0, &*pool.api());
block_on(pool.maintain(event));
assert_eq!(pool.status().ready, 3);
@@ -862,7 +863,7 @@ fn ready_set_should_not_resolve_before_block_update() {
#[test]
fn ready_set_should_resolve_after_block_update() {
let (pool, _guard, _notifier) = maintained_pool();
let header = pool.api.push_block(1, vec![], true);
let header = pool.api().push_block(1, vec![], true);
let xt1 = uxt(Alice, 209);
@@ -875,7 +876,7 @@ fn ready_set_should_resolve_after_block_update() {
#[test]
fn ready_set_should_eventually_resolve_when_block_update_arrives() {
let (pool, _guard, _notifier) = maintained_pool();
let header = pool.api.push_block(1, vec![], true);
let header = pool.api().push_block(1, vec![], true);
let xt1 = uxt(Alice, 209);
@@ -926,7 +927,8 @@ fn should_not_accept_old_signatures() {
// generated with schnorrkel 0.1.1 from `_bytes`
let old_singature = sp_core::sr25519::Signature::try_from(&hex::decode(
"c427eb672e8c441c86d31f1a81b22b43102058e9ce237cabe9897ea5099ffd426cd1c6a1f4f2869c3df57901d36bedcb295657adb3a4355add86ed234eb83108"
"c427eb672e8c441c86d31f1a81b22b43102058e9ce237cabe9897ea5099ffd426\
cd1c6a1f4f2869c3df57901d36bedcb295657adb3a4355add86ed234eb83108"
).expect("hex invalid")[..]).expect("signature construction failed");
let xt = Extrinsic::Transfer {
@@ -938,7 +940,7 @@ fn should_not_accept_old_signatures() {
assert_matches::assert_matches!(
block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt.clone())),
Err(error::Error::Pool(
sp_transaction_pool::error::Error::InvalidTransaction(InvalidTransaction::BadProof)
sc_transaction_pool_api::error::Error::InvalidTransaction(InvalidTransaction::BadProof)
)),
"Should be invalid transaction with bad proof",
);
@@ -985,7 +987,7 @@ fn pruning_a_transaction_should_remove_it_from_best_transaction() {
block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt1.clone())).expect("1. Imported");
assert_eq!(pool.status().ready, 1);
let header = pool.api.push_block(1, vec![xt1.clone()], true);
let header = pool.api().push_block(1, vec![xt1.clone()], true);
// This will prune `xt1`.
block_on(pool.maintain(block_event(header)));
@@ -1002,10 +1004,10 @@ fn only_revalidate_on_best_block() {
block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt.clone())).expect("1. Imported");
assert_eq!(pool.status().ready, 1);
let header = pool.api.push_block(1, vec![], true);
let header = pool.api().push_block(1, vec![], true);
pool.api.push_block(2, vec![], false);
pool.api.push_block(2, vec![], false);
pool.api().push_block(2, vec![], false);
pool.api().push_block(2, vec![], false);
block_on(pool.maintain(block_event(header)));
block_on(notifier.next());
@@ -1073,7 +1075,7 @@ fn stale_transactions_are_pruned() {
];
// Import block
let header = pool.api.push_block(1, xts, true);
let header = pool.api().push_block(1, xts, true);
block_on(pool.maintain(block_event(header)));
// The imported transactions have a different hash and should not evict our initial
// transactions.
@@ -1081,7 +1083,7 @@ fn stale_transactions_are_pruned() {
// Import enough blocks to make our transactions stale
for n in 1..66 {
let header = pool.api.push_block(n, vec![], true);
let header = pool.api().push_block(n, vec![], true);
block_on(pool.maintain(block_event(header)));
}
@@ -0,0 +1,32 @@
use sc_transaction_pool::test_helpers::{Pool, RevalidationQueue};
use sc_transaction_pool_api::TransactionSource;
use substrate_test_runtime_transaction_pool::{TestApi, uxt};
use futures::executor::block_on;
use substrate_test_runtime_client::AccountKeyring::*;
use std::sync::Arc;
use sp_runtime::generic::BlockId;
fn setup() -> (Arc<TestApi>, Pool<TestApi>) {
let test_api = Arc::new(TestApi::empty());
let pool = Pool::new(Default::default(), true.into(), test_api.clone());
(test_api, pool)
}
#[test]
fn smoky() {
let (api, pool) = setup();
let pool = Arc::new(pool);
let queue = Arc::new(RevalidationQueue::new(api.clone(), pool.clone()));
let uxt = uxt(Alice, 0);
let uxt_hash = block_on(
pool.submit_one(&BlockId::number(0), TransactionSource::External, uxt.clone())
).expect("Should be valid");
block_on(queue.revalidate_later(0, vec![uxt_hash]));
// revalidated in sync offload 2nd time
assert_eq!(api.validation_requests().len(), 2);
// number of ready
assert_eq!(pool.validated_pool().status().ready, 1);
}