mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 01:21:07 +00:00
Recover transaction pool on light client (#3833)
* recover tx pool on light client * revert local tests fix * removed import renamings * futures03::Future -> std::future::Future * Update core/transaction-pool/graph/src/error.rs Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * replace remove_from_ready with remove_invalid * avoid excess hashing * debug -> warn * TransactionPool + BasicTransactionPool * pause future tx reject when resubmitting * bump impl_version to make CI happy * and revert back local test fixes * alter doc to restart CI * Transaction::clone() -> Transaction::duplicate() * transactions -> updated_tranasctions * remove explicit consensus-common ref * ::std:: -> std:: * manual set/unset flag -> calling clusore with given flag value * removed comments * removed force argument * BestIterator -> Box<Iterator> * separate crate for TxPool + Maintainer trait * long line fix * pos-merge fix * fix benches compilation * Rename txpoolapi to txpool_api * Clean up. * Finalize merge. * post-merge fix * Move transaction pool api to primitives directly. * Consistent naming for txpool-runtime-api * Warn about missing docs. * Move abstraction for offchain calls to tx-pool-api. * Merge RPC instantiation. * Update cargo.lock * Post merge fixes. * Avoid depending on client. * Fix build
This commit is contained in:
committed by
Gavin Wood
parent
3e26fceda4
commit
a782021ee8
@@ -2,14 +2,11 @@
|
||||
**/*.rs.bk
|
||||
*.swp
|
||||
.wasm-binaries
|
||||
polkadot/runtime/wasm/target/
|
||||
core/executor/wasm/target/
|
||||
core/test-runtime/wasm/target/
|
||||
pwasm-alloc/target/
|
||||
pwasm-libc/target/
|
||||
pwasm-alloc/Cargo.lock
|
||||
pwasm-libc/Cargo.lock
|
||||
node/runtime/wasm/target/
|
||||
bin/node/runtime/wasm/target/
|
||||
**/._*
|
||||
**/.criterion/
|
||||
.vscode
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
ROOT=`dirname "$0"`
|
||||
|
||||
# A list of directories which contain wasm projects.
|
||||
SRCS=(
|
||||
"core/executor/wasm"
|
||||
"node/runtime/wasm"
|
||||
"node-template/runtime/wasm"
|
||||
"core/test-runtime/wasm"
|
||||
)
|
||||
|
||||
# Make pushd/popd silent.
|
||||
|
||||
pushd () {
|
||||
command pushd "$@" > /dev/null
|
||||
}
|
||||
|
||||
popd () {
|
||||
command popd "$@" > /dev/null
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# This script assumes that all pre-requisites are installed.
|
||||
|
||||
set -e
|
||||
|
||||
PROJECT_ROOT=`git rev-parse --show-toplevel`
|
||||
source `dirname "$0"`/common.sh
|
||||
|
||||
export CARGO_INCREMENTAL=0
|
||||
|
||||
# Save current directory.
|
||||
pushd .
|
||||
|
||||
cd $ROOT
|
||||
|
||||
for SRC in "${SRCS[@]}"
|
||||
do
|
||||
echo "*** Updating and building wasm binaries in $SRC"
|
||||
cd "$PROJECT_ROOT/$SRC"
|
||||
|
||||
cargo update
|
||||
./build.sh "$@"
|
||||
|
||||
cd - >> /dev/null
|
||||
done
|
||||
|
||||
# Restore initial directory.
|
||||
popd
|
||||
Generated
+58
-17
@@ -915,6 +915,19 @@ name = "data-encoding"
|
||||
version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.2"
|
||||
@@ -3009,6 +3022,7 @@ dependencies = [
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sp-finality-tracker 2.0.0",
|
||||
"sp-timestamp 2.0.0",
|
||||
"sp-transaction-pool-api 2.0.0",
|
||||
"sr-io 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
"structopt 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -3093,7 +3107,7 @@ dependencies = [
|
||||
"node-runtime 2.0.0",
|
||||
"pallet-contracts-rpc 2.0.0",
|
||||
"pallet-transaction-payment-rpc 2.0.0",
|
||||
"sc-transaction-pool 2.0.0",
|
||||
"sp-transaction-pool-api 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
"substrate-client 2.0.0",
|
||||
"substrate-frame-rpc-system 2.0.0",
|
||||
@@ -3152,6 +3166,7 @@ dependencies = [
|
||||
"rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sp-transaction-pool-runtime-api 2.0.0",
|
||||
"sr-api 2.0.0",
|
||||
"sr-io 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
@@ -3166,7 +3181,6 @@ dependencies = [
|
||||
"substrate-offchain-primitives 2.0.0",
|
||||
"substrate-primitives 2.0.0",
|
||||
"substrate-session 2.0.0",
|
||||
"substrate-transaction-pool-runtime-api 2.0.0",
|
||||
"substrate-wasm-builder-runner 1.0.4",
|
||||
]
|
||||
|
||||
@@ -3182,6 +3196,7 @@ dependencies = [
|
||||
"parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sc-transaction-pool 2.0.0",
|
||||
"sp-transaction-pool-api 2.0.0",
|
||||
"sr-io 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
"substrate-basic-authorship 2.0.0",
|
||||
@@ -3220,6 +3235,7 @@ dependencies = [
|
||||
"parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sp-transaction-pool-runtime-api 2.0.0",
|
||||
"sr-api 2.0.0",
|
||||
"sr-io 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
@@ -3231,7 +3247,6 @@ dependencies = [
|
||||
"substrate-offchain-primitives 2.0.0",
|
||||
"substrate-primitives 2.0.0",
|
||||
"substrate-session 2.0.0",
|
||||
"substrate-transaction-pool-runtime-api 2.0.0",
|
||||
"substrate-wasm-builder-runner 1.0.4",
|
||||
]
|
||||
|
||||
@@ -4842,9 +4857,9 @@ dependencies = [
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sc-transaction-graph 2.0.0",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sp-transaction-pool-api 2.0.0",
|
||||
"sr-version 2.0.0",
|
||||
"substrate-primitives 2.0.0",
|
||||
"substrate-rpc-primitives 2.0.0",
|
||||
@@ -4863,6 +4878,7 @@ dependencies = [
|
||||
"parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sp-transaction-pool-api 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
"substrate-primitives 2.0.0",
|
||||
"substrate-test-runtime 2.0.0",
|
||||
@@ -4878,12 +4894,15 @@ dependencies = [
|
||||
"parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sc-transaction-graph 2.0.0",
|
||||
"sp-blockchain 2.0.0",
|
||||
"sp-transaction-pool-api 2.0.0",
|
||||
"sp-transaction-pool-runtime-api 2.0.0",
|
||||
"sr-api 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
"substrate-client-api 2.0.0",
|
||||
"substrate-keyring 2.0.0",
|
||||
"substrate-primitives 2.0.0",
|
||||
"substrate-test-runtime-client 2.0.0",
|
||||
"substrate-transaction-pool-runtime-api 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5225,6 +5244,28 @@ dependencies = [
|
||||
"substrate-inherents 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-transaction-pool-api"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sr-primitives 2.0.0",
|
||||
"substrate-primitives 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-transaction-pool-runtime-api"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"sr-api 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
"substrate-primitives 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
@@ -5524,6 +5565,7 @@ dependencies = [
|
||||
"parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sc-transaction-pool 2.0.0",
|
||||
"sp-blockchain 2.0.0",
|
||||
"sp-transaction-pool-api 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
"substrate-block-builder 2.0.0",
|
||||
"substrate-client 2.0.0",
|
||||
@@ -5688,6 +5730,7 @@ dependencies = [
|
||||
"parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sp-blockchain 2.0.0",
|
||||
"sp-transaction-pool-api 2.0.0",
|
||||
"sr-api 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
"sr-std 2.0.0",
|
||||
@@ -6067,11 +6110,12 @@ dependencies = [
|
||||
"jsonrpc-derive 14.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sc-transaction-graph 2.0.0",
|
||||
"sc-transaction-pool 2.0.0",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sp-blockchain 2.0.0",
|
||||
"sp-transaction-pool-api 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
"substrate-client 2.0.0",
|
||||
"substrate-primitives 2.0.0",
|
||||
"substrate-test-runtime-client 2.0.0",
|
||||
]
|
||||
@@ -6182,6 +6226,7 @@ dependencies = [
|
||||
"parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sc-transaction-pool 2.0.0",
|
||||
"sp-transaction-pool-api 2.0.0",
|
||||
"sr-api 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
"substrate-client-api 2.0.0",
|
||||
@@ -6305,6 +6350,7 @@ dependencies = [
|
||||
"sc-transaction-pool 2.0.0",
|
||||
"serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sp-blockchain 2.0.0",
|
||||
"sp-transaction-pool-api 2.0.0",
|
||||
"sr-api 2.0.0",
|
||||
"sr-io 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
@@ -6430,6 +6476,8 @@ dependencies = [
|
||||
"serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"slog 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sp-blockchain 2.0.0",
|
||||
"sp-transaction-pool-api 2.0.0",
|
||||
"sp-transaction-pool-runtime-api 2.0.0",
|
||||
"sr-api 2.0.0",
|
||||
"sr-io 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
@@ -6453,7 +6501,6 @@ dependencies = [
|
||||
"substrate-telemetry 2.0.0",
|
||||
"substrate-test-runtime-client 2.0.0",
|
||||
"substrate-tracing 2.0.0",
|
||||
"substrate-transaction-pool-runtime-api 2.0.0",
|
||||
"sysinfo 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -6471,6 +6518,7 @@ dependencies = [
|
||||
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sp-transaction-pool-api 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
"substrate-client 2.0.0",
|
||||
"substrate-consensus-common 2.0.0",
|
||||
@@ -6586,6 +6634,7 @@ dependencies = [
|
||||
"pallet-timestamp 2.0.0",
|
||||
"parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sp-transaction-pool-runtime-api 2.0.0",
|
||||
"sr-api 2.0.0",
|
||||
"sr-io 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
@@ -6605,7 +6654,6 @@ dependencies = [
|
||||
"substrate-session 2.0.0",
|
||||
"substrate-state-machine 2.0.0",
|
||||
"substrate-test-runtime-client 2.0.0",
|
||||
"substrate-transaction-pool-runtime-api 2.0.0",
|
||||
"substrate-trie 2.0.0",
|
||||
"substrate-wasm-builder-runner 1.0.4",
|
||||
"trie-db 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -6615,6 +6663,7 @@ dependencies = [
|
||||
name = "substrate-test-runtime-client"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sp-blockchain 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
@@ -6642,15 +6691,6 @@ dependencies = [
|
||||
"tracing-core 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-transaction-pool-runtime-api"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"sr-api 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
"substrate-primitives 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-trie"
|
||||
version = "2.0.0"
|
||||
@@ -8044,6 +8084,7 @@ dependencies = [
|
||||
"checksum cuckoofilter 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8dd43f7cfaffe0a386636a10baea2ee05cc50df3b77bea4a456c9572a939bf1f"
|
||||
"checksum curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8b7dcd30ba50cdf88b55b033456138b7c0ac4afdc436d82e1b79f370f24cc66d"
|
||||
"checksum data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4f47ca1860a761136924ddd2422ba77b2ea54fe8cc75b9040804a0d9d32ad97"
|
||||
"checksum derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a141330240c921ec6d074a3e188a7c7ef95668bb95e7d44fa0e5778ec2a7afe"
|
||||
"checksum derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2159be042979966de68315bce7034bb000c775f22e3e834e1c52ff78f041cae8"
|
||||
"checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
|
||||
"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
|
||||
|
||||
@@ -126,6 +126,7 @@ members = [
|
||||
"primitives/sr-version",
|
||||
"primitives/state-machine",
|
||||
"primitives/timestamp",
|
||||
"primitives/transaction-pool",
|
||||
"primitives/transaction-pool/runtime-api",
|
||||
"primitives/trie",
|
||||
"primitives/wasm-interface",
|
||||
|
||||
@@ -24,7 +24,8 @@ primitives = { package = "substrate-primitives", path = "../../primitives/core"
|
||||
substrate-executor = { path = "../../client/executor" }
|
||||
substrate-service = { path = "../../client/service" }
|
||||
inherents = { package = "substrate-inherents", path = "../../primitives/inherents" }
|
||||
transaction-pool = { package = "sc-transaction-pool", path = "../../client/transaction-pool" }
|
||||
txpool = { package = "sc-transaction-pool", path = "../../client/transaction-pool" }
|
||||
txpool-api = { package = "sp-transaction-pool-api", path = "../../primitives/transaction-pool" }
|
||||
network = { package = "substrate-network", path = "../../client/network" }
|
||||
aura = { package = "substrate-consensus-aura", path = "../../client/consensus/aura" }
|
||||
aura-primitives = { package = "substrate-consensus-aura-primitives", path = "../../primitives/consensus/aura" }
|
||||
|
||||
@@ -29,7 +29,7 @@ support = { package = "frame-support", path = "../../../frame/support", default-
|
||||
system = { package = "frame-system", path = "../../../frame/system", default-features = false }
|
||||
timestamp = { package = "pallet-timestamp", path = "../../../frame/timestamp", default-features = false }
|
||||
transaction-payment = { package = "pallet-transaction-payment", path = "../../../frame/transaction-payment", default-features = false }
|
||||
tx-pool-api = { package = "substrate-transaction-pool-runtime-api", path = "../../../primitives/transaction-pool/runtime-api", default-features = false }
|
||||
txpool-runtime-api = { package = "sp-transaction-pool-runtime-api", path = "../../../primitives/transaction-pool/runtime-api", default-features = false }
|
||||
version = { package = "sr-version", path = "../../../primitives/sr-version", default-features = false }
|
||||
|
||||
[build-dependencies]
|
||||
@@ -62,6 +62,6 @@ std = [
|
||||
"system/std",
|
||||
"timestamp/std",
|
||||
"transaction-payment/std",
|
||||
"tx-pool-api/std",
|
||||
"txpool-runtime-api/std",
|
||||
"version/std",
|
||||
]
|
||||
|
||||
@@ -328,7 +328,7 @@ impl_runtime_apis! {
|
||||
}
|
||||
}
|
||||
|
||||
impl tx_pool_api::TaggedTransactionQueue<Block> for Runtime {
|
||||
impl txpool_runtime_api::TaggedTransactionQueue<Block> for Runtime {
|
||||
fn validate_transaction(tx: <Block as BlockT>::Extrinsic) -> TransactionValidity {
|
||||
Executive::validate_transaction(tx)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ use std::time::Duration;
|
||||
use substrate_client::LongestChain;
|
||||
use runtime::{self, GenesisConfig, opaque::Block, RuntimeApi};
|
||||
use substrate_service::{error::{Error as ServiceError}, AbstractService, Configuration, ServiceBuilder};
|
||||
use transaction_pool::{self, txpool::{Pool as TransactionPool}};
|
||||
use inherents::InherentDataProviders;
|
||||
use network::{construct_simple_protocol};
|
||||
use substrate_executor::native_executor_instance;
|
||||
@@ -41,9 +40,13 @@ macro_rules! new_full_start {
|
||||
.with_select_chain(|_config, backend| {
|
||||
Ok(substrate_client::LongestChain::new(backend.clone()))
|
||||
})?
|
||||
.with_transaction_pool(|config, client|
|
||||
Ok(transaction_pool::txpool::Pool::new(config, transaction_pool::FullChainApi::new(client)))
|
||||
)?
|
||||
.with_transaction_pool(|config, client, _fetcher| {
|
||||
let pool_api = txpool::FullChainApi::new(client.clone());
|
||||
let pool = txpool::BasicPool::new(config, pool_api);
|
||||
let maintainer = txpool::FullBasicPoolMaintainer::new(pool.pool().clone(), client);
|
||||
let maintainable_pool = txpool_api::MaintainableTransactionPool::new(pool, maintainer);
|
||||
Ok(maintainable_pool)
|
||||
})?
|
||||
.with_import_queue(|_config, client, mut select_chain, transaction_pool| {
|
||||
let select_chain = select_chain.take()
|
||||
.ok_or_else(|| substrate_service::Error::SelectChainRequired)?;
|
||||
@@ -191,9 +194,15 @@ pub fn new_light<C: Send + Default + 'static>(config: Configuration<C, GenesisCo
|
||||
.with_select_chain(|_config, backend| {
|
||||
Ok(LongestChain::new(backend.clone()))
|
||||
})?
|
||||
.with_transaction_pool(|config, client|
|
||||
Ok(TransactionPool::new(config, transaction_pool::FullChainApi::new(client)))
|
||||
)?
|
||||
.with_transaction_pool(|config, client, fetcher| {
|
||||
let fetcher = fetcher
|
||||
.ok_or_else(|| "Trying to start light transaction pool without active fetcher")?;
|
||||
let pool_api = txpool::LightChainApi::new(client.clone(), fetcher.clone());
|
||||
let pool = txpool::BasicPool::new(config, pool_api);
|
||||
let maintainer = txpool::LightBasicPoolMaintainer::with_defaults(pool.pool().clone(), client, fetcher);
|
||||
let maintainable_pool = txpool_api::MaintainableTransactionPool::new(pool, maintainer);
|
||||
Ok(maintainable_pool)
|
||||
})?
|
||||
.with_import_queue_and_fprb(|_config, client, backend, fetcher, _select_chain, _tx_pool| {
|
||||
let fetch_checker = fetcher
|
||||
.map(|fetcher| fetcher.checker().clone())
|
||||
|
||||
@@ -49,7 +49,8 @@ runtime-io = { package = "sr-io", path = "../../../primitives/sr-io" }
|
||||
client-api = { package = "substrate-client-api", path = "../../../client/api" }
|
||||
client = { package = "substrate-client", path = "../../../client/" }
|
||||
chain-spec = { package = "substrate-chain-spec", path = "../../../client/chain-spec" }
|
||||
transaction_pool = { package = "sc-transaction-pool", path = "../../../client/transaction-pool" }
|
||||
txpool = { package = "sc-transaction-pool", path = "../../../client/transaction-pool" }
|
||||
txpool-api = { package = "sp-transaction-pool-api", path = "../../../primitives/transaction-pool" }
|
||||
network = { package = "substrate-network", path = "../../../client/network" }
|
||||
babe = { package = "substrate-consensus-babe", path = "../../../client/consensus/babe" }
|
||||
grandpa = { package = "substrate-finality-grandpa", path = "../../../client/finality-grandpa" }
|
||||
|
||||
@@ -29,7 +29,6 @@ use node_runtime::{GenesisConfig, RuntimeApi};
|
||||
use substrate_service::{
|
||||
AbstractService, ServiceBuilder, config::Configuration, error::{Error as ServiceError},
|
||||
};
|
||||
use transaction_pool::{self, txpool::{Pool as TransactionPool}};
|
||||
use inherents::InherentDataProviders;
|
||||
use network::construct_simple_protocol;
|
||||
|
||||
@@ -63,9 +62,13 @@ macro_rules! new_full_start {
|
||||
.with_select_chain(|_config, backend| {
|
||||
Ok(client::LongestChain::new(backend.clone()))
|
||||
})?
|
||||
.with_transaction_pool(|config, client|
|
||||
Ok(transaction_pool::txpool::Pool::new(config, transaction_pool::FullChainApi::new(client)))
|
||||
)?
|
||||
.with_transaction_pool(|config, client, _fetcher| {
|
||||
let pool_api = txpool::FullChainApi::new(client.clone());
|
||||
let pool = txpool::BasicPool::new(config, pool_api);
|
||||
let maintainer = txpool::FullBasicPoolMaintainer::new(pool.pool().clone(), client);
|
||||
let maintainable_pool = txpool_api::MaintainableTransactionPool::new(pool, maintainer);
|
||||
Ok(maintainable_pool)
|
||||
})?
|
||||
.with_import_queue(|_config, client, mut select_chain, _transaction_pool| {
|
||||
let select_chain = select_chain.take()
|
||||
.ok_or_else(|| substrate_service::Error::SelectChainRequired)?;
|
||||
@@ -96,8 +99,8 @@ macro_rules! new_full_start {
|
||||
import_setup = Some((block_import, grandpa_link, babe_link));
|
||||
Ok(import_queue)
|
||||
})?
|
||||
.with_rpc_extensions(|client, pool, _backend| -> RpcExtension {
|
||||
node_rpc::create(client, pool)
|
||||
.with_rpc_extensions(|client, pool, _backend, fetcher, _remote_blockchain| -> Result<RpcExtension, _> {
|
||||
Ok(node_rpc::create(client, pool, node_rpc::LightDeps::none(fetcher)))
|
||||
})?;
|
||||
|
||||
(builder, import_setup, inherent_data_providers)
|
||||
@@ -267,6 +270,17 @@ type ConcreteClient =
|
||||
>;
|
||||
#[allow(dead_code)]
|
||||
type ConcreteBackend = Backend<ConcreteBlock>;
|
||||
#[allow(dead_code)]
|
||||
type ConcreteTransactionPool = txpool_api::MaintainableTransactionPool<
|
||||
txpool::BasicPool<
|
||||
txpool::FullChainApi<ConcreteClient, ConcreteBlock>,
|
||||
ConcreteBlock
|
||||
>,
|
||||
txpool::FullBasicPoolMaintainer<
|
||||
ConcreteClient,
|
||||
txpool::FullChainApi<ConcreteClient, Block>
|
||||
>
|
||||
>;
|
||||
|
||||
/// A specialized configuration object for setting up the node..
|
||||
pub type NodeConfiguration<C> = Configuration<C, GenesisConfig, crate::chain_spec::Extensions>;
|
||||
@@ -280,7 +294,7 @@ pub fn new_full<C: Send + Default + 'static>(config: NodeConfiguration<C>)
|
||||
LongestChain<ConcreteBackend, ConcreteBlock>,
|
||||
NetworkStatus<ConcreteBlock>,
|
||||
NetworkService<ConcreteBlock, crate::service::NodeProtocol, <ConcreteBlock as BlockT>::Hash>,
|
||||
TransactionPool<transaction_pool::FullChainApi<ConcreteClient, ConcreteBlock>>,
|
||||
ConcreteTransactionPool,
|
||||
OffchainWorkers<
|
||||
ConcreteClient,
|
||||
<ConcreteBackend as client_api::backend::Backend<Block, Blake2Hasher>>::OffchainStorage,
|
||||
@@ -303,9 +317,15 @@ pub fn new_light<C: Send + Default + 'static>(config: NodeConfiguration<C>)
|
||||
.with_select_chain(|_config, backend| {
|
||||
Ok(LongestChain::new(backend.clone()))
|
||||
})?
|
||||
.with_transaction_pool(|config, client|
|
||||
Ok(TransactionPool::new(config, transaction_pool::FullChainApi::new(client)))
|
||||
)?
|
||||
.with_transaction_pool(|config, client, fetcher| {
|
||||
let fetcher = fetcher
|
||||
.ok_or_else(|| "Trying to start light transaction pool without active fetcher")?;
|
||||
let pool_api = txpool::LightChainApi::new(client.clone(), fetcher.clone());
|
||||
let pool = txpool::BasicPool::new(config, pool_api);
|
||||
let maintainer = txpool::LightBasicPoolMaintainer::with_defaults(pool.pool().clone(), client, fetcher);
|
||||
let maintainable_pool = txpool_api::MaintainableTransactionPool::new(pool, maintainer);
|
||||
Ok(maintainable_pool)
|
||||
})?
|
||||
.with_import_queue_and_fprb(|_config, client, backend, fetcher, _select_chain, _tx_pool| {
|
||||
let fetch_checker = fetcher
|
||||
.map(|fetcher| fetcher.checker().clone())
|
||||
@@ -344,8 +364,14 @@ pub fn new_light<C: Send + Default + 'static>(config: NodeConfiguration<C>)
|
||||
.with_finality_proof_provider(|client, backend|
|
||||
Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, client)) as _)
|
||||
)?
|
||||
.with_rpc_extensions(|client, pool, _backend| -> RpcExtension {
|
||||
node_rpc::create(client, pool)
|
||||
.with_rpc_extensions(|client, pool, _backend, fetcher, remote_blockchain| -> Result<RpcExtension, _> {
|
||||
let fetcher = fetcher
|
||||
.ok_or_else(|| "Trying to start node RPC without active fetcher")?;
|
||||
let remote_blockchain = remote_blockchain
|
||||
.ok_or_else(|| "Trying to start node RPC without active remote blockchain")?;
|
||||
|
||||
let light_deps = node_rpc::LightDeps { remote_blockchain, fetcher };
|
||||
Ok(node_rpc::create(client, pool, Some(light_deps)))
|
||||
})?
|
||||
.build()?;
|
||||
|
||||
|
||||
@@ -13,4 +13,4 @@ sr-primitives = { path = "../../../primitives/sr-primitives" }
|
||||
pallet-contracts-rpc = { path = "../../../frame/contracts/rpc/" }
|
||||
pallet-transaction-payment-rpc = { path = "../../../frame/transaction-payment/rpc/" }
|
||||
substrate-frame-rpc-system = { path = "../../../utils/frame/rpc/system" }
|
||||
transaction_pool = { package = "sc-transaction-pool", path = "../../../client/transaction-pool" }
|
||||
txpool-api = { package = "sp-transaction-pool-api", path = "../../../primitives/transaction-pool" }
|
||||
|
||||
@@ -34,32 +34,68 @@ use std::sync::Arc;
|
||||
use node_primitives::{Block, AccountId, Index, Balance};
|
||||
use node_runtime::UncheckedExtrinsic;
|
||||
use sr_primitives::traits::ProvideRuntimeApi;
|
||||
use transaction_pool::txpool::{ChainApi, Pool};
|
||||
use txpool_api::TransactionPool;
|
||||
|
||||
/// Light client extra dependencies.
|
||||
pub struct LightDeps<F> {
|
||||
/// Remote access to the blockchain (async).
|
||||
pub remote_blockchain: Arc<dyn client::light::blockchain::RemoteBlockchain<Block>>,
|
||||
/// Fetcher instance.
|
||||
pub fetcher: Arc<F>,
|
||||
}
|
||||
|
||||
impl<F> LightDeps<F> {
|
||||
/// Create empty `LightDeps` with given `F` type.
|
||||
///
|
||||
/// This is a convenience method to be used in the service builder,
|
||||
/// to make sure the type of the `LightDeps<F>` is matching.
|
||||
pub fn none(_: Option<Arc<F>>) -> Option<Self> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Instantiate all RPC extensions.
|
||||
pub fn create<C, P, M>(client: Arc<C>, pool: Arc<Pool<P>>) -> jsonrpc_core::IoHandler<M> where
|
||||
///
|
||||
/// If you provide `LightDeps`, the system is configured for light client.
|
||||
pub fn create<C, P, M, F>(
|
||||
client: Arc<C>,
|
||||
pool: Arc<P>,
|
||||
light_deps: Option<LightDeps<F>>,
|
||||
) -> jsonrpc_core::IoHandler<M> where
|
||||
C: ProvideRuntimeApi,
|
||||
C: client::blockchain::HeaderBackend<Block>,
|
||||
C: Send + Sync + 'static,
|
||||
C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Index>,
|
||||
C::Api: pallet_contracts_rpc::ContractsRuntimeApi<Block, AccountId, Balance>,
|
||||
C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance, UncheckedExtrinsic>,
|
||||
P: ChainApi + Sync + Send + 'static,
|
||||
F: client::light::fetcher::Fetcher<Block> + 'static,
|
||||
P: TransactionPool + 'static,
|
||||
M: jsonrpc_core::Metadata + Default,
|
||||
{
|
||||
use substrate_frame_rpc_system::{System, SystemApi};
|
||||
use substrate_frame_rpc_system::{FullSystem, LightSystem, SystemApi};
|
||||
use pallet_contracts_rpc::{Contracts, ContractsApi};
|
||||
use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApi};
|
||||
|
||||
let mut io = jsonrpc_core::IoHandler::default();
|
||||
io.extend_with(
|
||||
SystemApi::to_delegate(System::new(client.clone(), pool))
|
||||
);
|
||||
io.extend_with(
|
||||
ContractsApi::to_delegate(Contracts::new(client.clone()))
|
||||
);
|
||||
io.extend_with(
|
||||
TransactionPaymentApi::to_delegate(TransactionPayment::new(client))
|
||||
);
|
||||
|
||||
if let Some(LightDeps { remote_blockchain, fetcher }) = light_deps {
|
||||
io.extend_with(
|
||||
SystemApi::<AccountId, Index>::to_delegate(LightSystem::new(client, remote_blockchain, fetcher, pool))
|
||||
);
|
||||
} else {
|
||||
io.extend_with(
|
||||
SystemApi::to_delegate(FullSystem::new(client.clone(), pool))
|
||||
);
|
||||
|
||||
// Making synchronous calls in light client freezes the browser currently,
|
||||
// more context: https://github.com/paritytech/substrate/pull/3480
|
||||
// These RPCs should use an asynchronous caller instead.
|
||||
io.extend_with(
|
||||
ContractsApi::to_delegate(Contracts::new(client.clone()))
|
||||
);
|
||||
io.extend_with(
|
||||
TransactionPaymentApi::to_delegate(TransactionPayment::new(client))
|
||||
);
|
||||
}
|
||||
io
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ sr-primitives = { path = "../../../primitives/sr-primitives", default-features =
|
||||
sr-staking-primitives = { path = "../../../primitives/sr-staking-primitives", default-features = false }
|
||||
substrate-keyring = { path = "../../../primitives/keyring", optional = true }
|
||||
substrate-session = { path = "../../../primitives/session", default-features = false }
|
||||
tx-pool-api = { package = "substrate-transaction-pool-runtime-api", path = "../../../primitives/transaction-pool/runtime-api", default-features = false }
|
||||
txpool-runtime-api = { package = "sp-transaction-pool-runtime-api", path = "../../../primitives/transaction-pool/runtime-api", default-features = false }
|
||||
version = { package = "sr-version", path = "../../../primitives/sr-version", default-features = false }
|
||||
|
||||
# frame dependencies
|
||||
@@ -116,7 +116,7 @@ std = [
|
||||
"transaction-payment-rpc-runtime-api/std",
|
||||
"transaction-payment/std",
|
||||
"treasury/std",
|
||||
"tx-pool-api/std",
|
||||
"txpool-runtime-api/std",
|
||||
"utility/std",
|
||||
"version/std",
|
||||
]
|
||||
|
||||
@@ -609,7 +609,7 @@ impl_runtime_apis! {
|
||||
}
|
||||
}
|
||||
|
||||
impl tx_pool_api::TaggedTransactionQueue<Block> for Runtime {
|
||||
impl txpool_runtime_api::TaggedTransactionQueue<Block> for Runtime {
|
||||
fn validate_transaction(tx: <Block as BlockT>::Extrinsic) -> TransactionValidity {
|
||||
Executive::validate_transaction(tx)
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ sr-primitives = { path = "../../primitives/sr-primitives", default-features = fa
|
||||
state-machine = { package = "substrate-state-machine", path = "../../primitives/state-machine" }
|
||||
substrate-telemetry = { path = "../telemetry" }
|
||||
trie = { package = "substrate-trie", path = "../../primitives/trie" }
|
||||
txpool-api = { package = "sp-transaction-pool-api", path = "../../primitives/transaction-pool" }
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.7.0"
|
||||
|
||||
@@ -30,7 +30,6 @@ use primitives::{
|
||||
use sr_primitives::{
|
||||
generic::BlockId,
|
||||
traits,
|
||||
offchain::{TransactionPool},
|
||||
};
|
||||
use state_machine::{ExecutionStrategy, ExecutionManager, DefaultHandler};
|
||||
use externalities::Extensions;
|
||||
@@ -71,7 +70,7 @@ impl Default for ExecutionStrategies {
|
||||
pub struct ExecutionExtensions<Block: traits::Block> {
|
||||
strategies: ExecutionStrategies,
|
||||
keystore: Option<BareCryptoStorePtr>,
|
||||
transaction_pool: RwLock<Option<Weak<dyn TransactionPool<Block>>>>,
|
||||
transaction_pool: RwLock<Option<Weak<dyn txpool_api::OffchainSubmitTransaction<Block>>>>,
|
||||
}
|
||||
|
||||
impl<Block: traits::Block> Default for ExecutionExtensions<Block> {
|
||||
@@ -105,7 +104,7 @@ impl<Block: traits::Block> ExecutionExtensions<Block> {
|
||||
/// extension to be a `Weak` reference.
|
||||
/// That's also the reason why it's being registered lazily instead of
|
||||
/// during initialisation.
|
||||
pub fn register_transaction_pool(&self, pool: Weak<dyn TransactionPool<Block>>) {
|
||||
pub fn register_transaction_pool(&self, pool: Weak<dyn txpool_api::OffchainSubmitTransaction<Block>>) {
|
||||
*self.transaction_pool.write() = Some(pool);
|
||||
}
|
||||
|
||||
@@ -166,7 +165,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 TransactionPool<Block>>,
|
||||
pool: Arc<dyn txpool_api::OffchainSubmitTransaction<Block>>,
|
||||
}
|
||||
|
||||
impl<Block: traits::Block> offchain::TransactionPool for TransactionPoolAdapter<Block> {
|
||||
|
||||
@@ -139,15 +139,30 @@ pub struct RemoteBodyRequest<Header: HeaderT> {
|
||||
/// is correct (see FetchedDataChecker) and return already checked data.
|
||||
pub trait Fetcher<Block: BlockT>: Send + Sync {
|
||||
/// Remote header future.
|
||||
type RemoteHeaderResult: Future<Output = Result<Block::Header, ClientError>> + Send + 'static;
|
||||
type RemoteHeaderResult: Future<Output = Result<
|
||||
Block::Header,
|
||||
ClientError,
|
||||
>> + Unpin + Send + 'static;
|
||||
/// Remote storage read future.
|
||||
type RemoteReadResult: Future<Output = Result<HashMap<Vec<u8>, Option<Vec<u8>>>, ClientError>> + Send + 'static;
|
||||
type RemoteReadResult: Future<Output = Result<
|
||||
HashMap<Vec<u8>, Option<Vec<u8>>>,
|
||||
ClientError,
|
||||
>> + Unpin + Send + 'static;
|
||||
/// Remote call result future.
|
||||
type RemoteCallResult: Future<Output = Result<Vec<u8>, ClientError>> + Send + 'static;
|
||||
type RemoteCallResult: Future<Output = Result<
|
||||
Vec<u8>,
|
||||
ClientError,
|
||||
>> + Unpin + Send + 'static;
|
||||
/// Remote changes result future.
|
||||
type RemoteChangesResult: Future<Output = Result<Vec<(NumberFor<Block>, u32)>, ClientError>> + Send + 'static;
|
||||
type RemoteChangesResult: Future<Output = Result<
|
||||
Vec<(NumberFor<Block>, u32)>,
|
||||
ClientError,
|
||||
>> + Unpin + Send + 'static;
|
||||
/// Remote block body result future.
|
||||
type RemoteBodyResult: Future<Output = Result<Vec<Block::Extrinsic>, ClientError>> + Send + 'static;
|
||||
type RemoteBodyResult: Future<Output = Result<
|
||||
Vec<Block::Extrinsic>,
|
||||
ClientError,
|
||||
>> + Unpin + Send + 'static;
|
||||
|
||||
/// Fetch remote header.
|
||||
fn remote_header(&self, request: RemoteHeaderRequest<Block::Header>) -> Self::RemoteHeaderResult;
|
||||
|
||||
@@ -16,10 +16,11 @@ client-api = { package = "substrate-client-api", path = "../api" }
|
||||
consensus_common = { package = "substrate-consensus-common", path = "../../primitives/consensus/common" }
|
||||
inherents = { package = "substrate-inherents", path = "../../primitives/inherents" }
|
||||
substrate-telemetry = { path = "../telemetry" }
|
||||
transaction_pool = { package = "sc-transaction-pool", path = "../../client/transaction-pool" }
|
||||
txpool-api = { package = "sp-transaction-pool-api", path = "../../primitives/transaction-pool" }
|
||||
block-builder = { package = "substrate-block-builder", path = "../block-builder" }
|
||||
tokio-executor = { version = "0.2.0-alpha.6", features = ["blocking"] }
|
||||
|
||||
[dev-dependencies]
|
||||
txpool = { package = "sc-transaction-pool", path = "../../client/transaction-pool" }
|
||||
test-client = { package = "substrate-test-runtime-client", path = "../../test/utils/runtime/client" }
|
||||
parking_lot = "0.9"
|
||||
|
||||
@@ -33,21 +33,21 @@ use sr_primitives::{
|
||||
},
|
||||
generic::BlockId,
|
||||
};
|
||||
use transaction_pool::txpool::{self, Pool as TransactionPool};
|
||||
use txpool_api::{TransactionPool, InPoolTransaction};
|
||||
use substrate_telemetry::{telemetry, CONSENSUS_INFO};
|
||||
use block_builder::BlockBuilderApi;
|
||||
|
||||
/// Proposer factory.
|
||||
pub struct ProposerFactory<C, A> where A: txpool::ChainApi {
|
||||
pub struct ProposerFactory<C, A> where A: TransactionPool {
|
||||
/// The client instance.
|
||||
pub client: Arc<C>,
|
||||
/// The transaction pool.
|
||||
pub transaction_pool: Arc<TransactionPool<A>>,
|
||||
pub transaction_pool: Arc<A>,
|
||||
}
|
||||
|
||||
impl<B, E, Block, RA, A> ProposerFactory<SubstrateClient<B, E, Block, RA>, A>
|
||||
where
|
||||
A: txpool::ChainApi<Block=Block> + 'static,
|
||||
A: TransactionPool<Block=Block> + 'static,
|
||||
B: client_api::backend::Backend<Block, Blake2Hasher> + Send + Sync + 'static,
|
||||
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + Clone + 'static,
|
||||
Block: BlockT<Hash=H256>,
|
||||
@@ -85,7 +85,7 @@ where
|
||||
impl<B, E, Block, RA, A> consensus_common::Environment<Block> for
|
||||
ProposerFactory<SubstrateClient<B, E, Block, RA>, A>
|
||||
where
|
||||
A: txpool::ChainApi<Block=Block> + 'static,
|
||||
A: TransactionPool<Block=Block> + 'static,
|
||||
B: client_api::backend::Backend<Block, Blake2Hasher> + Send + Sync + 'static,
|
||||
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + Clone + 'static,
|
||||
Block: BlockT<Hash=H256>,
|
||||
@@ -106,24 +106,24 @@ where
|
||||
}
|
||||
|
||||
/// The proposer logic.
|
||||
pub struct Proposer<Block: BlockT, C, A: txpool::ChainApi> {
|
||||
pub struct Proposer<Block: BlockT, C, A: TransactionPool> {
|
||||
inner: Arc<ProposerInner<Block, C, A>>,
|
||||
}
|
||||
|
||||
/// Proposer inner, to wrap parameters under Arc.
|
||||
struct ProposerInner<Block: BlockT, C, A: txpool::ChainApi> {
|
||||
struct ProposerInner<Block: BlockT, C, A: TransactionPool> {
|
||||
client: Arc<C>,
|
||||
parent_hash: <Block as BlockT>::Hash,
|
||||
parent_id: BlockId<Block>,
|
||||
parent_number: <<Block as BlockT>::Header as HeaderT>::Number,
|
||||
transaction_pool: Arc<TransactionPool<A>>,
|
||||
transaction_pool: Arc<A>,
|
||||
now: Box<dyn Fn() -> time::Instant + Send + Sync>,
|
||||
}
|
||||
|
||||
impl<B, E, Block, RA, A> consensus_common::Proposer<Block> for
|
||||
Proposer<Block, SubstrateClient<B, E, Block, RA>, A>
|
||||
where
|
||||
A: txpool::ChainApi<Block=Block> + 'static,
|
||||
A: TransactionPool<Block=Block> + 'static,
|
||||
B: client_api::backend::Backend<Block, Blake2Hasher> + Send + Sync + 'static,
|
||||
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + Clone + 'static,
|
||||
Block: BlockT<Hash=H256>,
|
||||
@@ -151,7 +151,7 @@ where
|
||||
}
|
||||
|
||||
impl<Block, B, E, RA, A> ProposerInner<Block, SubstrateClient<B, E, Block, RA>, A> where
|
||||
A: txpool::ChainApi<Block=Block> + 'static,
|
||||
A: TransactionPool<Block=Block> + 'static,
|
||||
B: client_api::backend::Backend<Block, Blake2Hasher> + Send + Sync + 'static,
|
||||
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + Clone + 'static,
|
||||
Block: BlockT<Hash=H256>,
|
||||
@@ -192,22 +192,24 @@ impl<Block, B, E, RA, A> ProposerInner<Block, SubstrateClient<B, E, Block, RA>,
|
||||
let pending_iterator = self.transaction_pool.ready();
|
||||
|
||||
debug!("Attempting to push transactions from the pool.");
|
||||
for pending in pending_iterator {
|
||||
for pending_tx in pending_iterator {
|
||||
if (self.now)() > deadline {
|
||||
debug!("Consensus deadline reached when pushing block transactions, proceeding with proposing.");
|
||||
break;
|
||||
}
|
||||
|
||||
trace!("[{:?}] Pushing to the block.", pending.hash);
|
||||
match block_builder::BlockBuilder::push(&mut block_builder, pending.data.clone()) {
|
||||
let pending_tx_data = pending_tx.data().clone();
|
||||
let pending_tx_hash = pending_tx.hash().clone();
|
||||
trace!("[{:?}] Pushing to the block.", pending_tx_hash);
|
||||
match block_builder::BlockBuilder::push(&mut block_builder, pending_tx_data) {
|
||||
Ok(()) => {
|
||||
debug!("[{:?}] Pushed to the block.", pending.hash);
|
||||
debug!("[{:?}] Pushed to the block.", pending_tx_hash);
|
||||
}
|
||||
Err(sp_blockchain::Error::ApplyExtrinsicFailed(sp_blockchain::ApplyExtrinsicFailed::Validity(e)))
|
||||
if e.exhausted_resources() => {
|
||||
if is_first {
|
||||
debug!("[{:?}] Invalid transaction: FullBlock on empty block", pending.hash);
|
||||
unqueue_invalid.push(pending.hash.clone());
|
||||
debug!("[{:?}] Invalid transaction: FullBlock on empty block", pending_tx_hash);
|
||||
unqueue_invalid.push(pending_tx_hash);
|
||||
} else if skipped < MAX_SKIPPED_TRANSACTIONS {
|
||||
skipped += 1;
|
||||
debug!(
|
||||
@@ -220,8 +222,8 @@ impl<Block, B, E, RA, A> ProposerInner<Block, SubstrateClient<B, E, Block, RA>,
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("[{:?}] Invalid transaction: {}", pending.hash, e);
|
||||
unqueue_invalid.push(pending.hash.clone());
|
||||
debug!("[{:?}] Invalid transaction: {}", pending_tx_hash, e);
|
||||
unqueue_invalid.push(pending_tx_hash);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,6 +268,7 @@ mod tests {
|
||||
use parking_lot::Mutex;
|
||||
use consensus_common::Proposer;
|
||||
use test_client::{self, runtime::{Extrinsic, Transfer}, AccountKeyring};
|
||||
use txpool::{BasicPool, FullChainApi};
|
||||
|
||||
fn extrinsic(nonce: u64) -> Extrinsic {
|
||||
Transfer {
|
||||
@@ -280,11 +283,10 @@ mod tests {
|
||||
fn should_cease_building_block_when_deadline_is_reached() {
|
||||
// given
|
||||
let client = Arc::new(test_client::new());
|
||||
let chain_api = transaction_pool::FullChainApi::new(client.clone());
|
||||
let txpool = Arc::new(TransactionPool::new(Default::default(), chain_api));
|
||||
let txpool = Arc::new(BasicPool::new(Default::default(), FullChainApi::new(client.clone())));
|
||||
|
||||
futures::executor::block_on(
|
||||
txpool.submit_at(&BlockId::number(0), vec![extrinsic(0), extrinsic(1)], false)
|
||||
txpool.submit_at(&BlockId::number(0), vec![extrinsic(0), extrinsic(1)])
|
||||
).unwrap();
|
||||
|
||||
let mut proposer_factory = ProposerFactory {
|
||||
|
||||
@@ -24,10 +24,9 @@
|
||||
//! # use sr_primitives::generic::BlockId;
|
||||
//! # use std::{sync::Arc, time::Duration};
|
||||
//! # use test_client::{self, runtime::{Extrinsic, Transfer}, AccountKeyring};
|
||||
//! # use transaction_pool::txpool::{self, Pool as TransactionPool};
|
||||
//! # use txpool::{BasicPool, FullChainApi};
|
||||
//! # let client = Arc::new(test_client::new());
|
||||
//! # let chain_api = transaction_pool::FullChainApi::new(client.clone());
|
||||
//! # let txpool = Arc::new(TransactionPool::new(Default::default(), chain_api));
|
||||
//! # let txpool = Arc::new(BasicPool::new(Default::default(), FullChainApi::new(client.clone())));
|
||||
//! // The first step is to create a `ProposerFactory`.
|
||||
//! let mut proposer_factory = ProposerFactory {
|
||||
//! client: client.clone(),
|
||||
|
||||
@@ -35,7 +35,8 @@ client-db = { package = "substrate-client-db", path = "../db/", default-features
|
||||
env_logger = "0.7.0"
|
||||
test-client = { package = "substrate-test-runtime-client", path = "../../test/utils/runtime/client" }
|
||||
tokio = "0.1.22"
|
||||
transaction_pool = { package = "sc-transaction-pool", path = "../../client/transaction-pool" }
|
||||
txpool = { package = "sc-transaction-pool", path = "../../client/transaction-pool" }
|
||||
txpool-api = { package = "sp-transaction-pool-api", path = "../../primitives/transaction-pool" }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
@@ -145,9 +145,11 @@ impl<Client, Storage, Block> OffchainWorkers<
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use network::{Multiaddr, PeerId};
|
||||
use std::sync::Arc;
|
||||
use transaction_pool::txpool::Pool;
|
||||
use network::{Multiaddr, PeerId};
|
||||
use test_client::runtime::Block;
|
||||
use txpool::{BasicPool, FullChainApi};
|
||||
use txpool_api::{TransactionPool, InPoolTransaction};
|
||||
|
||||
struct MockNetworkStateInfo();
|
||||
|
||||
@@ -161,15 +163,26 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
struct TestPool(BasicPool<FullChainApi<test_client::TestClient, Block>, Block>);
|
||||
|
||||
impl txpool_api::OffchainSubmitTransaction<Block> for TestPool {
|
||||
fn submit_at(
|
||||
&self,
|
||||
at: &BlockId<Block>,
|
||||
extrinsic: <Block as sr_primitives::traits::Block>::Extrinsic,
|
||||
) -> Result<(), ()> {
|
||||
futures::executor::block_on(self.0.submit_one(&at, extrinsic))
|
||||
.map(|_| ())
|
||||
.map_err(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_call_into_runtime_and_produce_extrinsic() {
|
||||
// given
|
||||
let _ = env_logger::try_init();
|
||||
let client = Arc::new(test_client::new());
|
||||
let pool = Arc::new(Pool::new(
|
||||
Default::default(),
|
||||
transaction_pool::FullChainApi::new(client.clone())
|
||||
));
|
||||
let pool = Arc::new(TestPool(BasicPool::new(Default::default(), FullChainApi::new(client.clone()))));
|
||||
client.execution_extensions()
|
||||
.register_transaction_pool(Arc::downgrade(&pool.clone()) as _);
|
||||
let db = client_db::offchain::LocalStorage::new_test();
|
||||
@@ -180,7 +193,7 @@ mod tests {
|
||||
futures::executor::block_on(offchain.on_block_imported(&0u64, network_state, false));
|
||||
|
||||
// then
|
||||
assert_eq!(pool.status().ready, 1);
|
||||
assert_eq!(pool.ready().next().unwrap().is_propagateable(), false);
|
||||
assert_eq!(pool.0.status().ready, 1);
|
||||
assert_eq!(pool.0.ready().next().unwrap().is_propagateable(), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ rpc-primitives = { package = "substrate-rpc-primitives", path = "../../primitive
|
||||
state_machine = { package = "substrate-state-machine", path = "../../primitives/state-machine" }
|
||||
substrate-executor = { path = "../executor" }
|
||||
substrate-keystore = { path = "../keystore" }
|
||||
transaction_pool = { package = "sc-transaction-pool", path = "../../client/transaction-pool" }
|
||||
txpool-api = { package = "sp-transaction-pool-api", path = "../../primitives/transaction-pool" }
|
||||
sp-blockchain = { path = "../../primitives/blockchain" }
|
||||
hash-db = { version = "0.15.2", default-features = false }
|
||||
parking_lot = { version = "0.9.0" }
|
||||
@@ -36,3 +36,4 @@ rustc-hex = "2.0.1"
|
||||
sr-io = { path = "../../primitives/sr-io" }
|
||||
test-client = { package = "substrate-test-runtime-client", path = "../../test/utils/runtime/client" }
|
||||
tokio = "0.1.22"
|
||||
txpool = { package = "sc-transaction-pool", path = "../transaction-pool" }
|
||||
|
||||
@@ -18,5 +18,5 @@ primitives = { package = "substrate-primitives", path = "../../../primitives/cor
|
||||
runtime_version = { package = "sr-version", path = "../../../primitives/sr-version" }
|
||||
serde = { version = "1.0.101", features = ["derive"] }
|
||||
serde_json = "1.0.41"
|
||||
txpool = { package = "sc-transaction-graph", path = "../../../client/transaction-pool/graph" }
|
||||
txpool-api = { package = "sp-transaction-pool-api", path = "../../../primitives/transaction-pool" }
|
||||
rpc-primitives = { package = "substrate-rpc-primitives", path = "../../../primitives/rpc" }
|
||||
|
||||
@@ -34,7 +34,7 @@ pub enum Error {
|
||||
Client(Box<dyn std::error::Error + Send>),
|
||||
/// Transaction pool error,
|
||||
#[display(fmt="Transaction pool error: {}", _0)]
|
||||
Pool(txpool::error::Error),
|
||||
Pool(txpool_api::error::Error),
|
||||
/// Verification error
|
||||
#[display(fmt="Extrinsic verification error: {}", _0)]
|
||||
#[from(ignore)]
|
||||
@@ -93,7 +93,7 @@ const UNSUPPORTED_KEY_TYPE: i64 = POOL_INVALID_TX + 7;
|
||||
|
||||
impl From<Error> for rpc::Error {
|
||||
fn from(e: Error) -> Self {
|
||||
use txpool::error::{Error as PoolError};
|
||||
use txpool_api::error::{Error as PoolError};
|
||||
|
||||
match e {
|
||||
Error::BadFormat(e) => rpc::Error {
|
||||
|
||||
@@ -21,11 +21,9 @@ pub mod hash;
|
||||
|
||||
use jsonrpc_derive::rpc;
|
||||
use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId};
|
||||
use primitives::{
|
||||
Bytes
|
||||
};
|
||||
use primitives::Bytes;
|
||||
use txpool_api::TransactionStatus;
|
||||
use self::error::{FutureResult, Result};
|
||||
use txpool::watcher::Status;
|
||||
|
||||
pub use self::gen_client::Client as AuthorClient;
|
||||
|
||||
@@ -69,7 +67,7 @@ pub trait AuthorApi<Hash, BlockHash> {
|
||||
)]
|
||||
fn watch_extrinsic(&self,
|
||||
metadata: Self::Metadata,
|
||||
subscriber: Subscriber<Status<Hash, BlockHash>>,
|
||||
subscriber: Subscriber<TransactionStatus<Hash, BlockHash>>,
|
||||
bytes: Bytes
|
||||
);
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
mod tests;
|
||||
|
||||
use std::{sync::Arc, convert::TryInto};
|
||||
use futures::future::{FutureExt, TryFutureExt};
|
||||
use log::warn;
|
||||
|
||||
use client::Client;
|
||||
@@ -30,21 +29,17 @@ use rpc::futures::{
|
||||
Sink, Future,
|
||||
future::result,
|
||||
};
|
||||
use futures::{StreamExt as _, compat::Compat, future::ready};
|
||||
use futures::{StreamExt as _, compat::Compat};
|
||||
use futures::future::{ready, FutureExt, TryFutureExt};
|
||||
use api::Subscriptions;
|
||||
use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId};
|
||||
use codec::{Encode, Decode};
|
||||
use primitives::{Bytes, Blake2Hasher, H256, traits::BareCryptoStorePtr};
|
||||
use sr_api::ConstructRuntimeApi;
|
||||
use sr_primitives::{generic, traits::{self, ProvideRuntimeApi}};
|
||||
use transaction_pool::{
|
||||
txpool::{
|
||||
ChainApi as PoolChainApi,
|
||||
BlockHash,
|
||||
ExHash,
|
||||
IntoPoolError,
|
||||
Pool,
|
||||
watcher::Status,
|
||||
},
|
||||
use txpool_api::{
|
||||
TransactionPool, InPoolTransaction, TransactionStatus,
|
||||
BlockHash, TxHash, TransactionFor, IntoPoolError,
|
||||
};
|
||||
use session::SessionKeys;
|
||||
|
||||
@@ -53,22 +48,22 @@ pub use api::author::*;
|
||||
use self::error::{Error, FutureResult, Result};
|
||||
|
||||
/// Authoring API
|
||||
pub struct Author<B, E, P, RA> where P: PoolChainApi + Sync + Send + 'static {
|
||||
pub struct Author<B, E, P, Block: traits::Block, RA> {
|
||||
/// Substrate client
|
||||
client: Arc<Client<B, E, <P as PoolChainApi>::Block, RA>>,
|
||||
client: Arc<Client<B, E, Block, RA>>,
|
||||
/// Transactions pool
|
||||
pool: Arc<Pool<P>>,
|
||||
pool: Arc<P>,
|
||||
/// Subscriptions manager
|
||||
subscriptions: Subscriptions,
|
||||
/// The key store.
|
||||
keystore: BareCryptoStorePtr,
|
||||
}
|
||||
|
||||
impl<B, E, P, RA> Author<B, E, P, RA> where P: PoolChainApi + Sync + Send + 'static {
|
||||
impl<B, E, P, Block: traits::Block, RA> Author<B, E, P, Block, RA> {
|
||||
/// Create new instance of Authoring API.
|
||||
pub fn new(
|
||||
client: Arc<Client<B, E, <P as PoolChainApi>::Block, RA>>,
|
||||
pool: Arc<Pool<P>>,
|
||||
client: Arc<Client<B, E, Block, RA>>,
|
||||
pool: Arc<P>,
|
||||
subscriptions: Subscriptions,
|
||||
keystore: BareCryptoStorePtr,
|
||||
) -> Self {
|
||||
@@ -81,16 +76,15 @@ impl<B, E, P, RA> Author<B, E, P, RA> where P: PoolChainApi + Sync + Send + 'sta
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E, P, RA> AuthorApi<ExHash<P>, BlockHash<P>> for Author<B, E, P, RA> where
|
||||
B: client_api::backend::Backend<<P as PoolChainApi>::Block, Blake2Hasher> + Send + Sync + 'static,
|
||||
E: client_api::CallExecutor<<P as PoolChainApi>::Block, Blake2Hasher> + Send + Sync + 'static,
|
||||
P: PoolChainApi + Sync + Send + 'static,
|
||||
P::Block: traits::Block<Hash=H256>,
|
||||
P::Error: 'static,
|
||||
RA: Send + Sync + 'static,
|
||||
Client<B, E, P::Block, RA>: ProvideRuntimeApi,
|
||||
<Client<B, E, P::Block, RA> as ProvideRuntimeApi>::Api:
|
||||
SessionKeys<P::Block, Error = ClientError>,
|
||||
impl<B, E, P, Block, RA> AuthorApi<Block::Hash, Block::Hash> for Author<B, E, P, Block, RA> where
|
||||
Block: traits::Block<Hash=H256>,
|
||||
B: client_api::backend::Backend<Block, Blake2Hasher> + Send + Sync + 'static,
|
||||
E: client_api::CallExecutor<Block, Blake2Hasher> + Clone + Send + Sync + 'static,
|
||||
P: TransactionPool<Block=Block, Hash=Block::Hash> + Sync + Send + 'static,
|
||||
RA: ConstructRuntimeApi<Block, Client<B, E, Block, RA>> + Send + Sync + 'static,
|
||||
Client<B, E, Block, RA>: ProvideRuntimeApi,
|
||||
<Client<B, E, Block, RA> as ProvideRuntimeApi>::Api:
|
||||
SessionKeys<Block, Error = ClientError>,
|
||||
{
|
||||
type Metadata = crate::metadata::Metadata;
|
||||
|
||||
@@ -115,7 +109,7 @@ impl<B, E, P, RA> AuthorApi<ExHash<P>, BlockHash<P>> for Author<B, E, P, RA> whe
|
||||
).map(Into::into).map_err(|e| Error::Client(Box::new(e)))
|
||||
}
|
||||
|
||||
fn submit_extrinsic(&self, ext: Bytes) -> FutureResult<ExHash<P>> {
|
||||
fn submit_extrinsic(&self, ext: Bytes) -> FutureResult<TxHash<P>> {
|
||||
let xt = match Decode::decode(&mut &ext[..]) {
|
||||
Ok(xt) => xt,
|
||||
Err(err) => return Box::new(result(Err(err.into()))),
|
||||
@@ -131,13 +125,13 @@ impl<B, E, P, RA> AuthorApi<ExHash<P>, BlockHash<P>> for Author<B, E, P, RA> whe
|
||||
}
|
||||
|
||||
fn pending_extrinsics(&self) -> Result<Vec<Bytes>> {
|
||||
Ok(self.pool.ready().map(|tx| tx.data.encode().into()).collect())
|
||||
Ok(self.pool.ready().map(|tx| tx.data().encode().into()).collect())
|
||||
}
|
||||
|
||||
fn remove_extrinsic(
|
||||
&self,
|
||||
bytes_or_hash: Vec<hash::ExtrinsicOrHash<ExHash<P>>>,
|
||||
) -> Result<Vec<ExHash<P>>> {
|
||||
bytes_or_hash: Vec<hash::ExtrinsicOrHash<TxHash<P>>>,
|
||||
) -> Result<Vec<TxHash<P>>> {
|
||||
let hashes = bytes_or_hash.into_iter()
|
||||
.map(|x| match x {
|
||||
hash::ExtrinsicOrHash::Hash(h) => Ok(h),
|
||||
@@ -149,21 +143,22 @@ impl<B, E, P, RA> AuthorApi<ExHash<P>, BlockHash<P>> for Author<B, E, P, RA> whe
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
Ok(
|
||||
self.pool.remove_invalid(&hashes)
|
||||
self.pool
|
||||
.remove_invalid(&hashes)
|
||||
.into_iter()
|
||||
.map(|tx| tx.hash.clone())
|
||||
.map(|tx| tx.hash().clone())
|
||||
.collect()
|
||||
)
|
||||
}
|
||||
|
||||
fn watch_extrinsic(&self,
|
||||
_metadata: Self::Metadata,
|
||||
subscriber: Subscriber<Status<ExHash<P>, BlockHash<P>>>,
|
||||
subscriber: Subscriber<TransactionStatus<TxHash<P>, BlockHash<P>>>,
|
||||
xt: Bytes,
|
||||
) {
|
||||
let submit = || -> Result<_> {
|
||||
let best_block_hash = self.client.info().chain.best_hash;
|
||||
let dxt = <<P as PoolChainApi>::Block as traits::Block>::Extrinsic::decode(&mut &xt[..])
|
||||
let dxt = TransactionFor::<P>::decode(&mut &xt[..])
|
||||
.map_err(error::Error::from)?;
|
||||
Ok(
|
||||
self.pool
|
||||
@@ -179,7 +174,7 @@ impl<B, E, P, RA> AuthorApi<ExHash<P>, BlockHash<P>> for Author<B, E, P, RA> whe
|
||||
let future = ready(submit())
|
||||
.and_then(|res| res)
|
||||
// convert the watcher into a `Stream`
|
||||
.map(|res| res.map(|watcher| watcher.into_stream().map(|v| Ok::<_, ()>(Ok(v)))))
|
||||
.map(|res| res.map(|stream| stream.map(|v| Ok::<_, ()>(Ok(v)))))
|
||||
// now handle the import result,
|
||||
// start a new subscrition
|
||||
.map(move |result| match result {
|
||||
|
||||
@@ -25,13 +25,10 @@ use primitives::{
|
||||
};
|
||||
use rpc::futures::Stream as _;
|
||||
use test_client::{
|
||||
self, AccountKeyring, runtime::{Extrinsic, Transfer, SessionKeys, RuntimeApi, Block}, DefaultTestClientBuilderExt,
|
||||
TestClientBuilderExt, Backend, Client, Executor
|
||||
};
|
||||
use transaction_pool::{
|
||||
txpool::Pool,
|
||||
FullChainApi,
|
||||
self, AccountKeyring, runtime::{Extrinsic, Transfer, SessionKeys, RuntimeApi, Block},
|
||||
DefaultTestClientBuilderExt, TestClientBuilderExt, Backend, Client, Executor,
|
||||
};
|
||||
use txpool::{BasicPool, FullChainApi};
|
||||
use tokio::runtime;
|
||||
|
||||
fn uxt(sender: AccountKeyring, nonce: u64) -> Extrinsic {
|
||||
@@ -44,18 +41,23 @@ fn uxt(sender: AccountKeyring, nonce: u64) -> Extrinsic {
|
||||
tx.into_signed_tx()
|
||||
}
|
||||
|
||||
type FullTransactionPool = BasicPool<
|
||||
FullChainApi<Client<Backend>, Block>,
|
||||
Block,
|
||||
>;
|
||||
|
||||
struct TestSetup {
|
||||
pub runtime: runtime::Runtime,
|
||||
pub client: Arc<Client<Backend>>,
|
||||
pub keystore: BareCryptoStorePtr,
|
||||
pub pool: Arc<Pool<FullChainApi<Client<Backend>, Block>>>,
|
||||
pub pool: Arc<FullTransactionPool>,
|
||||
}
|
||||
|
||||
impl Default for TestSetup {
|
||||
fn default() -> Self {
|
||||
let keystore = KeyStore::new();
|
||||
let client = Arc::new(test_client::TestClientBuilder::new().set_keystore(keystore.clone()).build());
|
||||
let pool = Arc::new(Pool::new(Default::default(), FullChainApi::new(client.clone())));
|
||||
let pool = Arc::new(BasicPool::new(Default::default(), FullChainApi::new(client.clone())));
|
||||
TestSetup {
|
||||
runtime: runtime::Runtime::new().expect("Failed to create runtime in test setup"),
|
||||
client,
|
||||
@@ -66,7 +68,7 @@ impl Default for TestSetup {
|
||||
}
|
||||
|
||||
impl TestSetup {
|
||||
fn author(&self) -> Author<Backend, Executor, FullChainApi<Client<Backend>, Block>, RuntimeApi> {
|
||||
fn author(&self) -> Author<Backend, Executor, FullTransactionPool, Block, RuntimeApi> {
|
||||
Author {
|
||||
client: self.client.clone(),
|
||||
pool: self.pool.clone(),
|
||||
|
||||
@@ -41,11 +41,12 @@ chain-spec = { package = "substrate-chain-spec", path = "../chain-spec" }
|
||||
client-api = { package = "substrate-client-api", path = "../api" }
|
||||
client = { package = "substrate-client", path = "../" }
|
||||
sr-api = { path = "../../primitives/sr-api" }
|
||||
tx-pool-api = { package = "substrate-transaction-pool-runtime-api", path = "../../primitives/transaction-pool/runtime-api" }
|
||||
txpool-runtime-api = { package = "sp-transaction-pool-runtime-api", path = "../../primitives/transaction-pool/runtime-api" }
|
||||
client_db = { package = "substrate-client-db", path = "../db" }
|
||||
codec = { package = "parity-scale-codec", version = "1.0.0" }
|
||||
substrate-executor = { path = "../executor" }
|
||||
transaction_pool = { package = "sc-transaction-pool", path = "../../client/transaction-pool" }
|
||||
txpool = { package = "sc-transaction-pool", path = "../transaction-pool" }
|
||||
txpool-api = { package = "sp-transaction-pool-api", path = "../../primitives/transaction-pool" }
|
||||
rpc-servers = { package = "substrate-rpc-servers", path = "../rpc-servers" }
|
||||
rpc = { package = "substrate-rpc", path = "../rpc" }
|
||||
tel = { package = "substrate-telemetry", path = "../telemetry" }
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{Service, NetworkStatus, NetworkState, error::{self, Error}, DEFAULT_PROTOCOL_ID};
|
||||
use crate::{Service, NetworkStatus, NetworkState, error::Error, DEFAULT_PROTOCOL_ID};
|
||||
use crate::{SpawnTaskHandle, start_rpc_servers, build_network_future, TransactionPoolAdapter};
|
||||
use crate::status_sinks;
|
||||
use crate::config::{Configuration, DatabaseConfig};
|
||||
@@ -30,7 +30,6 @@ use consensus_common::import_queue::ImportQueue;
|
||||
use futures::{prelude::*, sync::mpsc};
|
||||
use futures03::{
|
||||
compat::{Compat, Future01CompatExt},
|
||||
future::ready,
|
||||
FutureExt as _, TryFutureExt as _,
|
||||
StreamExt as _, TryStreamExt as _,
|
||||
future::{select, Either}
|
||||
@@ -42,9 +41,11 @@ use network::{config::BoxFinalityProofRequestBuilder, specialization::NetworkSpe
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use primitives::{Blake2Hasher, H256, Hasher};
|
||||
use rpc;
|
||||
use sr_api::ConstructRuntimeApi;
|
||||
use sr_primitives::generic::BlockId;
|
||||
use sr_primitives::traits::{
|
||||
Block as BlockT, Extrinsic, ProvideRuntimeApi, NumberFor, One, Zero, Header, SaturatedConversion
|
||||
Block as BlockT, ProvideRuntimeApi, NumberFor, One,
|
||||
Zero, Header, SaturatedConversion,
|
||||
};
|
||||
use substrate_executor::{NativeExecutor, NativeExecutionDispatch};
|
||||
use std::{
|
||||
@@ -53,7 +54,7 @@ use std::{
|
||||
};
|
||||
use sysinfo::{get_current_pid, ProcessExt, System, SystemExt};
|
||||
use tel::{telemetry, SUBSTRATE_INFO};
|
||||
use transaction_pool::txpool::{self, ChainApi, Pool as TransactionPool};
|
||||
use txpool_api::{TransactionPool, TransactionPoolMaintainer};
|
||||
use sp_blockchain;
|
||||
use grafana_data_source::{self, record_metrics};
|
||||
|
||||
@@ -295,7 +296,7 @@ where TGen: RuntimeGenesis, TCSExt: Extension {
|
||||
client,
|
||||
backend,
|
||||
keystore,
|
||||
fetcher: Some(fetcher),
|
||||
fetcher: Some(fetcher.clone()),
|
||||
select_chain: None,
|
||||
import_queue: (),
|
||||
finality_proof_request_builder: None,
|
||||
@@ -559,10 +560,19 @@ impl<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp, TNet
|
||||
/// Defines which transaction pool to use.
|
||||
pub fn with_transaction_pool<UExPool>(
|
||||
self,
|
||||
transaction_pool_builder: impl FnOnce(transaction_pool::txpool::Options, Arc<TCl>) -> Result<UExPool, Error>
|
||||
transaction_pool_builder: impl FnOnce(
|
||||
txpool::txpool::Options,
|
||||
Arc<TCl>,
|
||||
Option<TFchr>,
|
||||
) -> Result<UExPool, Error>
|
||||
) -> Result<ServiceBuilder<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp,
|
||||
TNetP, UExPool, TRpc, Backend>, Error> {
|
||||
let transaction_pool = transaction_pool_builder(self.config.transaction_pool.clone(), self.client.clone())?;
|
||||
TNetP, UExPool, TRpc, Backend>, Error>
|
||||
where TSc: Clone, TFchr: Clone {
|
||||
let transaction_pool = transaction_pool_builder(
|
||||
self.config.transaction_pool.clone(),
|
||||
self.client.clone(),
|
||||
self.fetcher.clone(),
|
||||
)?;
|
||||
|
||||
Ok(ServiceBuilder {
|
||||
config: self.config,
|
||||
@@ -586,10 +596,23 @@ impl<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp, TNet
|
||||
/// Defines the RPC extensions to use.
|
||||
pub fn with_rpc_extensions<URpc>(
|
||||
self,
|
||||
rpc_ext_builder: impl FnOnce(Arc<TCl>, Arc<TExPool>, Arc<Backend>) -> URpc
|
||||
rpc_ext_builder: impl FnOnce(
|
||||
Arc<TCl>,
|
||||
Arc<TExPool>,
|
||||
Arc<Backend>,
|
||||
Option<TFchr>,
|
||||
Option<Arc<dyn RemoteBlockchain<TBl>>>,
|
||||
) -> Result<URpc, Error>,
|
||||
) -> Result<ServiceBuilder<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp,
|
||||
TNetP, TExPool, URpc, Backend>, Error> {
|
||||
let rpc_extensions = rpc_ext_builder(self.client.clone(), self.transaction_pool.clone(), self.backend.clone());
|
||||
TNetP, TExPool, URpc, Backend>, Error>
|
||||
where TSc: Clone, TFchr: Clone {
|
||||
let rpc_extensions = rpc_ext_builder(
|
||||
self.client.clone(),
|
||||
self.transaction_pool.clone(),
|
||||
self.backend.clone(),
|
||||
self.fetcher.clone(),
|
||||
self.remote_backend.clone(),
|
||||
)?;
|
||||
|
||||
Ok(ServiceBuilder {
|
||||
config: self.config,
|
||||
@@ -742,7 +765,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<TBl, TRtApi, TCfg, TGen, TCSExt, TBackend, TExec, TSc, TImpQu, TNetP, TExPoolApi, TRpc>
|
||||
impl<TBl, TRtApi, TCfg, TGen, TCSExt, TBackend, TExec, TSc, TImpQu, TNetP, TExPool, TRpc>
|
||||
ServiceBuilder<
|
||||
TBl,
|
||||
TRtApi,
|
||||
@@ -756,7 +779,7 @@ ServiceBuilder<
|
||||
BoxFinalityProofRequestBuilder<TBl>,
|
||||
Arc<dyn FinalityProofProvider<TBl>>,
|
||||
TNetP,
|
||||
TransactionPool<TExPoolApi>,
|
||||
TExPool,
|
||||
TRpc,
|
||||
TBackend,
|
||||
> where
|
||||
@@ -764,11 +787,11 @@ ServiceBuilder<
|
||||
<Client<TBackend, TExec, TBl, TRtApi> as ProvideRuntimeApi>::Api:
|
||||
sr_api::Metadata<TBl> +
|
||||
offchain::OffchainWorkerApi<TBl> +
|
||||
tx_pool_api::TaggedTransactionQueue<TBl> +
|
||||
txpool_runtime_api::TaggedTransactionQueue<TBl> +
|
||||
session::SessionKeys<TBl> +
|
||||
sr_api::ApiExt<TBl, Error = sp_blockchain::Error>,
|
||||
TBl: BlockT<Hash = <Blake2Hasher as Hasher>::Out>,
|
||||
TRtApi: 'static + Send + Sync,
|
||||
TRtApi: ConstructRuntimeApi<TBl, Client<TBackend, TExec, TBl, TRtApi>> + 'static + Send + Sync,
|
||||
TCfg: Default,
|
||||
TGen: RuntimeGenesis,
|
||||
TCSExt: Extension,
|
||||
@@ -777,7 +800,9 @@ ServiceBuilder<
|
||||
TSc: Clone,
|
||||
TImpQu: 'static + ImportQueue<TBl>,
|
||||
TNetP: NetworkSpecialization<TBl>,
|
||||
TExPoolApi: 'static + ChainApi<Block = TBl, Hash = <TBl as BlockT>::Hash>,
|
||||
TExPool: 'static
|
||||
+ TransactionPool<Block=TBl, Hash = <TBl as BlockT>::Hash>
|
||||
+ TransactionPoolMaintainer<Block=TBl, Hash = <TBl as BlockT>::Hash>,
|
||||
TRpc: rpc::RpcExtension<rpc::Metadata> + Clone,
|
||||
{
|
||||
/// Builds the service.
|
||||
@@ -787,7 +812,7 @@ ServiceBuilder<
|
||||
TSc,
|
||||
NetworkStatus<TBl>,
|
||||
NetworkService<TBl, TNetP, <TBl as BlockT>::Hash>,
|
||||
TransactionPool<TExPoolApi>,
|
||||
TExPool,
|
||||
offchain::OffchainWorkers<
|
||||
Client<TBackend, TExec, TBl, TRtApi>,
|
||||
TBackend::OffchainStorage,
|
||||
@@ -900,7 +925,6 @@ ServiceBuilder<
|
||||
{
|
||||
// block notifications
|
||||
let txpool = Arc::downgrade(&transaction_pool);
|
||||
let wclient = Arc::downgrade(&client);
|
||||
let offchain = offchain_workers.as_ref().map(Arc::downgrade);
|
||||
let to_spawn_tx_ = to_spawn_tx.clone();
|
||||
let network_state_info: Arc<dyn NetworkStateInfo + Send + Sync> = network.clone();
|
||||
@@ -912,14 +936,12 @@ ServiceBuilder<
|
||||
let number = *notification.header.number();
|
||||
let txpool = txpool.upgrade();
|
||||
|
||||
if let (Some(txpool), Some(client)) = (txpool.as_ref(), wclient.upgrade()) {
|
||||
let future = maintain_transaction_pool(
|
||||
if let Some(txpool) = txpool.as_ref() {
|
||||
let future = txpool.maintain(
|
||||
&BlockId::hash(notification.hash),
|
||||
&client,
|
||||
&*txpool,
|
||||
¬ification.retracted,
|
||||
).map_err(|e| warn!("Pool error processing new block: {:?}", e))?;
|
||||
let _ = to_spawn_tx_.unbounded_send(future);
|
||||
).map(|_| Ok(())).compat();
|
||||
let _ = to_spawn_tx_.unbounded_send(Box::new(future));
|
||||
}
|
||||
|
||||
let offchain = offchain.as_ref().and_then(|o| o.upgrade());
|
||||
@@ -1202,157 +1224,3 @@ ServiceBuilder<
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn maintain_transaction_pool<Api, Backend, Block, Executor, PoolApi>(
|
||||
id: &BlockId<Block>,
|
||||
client: &Arc<Client<Backend, Executor, Block, Api>>,
|
||||
transaction_pool: &TransactionPool<PoolApi>,
|
||||
retracted: &[Block::Hash],
|
||||
) -> error::Result<Box<dyn Future<Item = (), Error = ()> + Send>> where
|
||||
Block: BlockT<Hash = <Blake2Hasher as primitives::Hasher>::Out>,
|
||||
Backend: 'static + client_api::backend::Backend<Block, Blake2Hasher>,
|
||||
Client<Backend, Executor, Block, Api>: ProvideRuntimeApi,
|
||||
<Client<Backend, Executor, Block, Api> as ProvideRuntimeApi>::Api:
|
||||
tx_pool_api::TaggedTransactionQueue<Block>,
|
||||
Executor: 'static + client::CallExecutor<Block, Blake2Hasher>,
|
||||
PoolApi: 'static + txpool::ChainApi<Hash = Block::Hash, Block = Block>,
|
||||
Api: 'static,
|
||||
{
|
||||
// Put transactions from retracted blocks back into the pool.
|
||||
let client_copy = client.clone();
|
||||
let retracted_transactions = retracted.to_vec().into_iter()
|
||||
.filter_map(move |hash| client_copy.block(&BlockId::hash(hash)).ok().unwrap_or(None))
|
||||
.flat_map(|block| block.block.deconstruct().1.into_iter())
|
||||
.filter(|tx| tx.is_signed().unwrap_or(false));
|
||||
let resubmit_future = transaction_pool
|
||||
.submit_at(id, retracted_transactions, true)
|
||||
.then(|resubmit_result| ready(match resubmit_result {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => {
|
||||
warn!("Error re-submitting transactions: {:?}", e);
|
||||
Ok(())
|
||||
}
|
||||
}))
|
||||
.compat();
|
||||
|
||||
// Avoid calling into runtime if there is nothing to prune from the pool anyway.
|
||||
if transaction_pool.status().is_empty() {
|
||||
return Ok(Box::new(resubmit_future))
|
||||
}
|
||||
|
||||
let block = client.block(id)?;
|
||||
Ok(match block {
|
||||
Some(block) => {
|
||||
let parent_id = BlockId::hash(*block.block.header().parent_hash());
|
||||
let prune_future = transaction_pool
|
||||
.prune(id, &parent_id, block.block.extrinsics())
|
||||
.boxed()
|
||||
.compat()
|
||||
.map_err(|e| { format!("{:?}", e); });
|
||||
|
||||
Box::new(resubmit_future.and_then(|_| prune_future))
|
||||
},
|
||||
None => Box::new(resubmit_future),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use futures03::executor::block_on;
|
||||
use consensus_common::{BlockOrigin, SelectChain};
|
||||
use substrate_test_runtime_client::{prelude::*, runtime::Transfer};
|
||||
|
||||
#[test]
|
||||
fn should_remove_transactions_from_the_pool() {
|
||||
let (client, longest_chain) = TestClientBuilder::new().build_with_longest_chain();
|
||||
let client = Arc::new(client);
|
||||
let pool = TransactionPool::new(Default::default(), ::transaction_pool::FullChainApi::new(client.clone()));
|
||||
let transaction = Transfer {
|
||||
amount: 5,
|
||||
nonce: 0,
|
||||
from: AccountKeyring::Alice.into(),
|
||||
to: Default::default(),
|
||||
}.into_signed_tx();
|
||||
let best = longest_chain.best_chain().unwrap();
|
||||
|
||||
// store the transaction in the pool
|
||||
block_on(pool.submit_one(&BlockId::hash(best.hash()), transaction.clone())).unwrap();
|
||||
|
||||
// import the block
|
||||
let mut builder = client.new_block(Default::default()).unwrap();
|
||||
builder.push(transaction.clone()).unwrap();
|
||||
let block = builder.bake().unwrap();
|
||||
let id = BlockId::hash(block.header().hash());
|
||||
client.import(BlockOrigin::Own, block).unwrap();
|
||||
|
||||
// fire notification - this should clean up the queue
|
||||
assert_eq!(pool.status().ready, 1);
|
||||
maintain_transaction_pool(
|
||||
&id,
|
||||
&client,
|
||||
&pool,
|
||||
&[]
|
||||
).unwrap().wait().unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(pool.status().ready, 0);
|
||||
assert_eq!(pool.status().future, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_add_reverted_transactions_to_the_pool() {
|
||||
let (client, longest_chain) = TestClientBuilder::new().build_with_longest_chain();
|
||||
let client = Arc::new(client);
|
||||
let pool = TransactionPool::new(Default::default(), ::transaction_pool::FullChainApi::new(client.clone()));
|
||||
let transaction = Transfer {
|
||||
amount: 5,
|
||||
nonce: 0,
|
||||
from: AccountKeyring::Alice.into(),
|
||||
to: Default::default(),
|
||||
}.into_signed_tx();
|
||||
let best = longest_chain.best_chain().unwrap();
|
||||
|
||||
// store the transaction in the pool
|
||||
block_on(pool.submit_one(&BlockId::hash(best.hash()), transaction.clone())).unwrap();
|
||||
|
||||
// import the block
|
||||
let mut builder = client.new_block(Default::default()).unwrap();
|
||||
builder.push(transaction.clone()).unwrap();
|
||||
let block = builder.bake().unwrap();
|
||||
let block1_hash = block.header().hash();
|
||||
let id = BlockId::hash(block1_hash.clone());
|
||||
client.import(BlockOrigin::Own, block).unwrap();
|
||||
|
||||
// fire notification - this should clean up the queue
|
||||
assert_eq!(pool.status().ready, 1);
|
||||
maintain_transaction_pool(
|
||||
&id,
|
||||
&client,
|
||||
&pool,
|
||||
&[]
|
||||
).unwrap().wait().unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(pool.status().ready, 0);
|
||||
assert_eq!(pool.status().future, 0);
|
||||
|
||||
// import second block
|
||||
let builder = client.new_block_at(&BlockId::hash(best.hash()), Default::default()).unwrap();
|
||||
let block = builder.bake().unwrap();
|
||||
let id = BlockId::hash(block.header().hash());
|
||||
client.import(BlockOrigin::Own, block).unwrap();
|
||||
|
||||
// fire notification - this should add the transaction back to the pool.
|
||||
maintain_transaction_pool(
|
||||
&id,
|
||||
&client,
|
||||
&pool,
|
||||
&[block1_hash]
|
||||
).unwrap().wait().unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(pool.status().ready, 1);
|
||||
assert_eq!(pool.status().future, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ pub use network::config::{ExtTransport, NetworkConfiguration, Roles};
|
||||
pub use substrate_executor::WasmExecutionMethod;
|
||||
|
||||
use std::{path::PathBuf, net::SocketAddr, sync::Arc};
|
||||
use transaction_pool;
|
||||
pub use txpool::txpool::Options as TransactionPoolOptions;
|
||||
use chain_spec::{ChainSpec, RuntimeGenesis, Extension, NoExtension};
|
||||
use primitives::crypto::Protected;
|
||||
use target_info::Target;
|
||||
@@ -40,7 +40,7 @@ pub struct Configuration<C, G, E = NoExtension> {
|
||||
/// Node roles.
|
||||
pub roles: Roles,
|
||||
/// Extrinsic pool configuration.
|
||||
pub transaction_pool: transaction_pool::txpool::Options,
|
||||
pub transaction_pool: TransactionPoolOptions,
|
||||
/// Network configuration.
|
||||
pub network: NetworkConfiguration,
|
||||
/// Path to the base configuration directory.
|
||||
|
||||
@@ -53,12 +53,13 @@ use sr_primitives::generic::BlockId;
|
||||
use sr_primitives::traits::{NumberFor, Block as BlockT};
|
||||
|
||||
pub use self::error::Error;
|
||||
pub use self::builder::{ServiceBuilder, ServiceBuilderExport, ServiceBuilderImport, ServiceBuilderRevert};
|
||||
pub use self::builder::{
|
||||
ServiceBuilder, ServiceBuilderExport, ServiceBuilderImport, ServiceBuilderRevert,
|
||||
};
|
||||
pub use config::{Configuration, Roles, PruningMode};
|
||||
pub use chain_spec::{ChainSpec, Properties, RuntimeGenesis, Extension as ChainSpecExtension};
|
||||
pub use transaction_pool::txpool::{
|
||||
self, Pool as TransactionPool, Options as TransactionPoolOptions, ChainApi, IntoPoolError
|
||||
};
|
||||
pub use txpool_api::{TransactionPool, TransactionPoolMaintainer, InPoolTransaction, IntoPoolError};
|
||||
pub use txpool::txpool::Options as TransactionPoolOptions;
|
||||
pub use client::FinalityNotifications;
|
||||
pub use rpc::Metadata as RpcMetadata;
|
||||
#[doc(hidden)]
|
||||
@@ -144,8 +145,9 @@ pub trait AbstractService: 'static + Future<Item = (), Error = Error> +
|
||||
type RuntimeApi: Send + Sync;
|
||||
/// Chain selection algorithm.
|
||||
type SelectChain: consensus_common::SelectChain<Self::Block>;
|
||||
/// API of the transaction pool.
|
||||
type TransactionPoolApi: ChainApi<Block = Self::Block>;
|
||||
/// Transaction pool.
|
||||
type TransactionPool: TransactionPool<Block = Self::Block>
|
||||
+ TransactionPoolMaintainer<Block = Self::Block>;
|
||||
/// Network specialization.
|
||||
type NetworkSpecialization: NetworkSpecialization<Self::Block>;
|
||||
|
||||
@@ -193,22 +195,23 @@ pub trait AbstractService: 'static + Future<Item = (), Error = Error> +
|
||||
fn network_status(&self, interval: Duration) -> mpsc::UnboundedReceiver<(NetworkStatus<Self::Block>, NetworkState)>;
|
||||
|
||||
/// Get shared transaction pool instance.
|
||||
fn transaction_pool(&self) -> Arc<TransactionPool<Self::TransactionPoolApi>>;
|
||||
fn transaction_pool(&self) -> Arc<Self::TransactionPool>;
|
||||
|
||||
/// Get a handle to a future that will resolve on exit.
|
||||
fn on_exit(&self) -> ::exit_future::Exit;
|
||||
}
|
||||
|
||||
impl<TBl, TBackend, TExec, TRtApi, TSc, TNetSpec, TExPoolApi, TOc> AbstractService for
|
||||
impl<TBl, TBackend, TExec, TRtApi, TSc, TNetSpec, TExPool, TOc> AbstractService for
|
||||
Service<TBl, Client<TBackend, TExec, TBl, TRtApi>, TSc, NetworkStatus<TBl>,
|
||||
NetworkService<TBl, TNetSpec, H256>, TransactionPool<TExPoolApi>, TOc>
|
||||
NetworkService<TBl, TNetSpec, H256>, TExPool, TOc>
|
||||
where
|
||||
TBl: BlockT<Hash = H256>,
|
||||
TBackend: 'static + client_api::backend::Backend<TBl, Blake2Hasher>,
|
||||
TExec: 'static + client::CallExecutor<TBl, Blake2Hasher> + Send + Sync + Clone,
|
||||
TRtApi: 'static + Send + Sync,
|
||||
TSc: consensus_common::SelectChain<TBl> + 'static + Clone + Send,
|
||||
TExPoolApi: 'static + ChainApi<Block = TBl>,
|
||||
TExPool: 'static + TransactionPool<Block = TBl>
|
||||
+ TransactionPoolMaintainer<Block = TBl>,
|
||||
TOc: 'static + Send + Sync,
|
||||
TNetSpec: NetworkSpecialization<TBl>,
|
||||
{
|
||||
@@ -217,7 +220,7 @@ where
|
||||
type CallExecutor = TExec;
|
||||
type RuntimeApi = TRtApi;
|
||||
type SelectChain = TSc;
|
||||
type TransactionPoolApi = TExPoolApi;
|
||||
type TransactionPool = TExPool;
|
||||
type NetworkSpecialization = TNetSpec;
|
||||
|
||||
fn telemetry_on_connect_stream(&self) -> mpsc::UnboundedReceiver<()> {
|
||||
@@ -282,7 +285,7 @@ where
|
||||
stream
|
||||
}
|
||||
|
||||
fn transaction_pool(&self) -> Arc<TransactionPool<Self::TransactionPoolApi>> {
|
||||
fn transaction_pool(&self) -> Arc<Self::TransactionPool> {
|
||||
self.transaction_pool.clone()
|
||||
}
|
||||
|
||||
@@ -589,35 +592,35 @@ pub struct TransactionPoolAdapter<C, P> {
|
||||
/// Get transactions for propagation.
|
||||
///
|
||||
/// Function extracted to simplify the test and prevent creating `ServiceFactory`.
|
||||
fn transactions_to_propagate<PoolApi, B, H, E>(pool: &TransactionPool<PoolApi>)
|
||||
fn transactions_to_propagate<Pool, B, H, E>(pool: &Pool)
|
||||
-> Vec<(H, B::Extrinsic)>
|
||||
where
|
||||
PoolApi: ChainApi<Block=B, Hash=H, Error=E>,
|
||||
Pool: TransactionPool<Block=B, Hash=H, Error=E>,
|
||||
B: BlockT,
|
||||
H: std::hash::Hash + Eq + sr_primitives::traits::Member + sr_primitives::traits::MaybeSerialize,
|
||||
E: txpool::error::IntoPoolError + From<txpool::error::Error>,
|
||||
E: IntoPoolError + From<txpool_api::error::Error>,
|
||||
{
|
||||
pool.ready()
|
||||
.filter(|t| t.is_propagateable())
|
||||
.map(|t| {
|
||||
let hash = t.hash.clone();
|
||||
let ex: B::Extrinsic = t.data.clone();
|
||||
let hash = t.hash().clone();
|
||||
let ex: B::Extrinsic = t.data().clone();
|
||||
(hash, ex)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl<B, H, C, PoolApi, E> network::TransactionPool<H, B> for
|
||||
TransactionPoolAdapter<C, TransactionPool<PoolApi>>
|
||||
impl<B, H, C, Pool, E> network::TransactionPool<H, B> for
|
||||
TransactionPoolAdapter<C, Pool>
|
||||
where
|
||||
C: network::ClientHandle<B> + Send + Sync,
|
||||
PoolApi: 'static + ChainApi<Block=B, Hash=H, Error=E>,
|
||||
Pool: 'static + TransactionPool<Block=B, Hash=H, Error=E>,
|
||||
B: BlockT,
|
||||
H: std::hash::Hash + Eq + sr_primitives::traits::Member + sr_primitives::traits::MaybeSerialize,
|
||||
E: txpool::error::IntoPoolError + From<txpool::error::Error>,
|
||||
E: 'static + IntoPoolError + From<txpool_api::error::Error>,
|
||||
{
|
||||
fn transactions(&self) -> Vec<(H, <B as BlockT>::Extrinsic)> {
|
||||
transactions_to_propagate(&self.pool)
|
||||
transactions_to_propagate(&*self.pool)
|
||||
}
|
||||
|
||||
fn hash_of(&self, transaction: &B::Extrinsic) -> H {
|
||||
@@ -647,7 +650,7 @@ where
|
||||
match import_result {
|
||||
Ok(_) => report_handle.report_peer(who, reputation_change_good),
|
||||
Err(e) => match e.into_pool_error() {
|
||||
Ok(txpool::error::Error::AlreadyImported(_)) => (),
|
||||
Ok(txpool_api::error::Error::AlreadyImported(_)) => (),
|
||||
Ok(e) => {
|
||||
report_handle.report_peer(who, reputation_change_bad);
|
||||
debug!("Error adding transaction to the pool: {:?}", e)
|
||||
@@ -679,16 +682,14 @@ mod tests {
|
||||
use consensus_common::SelectChain;
|
||||
use sr_primitives::traits::BlindCheckable;
|
||||
use substrate_test_runtime_client::{prelude::*, runtime::{Extrinsic, Transfer}};
|
||||
use txpool::{BasicPool, FullChainApi};
|
||||
|
||||
#[test]
|
||||
fn should_not_propagate_transactions_that_are_marked_as_such() {
|
||||
// given
|
||||
let (client, longest_chain) = TestClientBuilder::new().build_with_longest_chain();
|
||||
let client = Arc::new(client);
|
||||
let pool = Arc::new(TransactionPool::new(
|
||||
Default::default(),
|
||||
transaction_pool::FullChainApi::new(client.clone())
|
||||
));
|
||||
let pool = Arc::new(BasicPool::new(Default::default(), FullChainApi::new(client.clone())));
|
||||
let best = longest_chain.best_chain().unwrap();
|
||||
let transaction = Transfer {
|
||||
amount: 5,
|
||||
@@ -701,7 +702,7 @@ mod tests {
|
||||
assert_eq!(pool.status().ready, 2);
|
||||
|
||||
// when
|
||||
let transactions = transactions_to_propagate(&pool);
|
||||
let transactions = transactions_to_propagate(&*pool);
|
||||
|
||||
// then
|
||||
assert_eq!(transactions.len(), 1);
|
||||
|
||||
@@ -18,3 +18,4 @@ consensus = { package = "substrate-consensus-common", path = "../../../primitive
|
||||
client = { package = "substrate-client", path = "../../" }
|
||||
sr-primitives = { path = "../../../primitives/sr-primitives" }
|
||||
primitives = { package = "substrate-primitives", path = "../../../primitives/core" }
|
||||
txpool-api = { package = "sp-transaction-pool-api", path = "../../../primitives/transaction-pool" }
|
||||
|
||||
@@ -36,6 +36,7 @@ use service::{
|
||||
use network::{multiaddr, Multiaddr};
|
||||
use network::config::{NetworkConfiguration, TransportConfig, NodeKeyConfig, Secret, NonReservedPeerMode};
|
||||
use sr_primitives::{generic::BlockId, traits::Block as BlockT};
|
||||
use txpool_api::TransactionPool;
|
||||
|
||||
/// Maximum duration of single wait call.
|
||||
const MAX_WAIT_TIME: Duration = Duration::from_secs(60 * 3);
|
||||
|
||||
@@ -7,14 +7,17 @@ edition = "2018"
|
||||
[dependencies]
|
||||
codec = { package = "parity-scale-codec", version = "1.0.0" }
|
||||
derive_more = "0.99.2"
|
||||
futures = { version = "0.3.1", features = ["thread-pool"] }
|
||||
futures = { version = "0.3.1", features = ["compat", "compat"] }
|
||||
log = "0.4.8"
|
||||
parking_lot = "0.9.0"
|
||||
primitives = { package = "substrate-primitives", path = "../../primitives/core" }
|
||||
sr-api = { path = "../../primitives/sr-api" }
|
||||
sr-primitives = { path = "../../primitives/sr-primitives" }
|
||||
tx-runtime-api = { package = "substrate-transaction-pool-runtime-api", path = "../../primitives/transaction-pool/runtime-api" }
|
||||
txpool = { package = "sc-transaction-graph", path = "./graph" }
|
||||
txpool-api = { package = "sp-transaction-pool-api", path = "../../primitives/transaction-pool" }
|
||||
txpool-runtime-api = { package = "sp-transaction-pool-runtime-api", path = "../../primitives/transaction-pool/runtime-api" }
|
||||
client-api = { package = "substrate-client-api", path = "../api" }
|
||||
sp-blockchain = { path = "../../primitives/blockchain" }
|
||||
|
||||
[dev-dependencies]
|
||||
keyring = { package = "substrate-keyring", path = "../../primitives/keyring" }
|
||||
|
||||
@@ -12,6 +12,7 @@ parking_lot = "0.9.0"
|
||||
serde = { version = "1.0.101", features = ["derive"] }
|
||||
primitives = { package = "substrate-primitives", path = "../../../primitives/core" }
|
||||
sr-primitives = { path = "../../../primitives/sr-primitives" }
|
||||
txpool-api = { package = "sp-transaction-pool-api", path = "../../../primitives/transaction-pool" }
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.3.0"
|
||||
|
||||
@@ -48,8 +48,8 @@ fn to_tag(nonce: u64, from: AccountId) -> Tag {
|
||||
impl ChainApi for TestApi {
|
||||
type Block = Block;
|
||||
type Hash = H256;
|
||||
type Error = error::Error;
|
||||
type ValidationFuture = futures::future::Ready<error::Result<TransactionValidity>>;
|
||||
type Error = txpool_api::error::Error;
|
||||
type ValidationFuture = futures::future::Ready<txpool_api::error::Result<TransactionValidity>>;
|
||||
|
||||
fn validate_transaction(
|
||||
&self,
|
||||
|
||||
@@ -34,8 +34,8 @@ use sr_primitives::transaction_validity::{
|
||||
TransactionLongevity as Longevity,
|
||||
TransactionPriority as Priority,
|
||||
};
|
||||
use txpool_api::{error, PoolStatus, InPoolTransaction};
|
||||
|
||||
use crate::error;
|
||||
use crate::future::{FutureTransactions, WaitingTransaction};
|
||||
use crate::ready::ReadyTransactions;
|
||||
|
||||
@@ -104,13 +104,65 @@ pub struct Transaction<Hash, Extrinsic> {
|
||||
pub propagate: bool,
|
||||
}
|
||||
|
||||
impl<Hash, Extrinsic> Transaction<Hash, Extrinsic> {
|
||||
/// Returns `true` if the transaction should be propagated to other peers.
|
||||
pub fn is_propagateable(&self) -> bool {
|
||||
impl<Hash, Extrinsic> AsRef<Extrinsic> for Transaction<Hash, Extrinsic> {
|
||||
fn as_ref(&self) -> &Extrinsic {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<Hash, Extrinsic> InPoolTransaction for Transaction<Hash, Extrinsic> {
|
||||
type Transaction = Extrinsic;
|
||||
type Hash = Hash;
|
||||
|
||||
fn data(&self) -> &Extrinsic {
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn hash(&self) -> &Hash {
|
||||
&self.hash
|
||||
}
|
||||
|
||||
fn priority(&self) -> &Priority {
|
||||
&self.priority
|
||||
}
|
||||
|
||||
fn longevity(&self) ->&Longevity {
|
||||
&self.valid_till
|
||||
}
|
||||
|
||||
fn requires(&self) -> &[Tag] {
|
||||
&self.requires
|
||||
}
|
||||
|
||||
fn provides(&self) -> &[Tag] {
|
||||
&self.provides
|
||||
}
|
||||
|
||||
fn is_propagateable(&self) -> bool {
|
||||
self.propagate
|
||||
}
|
||||
}
|
||||
|
||||
impl<Hash: Clone, Extrinsic: Clone> Transaction<Hash, Extrinsic> {
|
||||
/// Explicit transaction clone.
|
||||
///
|
||||
/// Transaction should be cloned only if absolutely necessary && we want
|
||||
/// every reason to be commented. That's why we `Transaction` is not `Clone`,
|
||||
/// but there's explicit `duplicate` method.
|
||||
pub fn duplicate(&self) -> Self {
|
||||
Transaction {
|
||||
data: self.data.clone(),
|
||||
bytes: self.bytes.clone(),
|
||||
hash: self.hash.clone(),
|
||||
priority: self.priority.clone(),
|
||||
valid_till: self.valid_till.clone(),
|
||||
requires: self.requires.clone(),
|
||||
provides: self.provides.clone(),
|
||||
propagate: self.propagate,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Hash, Extrinsic> fmt::Debug for Transaction<Hash, Extrinsic> where
|
||||
Hash: fmt::Debug,
|
||||
Extrinsic: fmt::Debug,
|
||||
@@ -159,6 +211,7 @@ const RECENTLY_PRUNED_TAGS: usize = 2;
|
||||
/// required tags.
|
||||
#[derive(Debug)]
|
||||
pub struct BasePool<Hash: hash::Hash + Eq, Ex> {
|
||||
reject_future_transactions: bool,
|
||||
future: FutureTransactions<Hash, Ex>,
|
||||
ready: ReadyTransactions<Hash, Ex>,
|
||||
/// Store recently pruned tags (for last two invocations).
|
||||
@@ -169,18 +222,37 @@ pub struct BasePool<Hash: hash::Hash + Eq, Ex> {
|
||||
recently_pruned_index: usize,
|
||||
}
|
||||
|
||||
impl<Hash: hash::Hash + Eq, Ex> Default for BasePool<Hash, Ex> {
|
||||
impl<Hash: hash::Hash + Member + Serialize, Ex: std::fmt::Debug> Default for BasePool<Hash, Ex> {
|
||||
fn default() -> Self {
|
||||
Self::new(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Hash: hash::Hash + Member + Serialize, Ex: std::fmt::Debug> BasePool<Hash, Ex> {
|
||||
/// Create new pool given reject_future_transactions flag.
|
||||
pub fn new(reject_future_transactions: bool) -> Self {
|
||||
BasePool {
|
||||
reject_future_transactions,
|
||||
future: Default::default(),
|
||||
ready: Default::default(),
|
||||
recently_pruned: Default::default(),
|
||||
recently_pruned_index: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Hash: hash::Hash + Member + Serialize, Ex: ::std::fmt::Debug> BasePool<Hash, Ex> {
|
||||
/// Temporary enables future transactions, runs closure and then restores
|
||||
/// `reject_future_transactions` flag back to previous value.
|
||||
///
|
||||
/// The closure accepts the mutable reference to the pool and original value
|
||||
/// of the `reject_future_transactions` flag.
|
||||
pub(crate) fn with_futures_enabled<T>(&mut self, closure: impl FnOnce(&mut Self, bool) -> T) -> T {
|
||||
let previous = self.reject_future_transactions;
|
||||
self.reject_future_transactions = false;
|
||||
let return_value = closure(self, previous);
|
||||
self.reject_future_transactions = previous;
|
||||
return_value
|
||||
}
|
||||
|
||||
/// Imports transaction to the pool.
|
||||
///
|
||||
/// The pool consists of two parts: Future and Ready.
|
||||
@@ -206,6 +278,10 @@ impl<Hash: hash::Hash + Member + Serialize, Ex: ::std::fmt::Debug> BasePool<Hash
|
||||
|
||||
// If all tags are not satisfied import to future.
|
||||
if !tx.is_ready() {
|
||||
if self.reject_future_transactions {
|
||||
return Err(error::Error::RejectedFutureTransaction);
|
||||
}
|
||||
|
||||
let hash = tx.transaction.hash.clone();
|
||||
self.future.import(tx);
|
||||
return Ok(Imported::Future { hash });
|
||||
@@ -370,6 +446,11 @@ impl<Hash: hash::Hash + Member + Serialize, Ex: ::std::fmt::Debug> BasePool<Hash
|
||||
removed
|
||||
}
|
||||
|
||||
/// Removes and returns all transactions from the future queue.
|
||||
pub fn clear_future(&mut self) -> Vec<Arc<Transaction<Hash, Ex>>> {
|
||||
self.future.clear()
|
||||
}
|
||||
|
||||
/// Prunes transactions that provide given list of tags.
|
||||
///
|
||||
/// This will cause all transactions that provide these tags to be removed from the pool,
|
||||
@@ -385,7 +466,7 @@ impl<Hash: hash::Hash + Member + Serialize, Ex: ::std::fmt::Debug> BasePool<Hash
|
||||
|
||||
for tag in tags {
|
||||
// make sure to promote any future transactions that could be unlocked
|
||||
to_import.append(&mut self.future.satisfy_tags(::std::iter::once(&tag)));
|
||||
to_import.append(&mut self.future.satisfy_tags(std::iter::once(&tag)));
|
||||
// and actually prune transactions in ready queue
|
||||
pruned.append(&mut self.ready.prune_tags(tag.clone()));
|
||||
// store the tags for next submission
|
||||
@@ -413,8 +494,8 @@ impl<Hash: hash::Hash + Member + Serialize, Ex: ::std::fmt::Debug> BasePool<Hash
|
||||
}
|
||||
|
||||
/// Get pool status.
|
||||
pub fn status(&self) -> Status {
|
||||
Status {
|
||||
pub fn status(&self) -> PoolStatus {
|
||||
PoolStatus {
|
||||
ready: self.ready.len(),
|
||||
ready_bytes: self.ready.bytes(),
|
||||
future: self.future.len(),
|
||||
@@ -423,26 +504,6 @@ impl<Hash: hash::Hash + Member + Serialize, Ex: ::std::fmt::Debug> BasePool<Hash
|
||||
}
|
||||
}
|
||||
|
||||
/// Pool status
|
||||
#[derive(Debug)]
|
||||
pub struct Status {
|
||||
/// 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 Status {
|
||||
/// Returns true if the are no transactions in the pool.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.ready == 0 && self.future == 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Queue limits
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Limit {
|
||||
@@ -972,4 +1033,85 @@ requires: [03,02], provides: [04], data: [4]}".to_owned()
|
||||
propagate: false,
|
||||
}.is_propagateable(), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_reject_future_transactions() {
|
||||
// given
|
||||
let mut pool = pool();
|
||||
|
||||
// when
|
||||
pool.reject_future_transactions = true;
|
||||
|
||||
// then
|
||||
let err = pool.import(Transaction {
|
||||
data: vec![5u8],
|
||||
bytes: 1,
|
||||
hash: 5,
|
||||
priority: 5u64,
|
||||
valid_till: 64u64,
|
||||
requires: vec![vec![0]],
|
||||
provides: vec![],
|
||||
propagate: true,
|
||||
});
|
||||
|
||||
if let Err(error::Error::RejectedFutureTransaction) = err {
|
||||
} else {
|
||||
assert!(false, "Invalid error kind: {:?}", err);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_clear_future_queue() {
|
||||
// given
|
||||
let mut pool = pool();
|
||||
|
||||
// when
|
||||
pool.import(Transaction {
|
||||
data: vec![5u8],
|
||||
bytes: 1,
|
||||
hash: 5,
|
||||
priority: 5u64,
|
||||
valid_till: 64u64,
|
||||
requires: vec![vec![0]],
|
||||
provides: vec![],
|
||||
propagate: true,
|
||||
}).unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(pool.future.len(), 1);
|
||||
|
||||
// and then when
|
||||
assert_eq!(pool.clear_future().len(), 1);
|
||||
|
||||
// then
|
||||
assert_eq!(pool.future.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_accept_future_transactions_when_explcitly_asked_to() {
|
||||
// given
|
||||
let mut pool = pool();
|
||||
pool.reject_future_transactions = true;
|
||||
|
||||
// when
|
||||
let flag_value = pool.with_futures_enabled(|pool, flag| {
|
||||
pool.import(Transaction {
|
||||
data: vec![5u8],
|
||||
bytes: 1,
|
||||
hash: 5,
|
||||
priority: 5u64,
|
||||
valid_till: 64u64,
|
||||
requires: vec![vec![0]],
|
||||
provides: vec![],
|
||||
propagate: true,
|
||||
}).unwrap();
|
||||
|
||||
flag
|
||||
});
|
||||
|
||||
// then
|
||||
assert_eq!(flag_value, true);
|
||||
assert_eq!(pool.reject_future_transactions, true);
|
||||
assert_eq!(pool.future.len(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,6 +227,12 @@ impl<Hash: hash::Hash + Eq + Clone, Ex> FutureTransactions<Hash, Ex> {
|
||||
self.waiting.values().map(|waiting| &*waiting.transaction)
|
||||
}
|
||||
|
||||
/// Removes and returns all future transactions.
|
||||
pub fn clear(&mut self) -> Vec<Arc<Transaction<Hash, Ex>>> {
|
||||
self.wanted_tags.clear();
|
||||
self.waiting.drain().map(|(_, tx)| tx.transaction).collect()
|
||||
}
|
||||
|
||||
/// Returns number of transactions in the Future queue.
|
||||
pub fn len(&self) -> usize {
|
||||
self.waiting.len()
|
||||
|
||||
@@ -32,11 +32,9 @@ mod rotator;
|
||||
mod validated_pool;
|
||||
|
||||
pub mod base_pool;
|
||||
pub mod error;
|
||||
pub mod watcher;
|
||||
|
||||
pub use self::error::IntoPoolError;
|
||||
pub use self::base_pool::{Transaction, Status};
|
||||
pub use self::base_pool::Transaction;
|
||||
pub use self::pool::{
|
||||
Pool,
|
||||
Options, ChainApi, EventStream, ExtrinsicFor,
|
||||
|
||||
@@ -92,7 +92,7 @@ impl<H: hash::Hash + traits::Member + Serialize, H2: Clone + fmt::Debug> Listene
|
||||
|
||||
/// Transaction was removed as invalid.
|
||||
pub fn invalid(&mut self, tx: &H) {
|
||||
warn!(target: "transaction-pool", "Extrinsic invalid: {:?}", tx);
|
||||
warn!(target: "txpool", "Extrinsic invalid: {:?}", tx);
|
||||
self.fire(tx, |watcher| watcher.invalid());
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ use std::{
|
||||
};
|
||||
|
||||
use crate::base_pool as base;
|
||||
use crate::error;
|
||||
use crate::watcher::Watcher;
|
||||
use serde::Serialize;
|
||||
|
||||
@@ -35,6 +34,8 @@ use sr_primitives::{
|
||||
traits::{self, SaturatedConversion},
|
||||
transaction_validity::{TransactionValidity, TransactionTag as Tag, TransactionValidityError},
|
||||
};
|
||||
use txpool_api::{error, PoolStatus};
|
||||
|
||||
use crate::validated_pool::{ValidatedPool, ValidatedTransaction};
|
||||
|
||||
/// Modification notification event stream type;
|
||||
@@ -92,6 +93,8 @@ pub struct Options {
|
||||
pub ready: base::Limit,
|
||||
/// Future queue limits.
|
||||
pub future: base::Limit,
|
||||
/// Reject future transactions.
|
||||
pub reject_future_transactions: bool,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
@@ -105,6 +108,7 @@ impl Default for Options {
|
||||
count: 128,
|
||||
total_bytes: 1 * 1024 * 1024,
|
||||
},
|
||||
reject_future_transactions: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,7 +135,9 @@ impl<B: ChainApi> Pool<B> {
|
||||
let validated_pool = self.validated_pool.clone();
|
||||
self.verify(at, xts, force)
|
||||
.map(move |validated_transactions| validated_transactions
|
||||
.map(|validated_transactions| validated_pool.submit(validated_transactions)))
|
||||
.map(|validated_transactions| validated_pool.submit(validated_transactions
|
||||
.into_iter()
|
||||
.map(|(_, tx)| tx))))
|
||||
}
|
||||
|
||||
/// Imports one unverified extrinsic to the pool
|
||||
@@ -161,10 +167,40 @@ impl<B: ChainApi> Pool<B> {
|
||||
let validated_pool = self.validated_pool.clone();
|
||||
Either::Right(
|
||||
self.verify_one(at, block_number, xt, false)
|
||||
.map(move |validated_transactions| validated_pool.submit_and_watch(validated_transactions))
|
||||
.map(move |validated_transactions| validated_pool.submit_and_watch(validated_transactions.1))
|
||||
)
|
||||
}
|
||||
|
||||
/// Revalidate all ready transactions.
|
||||
///
|
||||
/// Returns future that performs validation of all ready transactions and
|
||||
/// then resubmits all transactions back to the pool.
|
||||
pub fn revalidate_ready(&self, at: &BlockId<B::Block>) -> impl Future<Output=Result<(), B::Error>> {
|
||||
let validated_pool = self.validated_pool.clone();
|
||||
let ready = self.validated_pool.ready().map(|tx| tx.data.clone());
|
||||
self.verify(at, ready, false)
|
||||
.map(move |revalidated_transactions| revalidated_transactions.map(
|
||||
move |revalidated_transactions| validated_pool.resubmit(revalidated_transactions)
|
||||
))
|
||||
}
|
||||
|
||||
/// Prunes known ready transactions.
|
||||
///
|
||||
/// Used to clear the pool from transactions that were part of recently imported block.
|
||||
/// The main difference from the `prune` is that we do not revalidate any transactions
|
||||
/// and ignore unknown passed hashes.
|
||||
pub fn prune_known(&self, at: &BlockId<B::Block>, hashes: &[ExHash<B>]) -> Result<(), B::Error> {
|
||||
// Get details of all extrinsics that are already in the pool
|
||||
let in_pool_tags = self.validated_pool.extrinsics_tags(hashes)
|
||||
.into_iter().filter_map(|x| x).flat_map(|x| x);
|
||||
|
||||
// Prune all transactions that provide given tags
|
||||
let prune_status = self.validated_pool.prune_tags(in_pool_tags)?;
|
||||
let pruned_transactions = hashes.into_iter().cloned()
|
||||
.chain(prune_status.pruned.iter().map(|tx| tx.hash.clone()));
|
||||
self.validated_pool.fire_pruned(at, pruned_transactions)
|
||||
}
|
||||
|
||||
/// Prunes ready transactions.
|
||||
///
|
||||
/// Used to clear the pool from transactions that were part of recently imported block.
|
||||
@@ -184,7 +220,8 @@ impl<B: ChainApi> Pool<B> {
|
||||
extrinsics.len()
|
||||
);
|
||||
// Get details of all extrinsics that are already in the pool
|
||||
let (in_pool_hashes, in_pool_tags) = self.validated_pool.extrinsics_tags(extrinsics);
|
||||
let in_pool_hashes = extrinsics.iter().map(|extrinsic| self.hash_of(extrinsic)).collect::<Vec<_>>();
|
||||
let in_pool_tags = self.validated_pool.extrinsics_tags(&in_pool_hashes);
|
||||
|
||||
// Zip the ones from the pool with the full list (we get pairs `(Extrinsic, Option<Vec<Tag>>)`)
|
||||
let all = extrinsics.iter().zip(in_pool_tags.into_iter());
|
||||
@@ -274,7 +311,7 @@ impl<B: ChainApi> Pool<B> {
|
||||
&at,
|
||||
known_imported_hashes,
|
||||
pruned_hashes,
|
||||
reverified_transactions,
|
||||
reverified_transactions.into_iter().map(|(_, xt)| xt).collect(),
|
||||
))
|
||||
)))
|
||||
}
|
||||
@@ -303,7 +340,7 @@ impl<B: ChainApi> Pool<B> {
|
||||
}
|
||||
|
||||
/// Returns pool status.
|
||||
pub fn status(&self) -> base::Status {
|
||||
pub fn status(&self) -> PoolStatus {
|
||||
self.validated_pool.status()
|
||||
}
|
||||
|
||||
@@ -325,7 +362,7 @@ impl<B: ChainApi> Pool<B> {
|
||||
at: &BlockId<B::Block>,
|
||||
xts: impl IntoIterator<Item=ExtrinsicFor<B>>,
|
||||
force: bool,
|
||||
) -> impl Future<Output=Result<Vec<ValidatedTransactionFor<B>>, B::Error>> {
|
||||
) -> impl Future<Output=Result<HashMap<ExHash<B>, ValidatedTransactionFor<B>>, B::Error>> {
|
||||
// we need a block number to compute tx validity
|
||||
let block_number = match self.resolve_block_number(at) {
|
||||
Ok(block_number) => block_number,
|
||||
@@ -338,7 +375,7 @@ impl<B: ChainApi> Pool<B> {
|
||||
);
|
||||
|
||||
// make single validation future that waits all until all extrinsics are validated
|
||||
Either::Right(join_all(validation_futures).then(|x| ready(Ok(x))))
|
||||
Either::Right(join_all(validation_futures).then(|x| ready(Ok(x.into_iter().collect()))))
|
||||
}
|
||||
|
||||
/// Returns future that validates single transaction at given block.
|
||||
@@ -348,14 +385,17 @@ impl<B: ChainApi> Pool<B> {
|
||||
block_number: NumberFor<B>,
|
||||
xt: ExtrinsicFor<B>,
|
||||
force: bool,
|
||||
) -> impl Future<Output=ValidatedTransactionFor<B>> {
|
||||
) -> impl Future<Output=(ExHash<B>, ValidatedTransactionFor<B>)> {
|
||||
let (hash, bytes) = self.validated_pool.api().hash_and_length(&xt);
|
||||
if !force && self.validated_pool.is_banned(&hash) {
|
||||
return Either::Left(ready(ValidatedTransaction::Invalid(hash, error::Error::TemporarilyBanned.into())))
|
||||
return Either::Left(ready((
|
||||
hash.clone(),
|
||||
ValidatedTransaction::Invalid(hash, error::Error::TemporarilyBanned.into()),
|
||||
)))
|
||||
}
|
||||
|
||||
Either::Right(self.validated_pool.api().validate_transaction(block_id, xt.clone())
|
||||
.then(move |validation_result| ready(match validation_result {
|
||||
.then(move |validation_result| ready((hash.clone(), match validation_result {
|
||||
Ok(validity) => match validity {
|
||||
Ok(validity) => if validity.provides.is_empty() {
|
||||
ValidatedTransaction::Invalid(hash, error::Error::NoTagsProvided.into())
|
||||
@@ -379,7 +419,7 @@ impl<B: ChainApi> Pool<B> {
|
||||
ValidatedTransaction::Unknown(hash, error::Error::UnknownTransaction(e).into()),
|
||||
},
|
||||
Err(e) => ValidatedTransaction::Invalid(hash, e),
|
||||
})))
|
||||
}))))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,50 +431,30 @@ impl<B: ChainApi> Clone for Pool<B> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: ChainApi> sr_primitives::offchain::TransactionPool<A::Block> for Pool<A> {
|
||||
fn submit_at(
|
||||
&self,
|
||||
at: &BlockId<A::Block>,
|
||||
extrinsic: <A::Block as sr_primitives::traits::Block>::Extrinsic,
|
||||
) -> Result<(), ()> {
|
||||
log::debug!(
|
||||
target: "txpool",
|
||||
"(offchain call) Submitting a transaction to the pool: {:?}",
|
||||
extrinsic
|
||||
);
|
||||
|
||||
let result = futures::executor::block_on(self.submit_one(&at, extrinsic));
|
||||
|
||||
result.map(|_| ())
|
||||
.map_err(|e| log::warn!(
|
||||
target: "txpool",
|
||||
"(offchain call) Error submitting a transaction to the pool: {:?}",
|
||||
e
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
collections::{HashMap, HashSet},
|
||||
time::Instant,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use futures::executor::block_on;
|
||||
use super::*;
|
||||
use txpool_api::TransactionStatus;
|
||||
use sr_primitives::transaction_validity::{ValidTransaction, InvalidTransaction};
|
||||
use codec::Encode;
|
||||
use test_runtime::{Block, Extrinsic, Transfer, H256, AccountId};
|
||||
use assert_matches::assert_matches;
|
||||
use crate::base_pool::Limit;
|
||||
use crate::watcher;
|
||||
|
||||
const INVALID_NONCE: u64 = 254;
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct TestApi {
|
||||
delay: Arc<Mutex<Option<std::sync::mpsc::Receiver<()>>>>,
|
||||
invalidate: Arc<Mutex<HashSet<u64>>>,
|
||||
clear_requirements: Arc<Mutex<HashSet<u64>>>,
|
||||
add_requirements: Arc<Mutex<HashSet<u64>>>,
|
||||
}
|
||||
|
||||
impl ChainApi for TestApi {
|
||||
@@ -449,6 +469,7 @@ mod tests {
|
||||
at: &BlockId<Self::Block>,
|
||||
uxt: ExtrinsicFor<Self>,
|
||||
) -> Self::ValidationFuture {
|
||||
let hash = self.hash_and_length(&uxt).0;
|
||||
let block_number = self.block_id_to_number(at).unwrap().unwrap();
|
||||
let nonce = uxt.transfer().nonce;
|
||||
|
||||
@@ -462,16 +483,30 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
if self.invalidate.lock().contains(&hash) {
|
||||
return futures::future::ready(Ok(InvalidTransaction::Custom(0).into()));
|
||||
}
|
||||
|
||||
futures::future::ready(if nonce < block_number {
|
||||
Ok(InvalidTransaction::Stale.into())
|
||||
} else {
|
||||
Ok(Ok(ValidTransaction {
|
||||
let mut transaction = ValidTransaction {
|
||||
priority: 4,
|
||||
requires: if nonce > block_number { vec![vec![nonce as u8 - 1]] } else { vec![] },
|
||||
provides: if nonce == INVALID_NONCE { vec![] } else { vec![vec![nonce as u8]] },
|
||||
longevity: 3,
|
||||
propagate: true,
|
||||
}))
|
||||
};
|
||||
|
||||
if self.clear_requirements.lock().contains(&hash) {
|
||||
transaction.requires.clear();
|
||||
}
|
||||
|
||||
if self.add_requirements.lock().contains(&hash) {
|
||||
transaction.requires.push(vec![128]);
|
||||
}
|
||||
|
||||
Ok(Ok(transaction))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -651,6 +686,7 @@ mod tests {
|
||||
let pool = Pool::new(Options {
|
||||
ready: limit.clone(),
|
||||
future: limit.clone(),
|
||||
..Default::default()
|
||||
}, TestApi::default());
|
||||
|
||||
let hash1 = block_on(pool.submit_one(&BlockId::Number(0), uxt(Transfer {
|
||||
@@ -685,6 +721,7 @@ mod tests {
|
||||
let pool = Pool::new(Options {
|
||||
ready: limit.clone(),
|
||||
future: limit.clone(),
|
||||
..Default::default()
|
||||
}, TestApi::default());
|
||||
|
||||
// when
|
||||
@@ -742,8 +779,8 @@ mod tests {
|
||||
|
||||
// then
|
||||
let mut stream = futures::executor::block_on_stream(watcher.into_stream());
|
||||
assert_eq!(stream.next(), Some(watcher::Status::Ready));
|
||||
assert_eq!(stream.next(), Some(watcher::Status::Finalized(H256::from_low_u64_be(2).into())));
|
||||
assert_eq!(stream.next(), Some(TransactionStatus::Ready));
|
||||
assert_eq!(stream.next(), Some(TransactionStatus::Finalized(H256::from_low_u64_be(2).into())));
|
||||
assert_eq!(stream.next(), None);
|
||||
}
|
||||
|
||||
@@ -767,8 +804,8 @@ mod tests {
|
||||
|
||||
// then
|
||||
let mut stream = futures::executor::block_on_stream(watcher.into_stream());
|
||||
assert_eq!(stream.next(), Some(watcher::Status::Ready));
|
||||
assert_eq!(stream.next(), Some(watcher::Status::Finalized(H256::from_low_u64_be(2).into())));
|
||||
assert_eq!(stream.next(), Some(TransactionStatus::Ready));
|
||||
assert_eq!(stream.next(), Some(TransactionStatus::Finalized(H256::from_low_u64_be(2).into())));
|
||||
assert_eq!(stream.next(), None);
|
||||
}
|
||||
|
||||
@@ -796,8 +833,8 @@ mod tests {
|
||||
|
||||
// then
|
||||
let mut stream = futures::executor::block_on_stream(watcher.into_stream());
|
||||
assert_eq!(stream.next(), Some(watcher::Status::Future));
|
||||
assert_eq!(stream.next(), Some(watcher::Status::Ready));
|
||||
assert_eq!(stream.next(), Some(TransactionStatus::Future));
|
||||
assert_eq!(stream.next(), Some(TransactionStatus::Ready));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -819,8 +856,8 @@ mod tests {
|
||||
|
||||
// then
|
||||
let mut stream = futures::executor::block_on_stream(watcher.into_stream());
|
||||
assert_eq!(stream.next(), Some(watcher::Status::Ready));
|
||||
assert_eq!(stream.next(), Some(watcher::Status::Invalid));
|
||||
assert_eq!(stream.next(), Some(TransactionStatus::Ready));
|
||||
assert_eq!(stream.next(), Some(TransactionStatus::Invalid));
|
||||
assert_eq!(stream.next(), None);
|
||||
}
|
||||
|
||||
@@ -846,8 +883,8 @@ mod tests {
|
||||
|
||||
// then
|
||||
let mut stream = futures::executor::block_on_stream(watcher.into_stream());
|
||||
assert_eq!(stream.next(), Some(watcher::Status::Ready));
|
||||
assert_eq!(stream.next(), Some(watcher::Status::Broadcast(peers)));
|
||||
assert_eq!(stream.next(), Some(TransactionStatus::Ready));
|
||||
assert_eq!(stream.next(), Some(TransactionStatus::Broadcast(peers)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -860,6 +897,7 @@ mod tests {
|
||||
let pool = Pool::new(Options {
|
||||
ready: limit.clone(),
|
||||
future: limit.clone(),
|
||||
..Default::default()
|
||||
}, TestApi::default());
|
||||
|
||||
let xt = uxt(Transfer {
|
||||
@@ -883,8 +921,8 @@ mod tests {
|
||||
|
||||
// then
|
||||
let mut stream = futures::executor::block_on_stream(watcher.into_stream());
|
||||
assert_eq!(stream.next(), Some(watcher::Status::Ready));
|
||||
assert_eq!(stream.next(), Some(watcher::Status::Dropped));
|
||||
assert_eq!(stream.next(), Some(TransactionStatus::Ready));
|
||||
assert_eq!(stream.next(), Some(TransactionStatus::Dropped));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -941,5 +979,81 @@ mod tests {
|
||||
assert_eq!(pool.status().future, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_revalidate_ready_transactions() {
|
||||
fn transfer(nonce: u64) -> Extrinsic {
|
||||
uxt(Transfer {
|
||||
from: AccountId::from_h256(H256::from_low_u64_be(1)),
|
||||
to: AccountId::from_h256(H256::from_low_u64_be(2)),
|
||||
amount: 5,
|
||||
nonce,
|
||||
})
|
||||
}
|
||||
|
||||
// given
|
||||
let pool = pool();
|
||||
let tx0 = transfer(0);
|
||||
let hash0 = pool.validated_pool.api().hash_and_length(&tx0).0;
|
||||
let watcher0 = block_on(pool.submit_and_watch(&BlockId::Number(0), tx0)).unwrap();
|
||||
let tx1 = transfer(1);
|
||||
let hash1 = pool.validated_pool.api().hash_and_length(&tx1).0;
|
||||
let watcher1 = block_on(pool.submit_and_watch(&BlockId::Number(0), tx1)).unwrap();
|
||||
let tx2 = transfer(2);
|
||||
let hash2 = pool.validated_pool.api().hash_and_length(&tx2).0;
|
||||
let watcher2 = block_on(pool.submit_and_watch(&BlockId::Number(0), tx2)).unwrap();
|
||||
let tx3 = transfer(3);
|
||||
let hash3 = pool.validated_pool.api().hash_and_length(&tx3).0;
|
||||
let watcher3 = block_on(pool.submit_and_watch(&BlockId::Number(0), tx3)).unwrap();
|
||||
let tx4 = transfer(4);
|
||||
let hash4 = pool.validated_pool.api().hash_and_length(&tx4).0;
|
||||
let watcher4 = block_on(pool.submit_and_watch(&BlockId::Number(0), tx4)).unwrap();
|
||||
assert_eq!(pool.status().ready, 5);
|
||||
|
||||
// when
|
||||
pool.validated_pool.api().invalidate.lock().insert(hash3);
|
||||
pool.validated_pool.api().clear_requirements.lock().insert(hash1);
|
||||
pool.validated_pool.api().add_requirements.lock().insert(hash0);
|
||||
block_on(pool.revalidate_ready(&BlockId::Number(0))).unwrap();
|
||||
|
||||
// then
|
||||
// hash0 now has unsatisfied requirements => it is moved to the future queue
|
||||
// hash1 is now independent of hash0 => it is in ready queue
|
||||
// hash2 still depends on hash1 => it is in ready queue
|
||||
// hash3 is now invalid => it is removed from the pool
|
||||
// hash4 now depends on invalidated hash3 => it is moved to the future queue
|
||||
//
|
||||
// events for hash3 are: Ready, Invalid
|
||||
// events for hash4 are: Ready, Invalid
|
||||
assert_eq!(pool.status().ready, 2);
|
||||
assert_eq!(
|
||||
futures::executor::block_on_stream(watcher3.into_stream()).collect::<Vec<_>>(),
|
||||
vec![TransactionStatus::Ready, TransactionStatus::Invalid],
|
||||
);
|
||||
|
||||
// when
|
||||
pool.validated_pool.remove_invalid(&[hash0, hash1, hash2, hash4]);
|
||||
|
||||
// then
|
||||
// events for hash0 are: Ready, Future, Invalid
|
||||
// events for hash1 are: Ready, Invalid
|
||||
// events for hash2 are: Ready, Invalid
|
||||
assert_eq!(
|
||||
futures::executor::block_on_stream(watcher0.into_stream()).collect::<Vec<_>>(),
|
||||
vec![TransactionStatus::Ready, TransactionStatus::Future, TransactionStatus::Invalid],
|
||||
);
|
||||
assert_eq!(
|
||||
futures::executor::block_on_stream(watcher1.into_stream()).collect::<Vec<_>>(),
|
||||
vec![TransactionStatus::Ready, TransactionStatus::Invalid],
|
||||
);
|
||||
assert_eq!(
|
||||
futures::executor::block_on_stream(watcher2.into_stream()).collect::<Vec<_>>(),
|
||||
vec![TransactionStatus::Ready, TransactionStatus::Invalid],
|
||||
);
|
||||
assert_eq!(
|
||||
futures::executor::block_on_stream(watcher4.into_stream()).collect::<Vec<_>>(),
|
||||
vec![TransactionStatus::Ready, TransactionStatus::Future, TransactionStatus::Invalid],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ use sr_primitives::traits::Member;
|
||||
use sr_primitives::transaction_validity::{
|
||||
TransactionTag as Tag,
|
||||
};
|
||||
use txpool_api::error;
|
||||
|
||||
use crate::error;
|
||||
use crate::future::WaitingTransaction;
|
||||
use crate::base_pool::Transaction;
|
||||
|
||||
@@ -433,6 +433,7 @@ impl<Hash: hash::Hash + Member + Serialize, Ex> ReadyTransactions<Hash, Ex> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator of ready transactions ordered by priority.
|
||||
pub struct BestIterator<Hash, Ex> {
|
||||
all: Arc<RwLock<HashMap<Hash, ReadyTx<Hash, Ex>>>>,
|
||||
awaiting: HashMap<Hash, (usize, TransactionRef<Hash, Ex>)>,
|
||||
|
||||
@@ -18,16 +18,16 @@ use std::{
|
||||
collections::{HashSet, HashMap},
|
||||
fmt,
|
||||
hash,
|
||||
sync::Arc,
|
||||
time,
|
||||
};
|
||||
|
||||
use crate::base_pool as base;
|
||||
use crate::error;
|
||||
use crate::listener::Listener;
|
||||
use crate::rotator::PoolRotator;
|
||||
use crate::watcher::Watcher;
|
||||
use serde::Serialize;
|
||||
use log::debug;
|
||||
use log::{debug, warn};
|
||||
|
||||
use futures::channel::mpsc;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
@@ -36,6 +36,7 @@ use sr_primitives::{
|
||||
traits::{self, SaturatedConversion},
|
||||
transaction_validity::TransactionTag as Tag,
|
||||
};
|
||||
use txpool_api::{error, PoolStatus};
|
||||
|
||||
use crate::base_pool::PruneStatus;
|
||||
use crate::pool::{EventStream, Options, ChainApi, BlockHash, ExHash, ExtrinsicFor, TransactionFor};
|
||||
@@ -76,11 +77,12 @@ pub(crate) struct ValidatedPool<B: ChainApi> {
|
||||
impl<B: ChainApi> ValidatedPool<B> {
|
||||
/// Create a new transaction pool.
|
||||
pub fn new(options: Options, api: B) -> Self {
|
||||
let base_pool = base::BasePool::new(options.reject_future_transactions);
|
||||
ValidatedPool {
|
||||
api,
|
||||
options,
|
||||
listener: Default::default(),
|
||||
pool: Default::default(),
|
||||
pool: RwLock::new(base_pool),
|
||||
import_notification_sinks: Default::default(),
|
||||
rotator: Default::default(),
|
||||
}
|
||||
@@ -189,18 +191,134 @@ impl<B: ChainApi> ValidatedPool<B> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Resubmits revalidated transactions back to the pool.
|
||||
///
|
||||
/// Removes and then submits passed transactions and all dependent transactions.
|
||||
/// Transactions that are missing from the pool are not submitted.
|
||||
pub fn resubmit(&self, mut updated_transactions: HashMap<ExHash<B>, ValidatedTransactionFor<B>>) {
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
enum Status { Future, Ready, Failed, Dropped };
|
||||
|
||||
let (mut initial_statuses, final_statuses) = {
|
||||
let mut pool = self.pool.write();
|
||||
|
||||
// remove all passed transactions from the ready/future queues
|
||||
// (this may remove additional transactions as well)
|
||||
//
|
||||
// for every transaction that has an entry in the `updated_transactions`,
|
||||
// we store updated validation result in txs_to_resubmit
|
||||
// for every transaction that has no entry in the `updated_transactions`,
|
||||
// we store last validation result (i.e. the pool entry) in txs_to_resubmit
|
||||
let mut initial_statuses = HashMap::new();
|
||||
let mut txs_to_resubmit = Vec::with_capacity(updated_transactions.len());
|
||||
while !updated_transactions.is_empty() {
|
||||
let hash = updated_transactions.keys().next().cloned().expect("transactions is not empty; qed");
|
||||
|
||||
// note we are not considering tx with hash invalid here - we just want
|
||||
// to remove it along with dependent transactions and `remove_invalid()`
|
||||
// does exactly what we need
|
||||
let removed = pool.remove_invalid(&[hash.clone()]);
|
||||
for removed_tx in removed {
|
||||
let removed_hash = removed_tx.hash.clone();
|
||||
let updated_transaction = updated_transactions.remove(&removed_hash);
|
||||
let tx_to_resubmit = if let Some(updated_tx) = updated_transaction {
|
||||
updated_tx
|
||||
} else {
|
||||
// in most cases we'll end up in successful `try_unwrap`, but if not
|
||||
// we still need to reinsert transaction back to the pool => duplicate call
|
||||
let transaction = match Arc::try_unwrap(removed_tx) {
|
||||
Ok(transaction) => transaction,
|
||||
Err(transaction) => transaction.duplicate(),
|
||||
};
|
||||
ValidatedTransaction::Valid(transaction)
|
||||
};
|
||||
|
||||
initial_statuses.insert(removed_hash.clone(), Status::Ready);
|
||||
txs_to_resubmit.push((removed_hash, tx_to_resubmit));
|
||||
}
|
||||
}
|
||||
|
||||
// if we're rejecting future transactions, then insertion order matters here:
|
||||
// if tx1 depends on tx2, then if tx1 is inserted before tx2, then it goes
|
||||
// to the future queue and gets rejected immediately
|
||||
// => let's temporary stop rejection and clear future queue before return
|
||||
pool.with_futures_enabled(|pool, reject_future_transactions| {
|
||||
// now resubmit all removed transactions back to the pool
|
||||
let mut final_statuses = HashMap::new();
|
||||
for (hash, tx_to_resubmit) in txs_to_resubmit {
|
||||
match tx_to_resubmit {
|
||||
ValidatedTransaction::Valid(tx) => match pool.import(tx) {
|
||||
Ok(imported) => match imported {
|
||||
base::Imported::Ready { promoted, failed, removed, .. } => {
|
||||
final_statuses.insert(hash, Status::Ready);
|
||||
for hash in promoted {
|
||||
final_statuses.insert(hash, Status::Ready);
|
||||
}
|
||||
for hash in failed {
|
||||
final_statuses.insert(hash, Status::Failed);
|
||||
}
|
||||
for tx in removed {
|
||||
final_statuses.insert(tx.hash.clone(), Status::Dropped);
|
||||
}
|
||||
},
|
||||
base::Imported::Future { .. } => {
|
||||
final_statuses.insert(hash, Status::Future);
|
||||
},
|
||||
},
|
||||
Err(err) => {
|
||||
// we do not want to fail if single transaction import has failed
|
||||
// nor we do want to propagate this error, because it could tx unknown to caller
|
||||
// => let's just notify listeners (and issue debug message)
|
||||
warn!(
|
||||
target: "txpool",
|
||||
"[{:?}] Removing invalid transaction from update: {}",
|
||||
hash,
|
||||
err,
|
||||
);
|
||||
final_statuses.insert(hash, Status::Failed);
|
||||
},
|
||||
},
|
||||
ValidatedTransaction::Invalid(_, _) | ValidatedTransaction::Unknown(_, _) => {
|
||||
final_statuses.insert(hash, Status::Failed);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// if the pool is configured to reject future transactions, let's clear the future
|
||||
// queue, updating final statuses as required
|
||||
if reject_future_transactions {
|
||||
for future_tx in pool.clear_future() {
|
||||
final_statuses.insert(future_tx.hash.clone(), Status::Dropped);
|
||||
}
|
||||
}
|
||||
|
||||
(initial_statuses, final_statuses)
|
||||
})
|
||||
};
|
||||
|
||||
// and now let's notify listeners about status changes
|
||||
let mut listener = self.listener.write();
|
||||
for (hash, final_status) in final_statuses {
|
||||
let initial_status = initial_statuses.remove(&hash);
|
||||
if initial_status.is_none() || Some(final_status) != initial_status {
|
||||
match final_status {
|
||||
Status::Future => listener.future(&hash),
|
||||
Status::Ready => listener.ready(&hash, None),
|
||||
Status::Failed => listener.invalid(&hash),
|
||||
Status::Dropped => listener.dropped(&hash, None),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// For each extrinsic, returns tags that it provides (if known), or None (if it is unknown).
|
||||
pub fn extrinsics_tags(&self, extrinsics: &[ExtrinsicFor<B>]) -> (Vec<ExHash<B>>, Vec<Option<Vec<Tag>>>) {
|
||||
let hashes = extrinsics.iter().map(|extrinsic| self.api.hash_and_length(extrinsic).0).collect::<Vec<_>>();
|
||||
let in_pool = self.pool.read().by_hash(&hashes);
|
||||
(
|
||||
hashes,
|
||||
in_pool.into_iter()
|
||||
.map(|existing_in_pool| existing_in_pool
|
||||
.map(|transaction| transaction.provides.iter().cloned()
|
||||
.collect()))
|
||||
.collect(),
|
||||
)
|
||||
pub fn extrinsics_tags(&self, hashes: &[ExHash<B>]) -> Vec<Option<Vec<Tag>>> {
|
||||
self.pool.read().by_hash(&hashes)
|
||||
.into_iter()
|
||||
.map(|existing_in_pool| existing_in_pool
|
||||
.map(|transaction| transaction.provides.iter().cloned()
|
||||
.collect()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Prunes ready transactions that provide given list of tags.
|
||||
@@ -249,20 +367,29 @@ impl<B: ChainApi> ValidatedPool<B> {
|
||||
// Fire `pruned` notifications for collected hashes and make sure to include
|
||||
// `known_imported_hashes` since they were just imported as part of the block.
|
||||
let hashes = hashes.chain(known_imported_hashes.into_iter());
|
||||
{
|
||||
let header_hash = self.api.block_id_to_hash(at)?
|
||||
.ok_or_else(|| error::Error::InvalidBlockId(format!("{:?}", at)).into())?;
|
||||
let mut listener = self.listener.write();
|
||||
for h in hashes {
|
||||
listener.pruned(header_hash, &h);
|
||||
}
|
||||
}
|
||||
self.fire_pruned(at, hashes)?;
|
||||
|
||||
// perform regular cleanup of old transactions in the pool
|
||||
// and update temporary bans.
|
||||
self.clear_stale(at)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Fire notifications for pruned transactions.
|
||||
pub fn fire_pruned(
|
||||
&self,
|
||||
at: &BlockId<B::Block>,
|
||||
hashes: impl Iterator<Item=ExHash<B>>,
|
||||
) -> Result<(), B::Error> {
|
||||
let header_hash = self.api.block_id_to_hash(at)?
|
||||
.ok_or_else(|| error::Error::InvalidBlockId(format!("{:?}", at)).into())?;
|
||||
let mut listener = self.listener.write();
|
||||
for h in hashes {
|
||||
listener.pruned(header_hash, &h);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Removes stale transactions from the pool.
|
||||
///
|
||||
/// Stale transactions are transaction beyond their longevity period.
|
||||
@@ -270,8 +397,8 @@ impl<B: ChainApi> ValidatedPool<B> {
|
||||
/// See `prune_tags` if you want this.
|
||||
pub fn clear_stale(&self, at: &BlockId<B::Block>) -> Result<(), B::Error> {
|
||||
let block_number = self.api.block_id_to_number(at)?
|
||||
.ok_or_else(|| error::Error::InvalidBlockId(format!("{:?}", at)).into())?
|
||||
.saturated_into::<u64>();
|
||||
.ok_or_else(|| error::Error::InvalidBlockId(format!("{:?}", at)).into())?
|
||||
.saturated_into::<u64>();
|
||||
let now = time::Instant::now();
|
||||
let to_remove = {
|
||||
self.ready()
|
||||
@@ -346,7 +473,7 @@ impl<B: ChainApi> ValidatedPool<B> {
|
||||
}
|
||||
|
||||
/// Returns pool status.
|
||||
pub fn status(&self) -> base::Status {
|
||||
pub fn status(&self) -> PoolStatus {
|
||||
self.pool.read().status()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,34 +20,14 @@ use futures::{
|
||||
Stream,
|
||||
channel::mpsc,
|
||||
};
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
/// Possible extrinsic status events
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum Status<H, H2> {
|
||||
/// Extrinsic is part of the future queue.
|
||||
Future,
|
||||
/// Extrinsic is part of the ready queue.
|
||||
Ready,
|
||||
/// Extrinsic has been finalized in block with given hash.
|
||||
Finalized(H2),
|
||||
/// Some state change (perhaps another extrinsic was included) rendered this extrinsic invalid.
|
||||
Usurped(H),
|
||||
/// The extrinsic has been broadcast to the given peers.
|
||||
Broadcast(Vec<String>),
|
||||
/// Extrinsic has been dropped from the pool because of the limit.
|
||||
Dropped,
|
||||
/// Extrinsic was detected as invalid.
|
||||
Invalid,
|
||||
}
|
||||
use txpool_api::TransactionStatus;
|
||||
|
||||
/// Extrinsic watcher.
|
||||
///
|
||||
/// Represents a stream of status updates for particular extrinsic.
|
||||
#[derive(Debug)]
|
||||
pub struct Watcher<H, H2> {
|
||||
receiver: mpsc::UnboundedReceiver<Status<H, H2>>,
|
||||
receiver: mpsc::UnboundedReceiver<TransactionStatus<H, H2>>,
|
||||
hash: H,
|
||||
}
|
||||
|
||||
@@ -60,7 +40,7 @@ impl<H, H2> Watcher<H, H2> {
|
||||
/// Pipe the notifications to given sink.
|
||||
///
|
||||
/// Make sure to drive the future to completion.
|
||||
pub fn into_stream(self) -> impl Stream<Item=Status<H, H2>> {
|
||||
pub fn into_stream(self) -> impl Stream<Item=TransactionStatus<H, H2>> {
|
||||
self.receiver
|
||||
}
|
||||
}
|
||||
@@ -68,7 +48,7 @@ impl<H, H2> Watcher<H, H2> {
|
||||
/// Sender part of the watcher. Exposed only for testing purposes.
|
||||
#[derive(Debug)]
|
||||
pub struct Sender<H, H2> {
|
||||
receivers: Vec<mpsc::UnboundedSender<Status<H, H2>>>,
|
||||
receivers: Vec<mpsc::UnboundedSender<TransactionStatus<H, H2>>>,
|
||||
finalized: bool,
|
||||
}
|
||||
|
||||
@@ -94,49 +74,48 @@ impl<H: Clone, H2: Clone> Sender<H, H2> {
|
||||
|
||||
/// Transaction became ready.
|
||||
pub fn ready(&mut self) {
|
||||
self.send(Status::Ready)
|
||||
self.send(TransactionStatus::Ready)
|
||||
}
|
||||
|
||||
/// Transaction was moved to future.
|
||||
pub fn future(&mut self) {
|
||||
self.send(Status::Future)
|
||||
self.send(TransactionStatus::Future)
|
||||
}
|
||||
|
||||
/// Some state change (perhaps another extrinsic was included) rendered this extrinsic invalid.
|
||||
pub fn usurped(&mut self, hash: H) {
|
||||
self.send(Status::Usurped(hash))
|
||||
self.send(TransactionStatus::Usurped(hash))
|
||||
}
|
||||
|
||||
/// Extrinsic has been finalized in block with given hash.
|
||||
pub fn finalized(&mut self, hash: H2) {
|
||||
self.send(Status::Finalized(hash));
|
||||
self.send(TransactionStatus::Finalized(hash));
|
||||
self.finalized = true;
|
||||
}
|
||||
|
||||
/// Extrinsic has been marked as invalid by the block builder.
|
||||
pub fn invalid(&mut self) {
|
||||
self.send(Status::Invalid);
|
||||
self.send(TransactionStatus::Invalid);
|
||||
// we mark as finalized as there are no more notifications
|
||||
self.finalized = true;
|
||||
}
|
||||
|
||||
/// Transaction has been dropped from the pool because of the limit.
|
||||
pub fn dropped(&mut self) {
|
||||
self.send(Status::Dropped);
|
||||
self.send(TransactionStatus::Dropped);
|
||||
}
|
||||
|
||||
/// The extrinsic has been broadcast to the given peers.
|
||||
pub fn broadcast(&mut self, peers: Vec<String>) {
|
||||
self.send(Status::Broadcast(peers))
|
||||
self.send(TransactionStatus::Broadcast(peers))
|
||||
}
|
||||
|
||||
|
||||
/// Returns true if the are no more listeners for this extrinsic or it was finalized.
|
||||
pub fn is_done(&self) -> bool {
|
||||
self.finalized || self.receivers.is_empty()
|
||||
}
|
||||
|
||||
fn send(&mut self, status: Status<H, H2>) {
|
||||
fn send(&mut self, status: TransactionStatus<H, H2>) {
|
||||
self.receivers.retain(|sender| sender.unbounded_send(status.clone()).is_ok())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,20 +17,20 @@
|
||||
//! Chain api required for the transaction pool.
|
||||
|
||||
use std::{marker::PhantomData, pin::Pin, sync::Arc};
|
||||
use codec::{Decode, Encode};
|
||||
use futures::{channel::oneshot, executor::{ThreadPool, ThreadPoolBuilder}, future::{Future, FutureExt, ready}};
|
||||
|
||||
use codec::Encode;
|
||||
|
||||
use futures::{channel::oneshot, executor::{ThreadPool, ThreadPoolBuilder}, future::Future};
|
||||
|
||||
use client_api::{
|
||||
blockchain::HeaderBackend,
|
||||
light::{Fetcher, RemoteCallRequest}
|
||||
};
|
||||
use primitives::{H256, Blake2Hasher, Hasher};
|
||||
|
||||
use sr_primitives::{generic::BlockId, traits, transaction_validity::TransactionValidity};
|
||||
|
||||
use tx_runtime_api::TaggedTransactionQueue;
|
||||
use sr_primitives::{generic::BlockId, traits::{self, Block as BlockT}, transaction_validity::TransactionValidity};
|
||||
use txpool_runtime_api::TaggedTransactionQueue;
|
||||
|
||||
use crate::error::{self, Error};
|
||||
|
||||
/// The transaction pool logic
|
||||
/// The transaction pool logic for full client.
|
||||
pub struct FullChainApi<T, Block> {
|
||||
client: Arc<T>,
|
||||
pool: ThreadPool,
|
||||
@@ -38,7 +38,7 @@ pub struct FullChainApi<T, Block> {
|
||||
}
|
||||
|
||||
impl<T, Block> FullChainApi<T, Block> where
|
||||
Block: traits::Block,
|
||||
Block: BlockT,
|
||||
T: traits::ProvideRuntimeApi + traits::BlockIdTo<Block> {
|
||||
/// Create new transaction pool logic.
|
||||
pub fn new(client: Arc<T>) -> Self {
|
||||
@@ -55,7 +55,7 @@ impl<T, Block> FullChainApi<T, Block> where
|
||||
}
|
||||
|
||||
impl<T, Block> txpool::ChainApi for FullChainApi<T, Block> where
|
||||
Block: traits::Block<Hash = H256>,
|
||||
Block: BlockT<Hash = H256>,
|
||||
T: traits::ProvideRuntimeApi + traits::BlockIdTo<Block> + 'static + Send + Sync,
|
||||
T::Api: TaggedTransactionQueue<Block>,
|
||||
sr_api::ApiErrorFor<T, Block>: Send,
|
||||
@@ -110,3 +110,84 @@ impl<T, Block> txpool::ChainApi for FullChainApi<T, Block> where
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The transaction pool logic for light client.
|
||||
pub struct LightChainApi<T, F, Block> {
|
||||
client: Arc<T>,
|
||||
fetcher: Arc<F>,
|
||||
_phantom: PhantomData<Block>,
|
||||
}
|
||||
|
||||
impl<T, F, Block> LightChainApi<T, F, Block> where
|
||||
Block: BlockT,
|
||||
T: HeaderBackend<Block>,
|
||||
F: Fetcher<Block>,
|
||||
{
|
||||
/// Create new transaction pool logic.
|
||||
pub fn new(client: Arc<T>, fetcher: Arc<F>) -> Self {
|
||||
LightChainApi {
|
||||
client,
|
||||
fetcher,
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, F, Block> txpool::ChainApi for LightChainApi<T, F, Block> where
|
||||
Block: BlockT<Hash=H256>,
|
||||
T: HeaderBackend<Block> + 'static,
|
||||
F: Fetcher<Block> + 'static,
|
||||
{
|
||||
type Block = Block;
|
||||
type Hash = H256;
|
||||
type Error = error::Error;
|
||||
type ValidationFuture = Box<dyn Future<Output = error::Result<TransactionValidity>> + Send + Unpin>;
|
||||
|
||||
fn validate_transaction(
|
||||
&self,
|
||||
at: &BlockId<Self::Block>,
|
||||
uxt: txpool::ExtrinsicFor<Self>,
|
||||
) -> Self::ValidationFuture {
|
||||
let header_hash = self.client.expect_block_hash_from_id(at);
|
||||
let header_and_hash = header_hash
|
||||
.and_then(|header_hash| self.client.expect_header(BlockId::Hash(header_hash))
|
||||
.map(|header| (header_hash, header)));
|
||||
let (block, header) = match header_and_hash {
|
||||
Ok((header_hash, header)) => (header_hash, header),
|
||||
Err(err) => return Box::new(ready(Err(err.into()))),
|
||||
};
|
||||
let remote_validation_request = self.fetcher.remote_call(RemoteCallRequest {
|
||||
block,
|
||||
header,
|
||||
method: "TaggedTransactionQueue_validate_transaction".into(),
|
||||
call_data: uxt.encode(),
|
||||
retry_count: None,
|
||||
});
|
||||
let remote_validation_request = remote_validation_request.then(move |result| {
|
||||
let result: error::Result<TransactionValidity> = result
|
||||
.map_err(Into::into)
|
||||
.and_then(|result| Decode::decode(&mut &result[..])
|
||||
.map_err(|e| Error::RuntimeApi(
|
||||
format!("Error decoding tx validation result: {:?}", e)
|
||||
))
|
||||
);
|
||||
ready(result)
|
||||
});
|
||||
|
||||
Box::new(remote_validation_request)
|
||||
}
|
||||
|
||||
fn block_id_to_number(&self, at: &BlockId<Self::Block>) -> error::Result<Option<txpool::NumberFor<Self>>> {
|
||||
Ok(self.client.block_number_from_id(at)?)
|
||||
}
|
||||
|
||||
fn block_id_to_hash(&self, at: &BlockId<Self::Block>) -> error::Result<Option<txpool::BlockHash<Self>>> {
|
||||
Ok(self.client.block_hash_from_id(at)?)
|
||||
}
|
||||
|
||||
fn hash_and_length(&self, ex: &txpool::ExtrinsicFor<Self>) -> (Self::Hash, usize) {
|
||||
ex.using_encoded(|x| {
|
||||
(Blake2Hasher::hash(x), x.len())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,9 @@ pub type Result<T> = std::result::Result<T, Error>;
|
||||
#[derive(Debug, derive_more::Display, derive_more::From)]
|
||||
pub enum Error {
|
||||
/// Pool error.
|
||||
Pool(txpool::error::Error),
|
||||
Pool(txpool_api::error::Error),
|
||||
/// Blockchain error.
|
||||
Blockchain(sp_blockchain::Error),
|
||||
/// Error while converting a `BlockId`.
|
||||
#[from(ignore)]
|
||||
BlockIdConversion(String),
|
||||
@@ -36,14 +38,15 @@ impl std::error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
Error::Pool(ref err) => Some(err),
|
||||
Error::Blockchain(ref err) => Some(err),
|
||||
Error::BlockIdConversion(_) => None,
|
||||
Error::RuntimeApi(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl txpool::IntoPoolError for Error {
|
||||
fn into_pool_error(self) -> std::result::Result<txpool::error::Error, Self> {
|
||||
impl txpool_api::IntoPoolError for Error {
|
||||
fn into_pool_error(self) -> std::result::Result<txpool_api::error::Error, Self> {
|
||||
match self {
|
||||
Error::Pool(e) => Ok(e),
|
||||
e => Err(e),
|
||||
|
||||
@@ -14,15 +14,119 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Substrate transaction pool.
|
||||
//! Substrate transaction pool implementation.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![warn(unused_extern_crates)]
|
||||
|
||||
mod api;
|
||||
mod maintainer;
|
||||
|
||||
pub mod error;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub use api::FullChainApi;
|
||||
pub use txpool;
|
||||
pub use crate::api::{FullChainApi, LightChainApi};
|
||||
pub use crate::maintainer::{FullBasicPoolMaintainer, LightBasicPoolMaintainer};
|
||||
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use futures::{Future, FutureExt};
|
||||
|
||||
use sr_primitives::{
|
||||
generic::BlockId,
|
||||
traits::Block as BlockT,
|
||||
};
|
||||
use txpool_api::{
|
||||
TransactionPool, PoolStatus, ImportNotificationStream,
|
||||
TxHash, TransactionFor, TransactionStatusStreamFor,
|
||||
};
|
||||
|
||||
/// Basic implementation of transaction pool that can be customized by providing PoolApi.
|
||||
pub struct BasicPool<PoolApi, Block>
|
||||
where
|
||||
Block: BlockT,
|
||||
PoolApi: txpool::ChainApi<Block=Block, Hash=Block::Hash>,
|
||||
{
|
||||
pool: Arc<txpool::Pool<PoolApi>>,
|
||||
}
|
||||
|
||||
impl<PoolApi, Block> BasicPool<PoolApi, Block>
|
||||
where
|
||||
Block: BlockT,
|
||||
PoolApi: txpool::ChainApi<Block=Block, Hash=Block::Hash>,
|
||||
{
|
||||
/// Create new basic transaction pool with provided api.
|
||||
pub fn new(options: txpool::Options, pool_api: PoolApi) -> Self {
|
||||
BasicPool {
|
||||
pool: Arc::new(txpool::Pool::new(options, pool_api)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets shared reference to the underlying pool.
|
||||
pub fn pool(&self) -> &Arc<txpool::Pool<PoolApi>> {
|
||||
&self.pool
|
||||
}
|
||||
}
|
||||
|
||||
impl<PoolApi, Block> TransactionPool for BasicPool<PoolApi, Block>
|
||||
where
|
||||
Block: BlockT,
|
||||
PoolApi: 'static + txpool::ChainApi<Block=Block, Hash=Block::Hash, Error=error::Error>,
|
||||
{
|
||||
type Block = PoolApi::Block;
|
||||
type Hash = txpool::ExHash<PoolApi>;
|
||||
type InPoolTransaction = txpool::base_pool::Transaction<TxHash<Self>, TransactionFor<Self>>;
|
||||
type Error = error::Error;
|
||||
|
||||
fn submit_at(
|
||||
&self,
|
||||
at: &BlockId<Self::Block>,
|
||||
xts: impl IntoIterator<Item=TransactionFor<Self>> + 'static,
|
||||
) -> Box<dyn Future<Output=Result<Vec<Result<TxHash<Self>, Self::Error>>, Self::Error>> + Send + Unpin> {
|
||||
Box::new(self.pool.submit_at(at, xts, false))
|
||||
}
|
||||
|
||||
fn submit_one(
|
||||
&self,
|
||||
at: &BlockId<Self::Block>,
|
||||
xt: TransactionFor<Self>,
|
||||
) -> Box<dyn Future<Output=Result<TxHash<Self>, Self::Error>> + Send + Unpin> {
|
||||
Box::new(self.pool.submit_one(at, xt))
|
||||
}
|
||||
|
||||
fn submit_and_watch(
|
||||
&self,
|
||||
at: &BlockId<Self::Block>,
|
||||
xt: TransactionFor<Self>,
|
||||
) -> Box<dyn Future<Output=Result<Box<TransactionStatusStreamFor<Self>>, Self::Error>> + Send + Unpin> {
|
||||
Box::new(
|
||||
self.pool.submit_and_watch(at, xt)
|
||||
.map(|result| result.map(|watcher| Box::new(watcher.into_stream()) as _))
|
||||
)
|
||||
}
|
||||
|
||||
fn remove_invalid(&self, hashes: &[TxHash<Self>]) -> Vec<Arc<Self::InPoolTransaction>> {
|
||||
self.pool.remove_invalid(hashes)
|
||||
}
|
||||
|
||||
fn status(&self) -> PoolStatus {
|
||||
self.pool.status()
|
||||
}
|
||||
|
||||
fn ready(&self) -> Box<dyn Iterator<Item=Arc<Self::InPoolTransaction>>> {
|
||||
Box::new(self.pool.ready())
|
||||
}
|
||||
|
||||
fn import_notification_stream(&self) -> ImportNotificationStream {
|
||||
self.pool.import_notification_stream()
|
||||
}
|
||||
|
||||
fn hash_of(&self, xt: &TransactionFor<Self>) -> TxHash<Self> {
|
||||
self.pool.hash_of(xt)
|
||||
}
|
||||
|
||||
fn on_broadcasted(&self, propagations: HashMap<TxHash<Self>, Vec<String>>) {
|
||||
self.pool.on_broadcasted(propagations)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,587 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{
|
||||
marker::{PhantomData, Unpin},
|
||||
sync::Arc,
|
||||
time::Instant,
|
||||
};
|
||||
use futures::{
|
||||
Future, FutureExt,
|
||||
future::{Either, join, ready},
|
||||
};
|
||||
use log::warn;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use client_api::{
|
||||
client::BlockBody,
|
||||
light::{Fetcher, RemoteBodyRequest},
|
||||
};
|
||||
use primitives::{Blake2Hasher, H256};
|
||||
use sr_primitives::{
|
||||
generic::BlockId,
|
||||
traits::{Block as BlockT, Extrinsic, Header, NumberFor, ProvideRuntimeApi, SimpleArithmetic},
|
||||
};
|
||||
use sp_blockchain::HeaderBackend;
|
||||
use txpool_api::TransactionPoolMaintainer;
|
||||
use txpool_runtime_api::TaggedTransactionQueue;
|
||||
|
||||
use txpool::{self, ChainApi};
|
||||
|
||||
/// Basic transaction pool maintainer for full clients.
|
||||
pub struct FullBasicPoolMaintainer<Client, PoolApi: ChainApi> {
|
||||
pool: Arc<txpool::Pool<PoolApi>>,
|
||||
client: Arc<Client>,
|
||||
}
|
||||
|
||||
impl<Client, PoolApi: ChainApi> FullBasicPoolMaintainer<Client, PoolApi> {
|
||||
/// Create new basic full pool maintainer.
|
||||
pub fn new(
|
||||
pool: Arc<txpool::Pool<PoolApi>>,
|
||||
client: Arc<Client>,
|
||||
) -> Self {
|
||||
FullBasicPoolMaintainer { pool, client }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block, Client, PoolApi> TransactionPoolMaintainer
|
||||
for
|
||||
FullBasicPoolMaintainer<Client, PoolApi>
|
||||
where
|
||||
Block: BlockT<Hash = <Blake2Hasher as primitives::Hasher>::Out>,
|
||||
Client: ProvideRuntimeApi + HeaderBackend<Block> + BlockBody<Block> + 'static,
|
||||
Client::Api: TaggedTransactionQueue<Block>,
|
||||
PoolApi: ChainApi<Block = Block, Hash = H256> + 'static,
|
||||
{
|
||||
type Block = Block;
|
||||
type Hash = Block::Hash;
|
||||
|
||||
fn maintain(
|
||||
&self,
|
||||
id: &BlockId<Block>,
|
||||
retracted: &[Block::Hash],
|
||||
) -> Box<dyn Future<Output=()> + Send + Unpin> {
|
||||
// Put transactions from retracted blocks back into the pool.
|
||||
let client_copy = self.client.clone();
|
||||
let retracted_transactions = retracted.to_vec().into_iter()
|
||||
.filter_map(move |hash| client_copy.block_body(&BlockId::hash(hash)).ok().unwrap_or(None))
|
||||
.flat_map(|block| block.into_iter())
|
||||
.filter(|tx| tx.is_signed().unwrap_or(false));
|
||||
let resubmit_future = self.pool
|
||||
.submit_at(id, retracted_transactions, true)
|
||||
.then(|resubmit_result| ready(match resubmit_result {
|
||||
Ok(_) => (),
|
||||
Err(e) => {
|
||||
warn!("Error re-submitting transactions: {:?}", e);
|
||||
()
|
||||
}
|
||||
}));
|
||||
|
||||
// Avoid calling into runtime if there is nothing to prune from the pool anyway.
|
||||
if self.pool.status().is_empty() {
|
||||
return Box::new(resubmit_future)
|
||||
}
|
||||
|
||||
let block = (self.client.header(*id), self.client.block_body(id));
|
||||
match block {
|
||||
(Ok(Some(header)), Ok(Some(extrinsics))) => {
|
||||
let parent_id = BlockId::hash(*header.parent_hash());
|
||||
let prune_future = self.pool
|
||||
.prune(id, &parent_id, &extrinsics)
|
||||
.then(|prune_result| ready(match prune_result {
|
||||
Ok(_) => (),
|
||||
Err(e) => {
|
||||
warn!("Error pruning transactions: {:?}", e);
|
||||
()
|
||||
}
|
||||
}));
|
||||
|
||||
Box::new(resubmit_future.then(|_| prune_future))
|
||||
},
|
||||
(Ok(_), Ok(_)) => Box::new(resubmit_future),
|
||||
err => {
|
||||
warn!("Error reading block: {:?}", err);
|
||||
Box::new(resubmit_future)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Basic transaction pool maintainer for light clients.
|
||||
pub struct LightBasicPoolMaintainer<Block: BlockT, Client, PoolApi: ChainApi, F> {
|
||||
pool: Arc<txpool::Pool<PoolApi>>,
|
||||
client: Arc<Client>,
|
||||
fetcher: Arc<F>,
|
||||
revalidate_time_period: Option<std::time::Duration>,
|
||||
revalidate_block_period: Option<NumberFor<Block>>,
|
||||
revalidation_status: Arc<Mutex<TxPoolRevalidationStatus<NumberFor<Block>>>>,
|
||||
_phantom: PhantomData<Block>,
|
||||
}
|
||||
|
||||
impl<Block, Client, PoolApi, F> LightBasicPoolMaintainer<Block, Client, PoolApi, F>
|
||||
where
|
||||
Block: BlockT<Hash = <Blake2Hasher as primitives::Hasher>::Out>,
|
||||
Client: ProvideRuntimeApi + HeaderBackend<Block> + BlockBody<Block> + 'static,
|
||||
Client::Api: TaggedTransactionQueue<Block>,
|
||||
PoolApi: ChainApi<Block = Block, Hash = H256> + 'static,
|
||||
F: Fetcher<Block> + 'static,
|
||||
{
|
||||
/// Create light pool maintainer with default constants.
|
||||
///
|
||||
/// Default constants are: revalidate every 60 seconds or every 20 blocks
|
||||
/// (whatever happens first).
|
||||
pub fn with_defaults(
|
||||
pool: Arc<txpool::Pool<PoolApi>>,
|
||||
client: Arc<Client>,
|
||||
fetcher: Arc<F>,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
pool,
|
||||
client,
|
||||
fetcher,
|
||||
Some(std::time::Duration::from_secs(60)),
|
||||
Some(20.into()),
|
||||
)
|
||||
}
|
||||
|
||||
/// Create light pool maintainer with passed constants.
|
||||
pub fn new(
|
||||
pool: Arc<txpool::Pool<PoolApi>>,
|
||||
client: Arc<Client>,
|
||||
fetcher: Arc<F>,
|
||||
revalidate_time_period: Option<std::time::Duration>,
|
||||
revalidate_block_period: Option<NumberFor<Block>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
pool,
|
||||
client,
|
||||
fetcher,
|
||||
revalidate_time_period,
|
||||
revalidate_block_period,
|
||||
revalidation_status: Arc::new(Mutex::new(TxPoolRevalidationStatus::NotScheduled)),
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns future that prunes block transactions from the pool.
|
||||
fn prune(
|
||||
&self,
|
||||
id: &BlockId<Block>,
|
||||
header: &Block::Header,
|
||||
) -> impl std::future::Future<Output = ()> {
|
||||
// fetch transactions (possible future optimization: proofs of inclusion) that
|
||||
// have been included into new block and prune these from the pool
|
||||
let id = id.clone();
|
||||
let pool = self.pool.clone();
|
||||
self.fetcher.remote_body(RemoteBodyRequest {
|
||||
header: header.clone(),
|
||||
retry_count: None,
|
||||
})
|
||||
.then(move |transactions| ready(
|
||||
transactions
|
||||
.map_err(|e| format!("{}", e))
|
||||
.and_then(|transactions| {
|
||||
let hashes = transactions
|
||||
.into_iter()
|
||||
.map(|tx| pool.hash_of(&tx))
|
||||
.collect::<Vec<_>>();
|
||||
pool.prune_known(&id, &hashes)
|
||||
.map_err(|e| format!("{}", e))
|
||||
})
|
||||
))
|
||||
.then(|r| {
|
||||
if let Err(e) = r {
|
||||
warn!("Error pruning known transactions: {}", e)
|
||||
}
|
||||
ready(())
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns future that performs in-pool transations revalidation, if required.
|
||||
fn revalidate(
|
||||
&self,
|
||||
id: &BlockId<Block>,
|
||||
header: &Block::Header,
|
||||
) -> impl std::future::Future<Output = ()> {
|
||||
// to determine whether ready transaction is still valid, we perform periodic revalidaton
|
||||
// of ready transactions
|
||||
let is_revalidation_required = self.revalidation_status.lock().is_required(
|
||||
*header.number(),
|
||||
self.revalidate_time_period,
|
||||
self.revalidate_block_period,
|
||||
);
|
||||
match is_revalidation_required {
|
||||
true => {
|
||||
let revalidation_status = self.revalidation_status.clone();
|
||||
Either::Left(self.pool
|
||||
.revalidate_ready(id)
|
||||
.map(|r| r.map_err(|e| warn!("Error revalidating known transactions: {}", e)))
|
||||
.map(move |_| revalidation_status.lock().clear()))
|
||||
},
|
||||
false => Either::Right(ready(())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block, Client, PoolApi, F> TransactionPoolMaintainer
|
||||
for
|
||||
LightBasicPoolMaintainer<Block, Client, PoolApi, F>
|
||||
where
|
||||
Block: BlockT<Hash = <Blake2Hasher as primitives::Hasher>::Out>,
|
||||
Client: ProvideRuntimeApi + HeaderBackend<Block> + BlockBody<Block> + 'static,
|
||||
Client::Api: TaggedTransactionQueue<Block>,
|
||||
PoolApi: ChainApi<Block = Block, Hash = H256> + 'static,
|
||||
F: Fetcher<Block> + 'static,
|
||||
{
|
||||
type Block = Block;
|
||||
type Hash = Block::Hash;
|
||||
|
||||
fn maintain(
|
||||
&self,
|
||||
id: &BlockId<Block>,
|
||||
_retracted: &[Block::Hash],
|
||||
) -> Box<dyn Future<Output=()> + Send + Unpin> {
|
||||
// Do nothing if transaction pool is empty.
|
||||
if self.pool.status().is_empty() {
|
||||
self.revalidation_status.lock().clear();
|
||||
return Box::new(ready(()));
|
||||
}
|
||||
let header = self.client.header(*id)
|
||||
.and_then(|h| h.ok_or(sp_blockchain::Error::UnknownBlock(format!("{}", id))));
|
||||
let header = match header {
|
||||
Ok(header) => header,
|
||||
Err(err) => {
|
||||
println!("Failed to maintain light tx pool: {:?}", err);
|
||||
return Box::new(ready(()));
|
||||
}
|
||||
};
|
||||
|
||||
// else prune block transactions from the pool
|
||||
let prune_future = self.prune(id, &header);
|
||||
|
||||
// and then (optionally) revalidate in-pool transactions
|
||||
let revalidate_future = self.revalidate(id, &header);
|
||||
|
||||
let maintain_future = join(
|
||||
prune_future,
|
||||
revalidate_future,
|
||||
).map(|_| ());
|
||||
|
||||
Box::new(maintain_future)
|
||||
}
|
||||
}
|
||||
|
||||
/// The status of transactions revalidation at light tx pool.
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
enum TxPoolRevalidationStatus<N> {
|
||||
/// The revalidation has never been completed.
|
||||
NotScheduled,
|
||||
/// The revalidation is scheduled.
|
||||
Scheduled(Option<std::time::Instant>, Option<N>),
|
||||
/// The revalidation is in progress.
|
||||
InProgress,
|
||||
}
|
||||
|
||||
impl<N: Clone + Copy + SimpleArithmetic> TxPoolRevalidationStatus<N> {
|
||||
/// Called when revalidation is completed.
|
||||
pub fn clear(&mut self) {
|
||||
*self = TxPoolRevalidationStatus::NotScheduled;
|
||||
}
|
||||
|
||||
/// Returns true if revalidation is required.
|
||||
pub fn is_required(
|
||||
&mut self,
|
||||
block: N,
|
||||
revalidate_time_period: Option<std::time::Duration>,
|
||||
revalidate_block_period: Option<N>,
|
||||
) -> bool {
|
||||
match *self {
|
||||
TxPoolRevalidationStatus::NotScheduled => {
|
||||
*self = TxPoolRevalidationStatus::Scheduled(
|
||||
revalidate_time_period.map(|period| Instant::now() + period),
|
||||
revalidate_block_period.map(|period| block + period),
|
||||
);
|
||||
false
|
||||
},
|
||||
TxPoolRevalidationStatus::Scheduled(revalidate_at_time, revalidate_at_block) => {
|
||||
let is_required = revalidate_at_time.map(|at| Instant::now() >= at).unwrap_or(false)
|
||||
|| revalidate_at_block.map(|at| block >= at).unwrap_or(false);
|
||||
if is_required {
|
||||
*self = TxPoolRevalidationStatus::InProgress;
|
||||
}
|
||||
is_required
|
||||
},
|
||||
TxPoolRevalidationStatus::InProgress => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use futures::executor::block_on;
|
||||
use codec::Encode;
|
||||
use test_client::{prelude::*, runtime::{Block, Transfer}, consensus::{BlockOrigin, SelectChain}};
|
||||
use txpool_api::PoolStatus;
|
||||
use crate::api::{FullChainApi, LightChainApi};
|
||||
|
||||
#[test]
|
||||
fn should_remove_transactions_from_the_full_pool() {
|
||||
let (client, longest_chain) = TestClientBuilder::new().build_with_longest_chain();
|
||||
let client = Arc::new(client);
|
||||
let pool = txpool::Pool::new(Default::default(), FullChainApi::new(client.clone()));
|
||||
let pool = Arc::new(pool);
|
||||
let transaction = Transfer {
|
||||
amount: 5,
|
||||
nonce: 0,
|
||||
from: AccountKeyring::Alice.into(),
|
||||
to: Default::default(),
|
||||
}.into_signed_tx();
|
||||
let best = longest_chain.best_chain().unwrap();
|
||||
|
||||
// store the transaction in the pool
|
||||
block_on(pool.submit_one(&BlockId::hash(best.hash()), transaction.clone())).unwrap();
|
||||
|
||||
// import the block
|
||||
let mut builder = client.new_block(Default::default()).unwrap();
|
||||
builder.push(transaction.clone()).unwrap();
|
||||
let block = builder.bake().unwrap();
|
||||
let id = BlockId::hash(block.header().hash());
|
||||
client.import(BlockOrigin::Own, block).unwrap();
|
||||
|
||||
// fire notification - this should clean up the queue
|
||||
assert_eq!(pool.status().ready, 1);
|
||||
block_on(FullBasicPoolMaintainer::new(pool.clone(), client).maintain(&id, &[]));
|
||||
|
||||
// then
|
||||
assert_eq!(pool.status().ready, 0);
|
||||
assert_eq!(pool.status().future, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_remove_transactions_from_the_light_pool() {
|
||||
let transaction = Transfer {
|
||||
amount: 5,
|
||||
nonce: 0,
|
||||
from: AccountKeyring::Alice.into(),
|
||||
to: Default::default(),
|
||||
}.into_signed_tx();
|
||||
let fetcher_transaction = transaction.clone();
|
||||
let fetcher = Arc::new(test_client::new_light_fetcher()
|
||||
.with_remote_body(Some(Box::new(move |_| Ok(vec![fetcher_transaction.clone()]))))
|
||||
.with_remote_call(Some(Box::new(move |_| {
|
||||
let validity: sr_primitives::transaction_validity::TransactionValidity =
|
||||
Ok(sr_primitives::transaction_validity::ValidTransaction {
|
||||
priority: 0,
|
||||
requires: Vec::new(),
|
||||
provides: vec![vec![42]],
|
||||
longevity: 0,
|
||||
propagate: true,
|
||||
});
|
||||
Ok(validity.encode())
|
||||
}))));
|
||||
|
||||
let (client, longest_chain) = TestClientBuilder::new().build_with_longest_chain();
|
||||
let client = Arc::new(client);
|
||||
let pool = txpool::Pool::new(Default::default(), LightChainApi::new(
|
||||
client.clone(),
|
||||
fetcher.clone(),
|
||||
));
|
||||
let pool = Arc::new(pool);
|
||||
let best = longest_chain.best_chain().unwrap();
|
||||
|
||||
// store the transaction in the pool
|
||||
block_on(pool.submit_one(&BlockId::hash(best.hash()), transaction.clone())).unwrap();
|
||||
|
||||
// fire notification - this should clean up the queue
|
||||
assert_eq!(pool.status().ready, 1);
|
||||
block_on(LightBasicPoolMaintainer::with_defaults(pool.clone(), client.clone(), fetcher).maintain(
|
||||
&BlockId::Number(0),
|
||||
&[],
|
||||
));
|
||||
|
||||
// then
|
||||
assert_eq!(pool.status().ready, 0);
|
||||
assert_eq!(pool.status().future, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_schedule_transactions_revalidation_at_light_pool() {
|
||||
// when revalidation is not scheduled, it became scheduled
|
||||
let mut status = TxPoolRevalidationStatus::NotScheduled;
|
||||
assert!(!status.is_required(10u32, None, None));
|
||||
match status {
|
||||
TxPoolRevalidationStatus::Scheduled(_, _) => (),
|
||||
_ => panic!("Unexpected status: {:?}", status),
|
||||
}
|
||||
|
||||
// revalidation required at time
|
||||
let mut status = TxPoolRevalidationStatus::Scheduled(Some(Instant::now()), None);
|
||||
assert!(status.is_required(10u32, None, None));
|
||||
match status {
|
||||
TxPoolRevalidationStatus::InProgress => (),
|
||||
_ => panic!("Unexpected status: {:?}", status),
|
||||
}
|
||||
|
||||
// revalidation required at block
|
||||
let mut status = TxPoolRevalidationStatus::Scheduled(None, Some(10));
|
||||
assert!(status.is_required(10u32, None, None));
|
||||
match status {
|
||||
TxPoolRevalidationStatus::InProgress => (),
|
||||
_ => panic!("Unexpected status: {:?}", status),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_revalidate_transactions_at_light_pool() {
|
||||
use std::sync::atomic;
|
||||
use sr_primitives::transaction_validity::*;
|
||||
|
||||
let build_fetcher = || {
|
||||
let validated = Arc::new(atomic::AtomicBool::new(false));
|
||||
Arc::new(test_client::new_light_fetcher()
|
||||
.with_remote_body(Some(Box::new(move |_| Ok(vec![]))))
|
||||
.with_remote_call(Some(Box::new(move |_| {
|
||||
let is_inserted = validated.swap(true, atomic::Ordering::SeqCst);
|
||||
let validity: TransactionValidity = if is_inserted {
|
||||
Err(TransactionValidityError::Invalid(
|
||||
InvalidTransaction::Custom(0)
|
||||
))
|
||||
} else {
|
||||
Ok(ValidTransaction {
|
||||
priority: 0,
|
||||
requires: Vec::new(),
|
||||
provides: vec![vec![42]],
|
||||
longevity: 0,
|
||||
propagate: true,
|
||||
})
|
||||
};
|
||||
Ok(validity.encode())
|
||||
}))))
|
||||
};
|
||||
|
||||
fn with_fetcher_maintain<F: Fetcher<Block> + 'static>(
|
||||
fetcher: Arc<F>,
|
||||
revalidate_time_period: Option<std::time::Duration>,
|
||||
revalidate_block_period: Option<u64>,
|
||||
prepare_maintainer: impl Fn(&Mutex<TxPoolRevalidationStatus<u64>>),
|
||||
) -> PoolStatus {
|
||||
let (client, longest_chain) = TestClientBuilder::new().build_with_longest_chain();
|
||||
let client = Arc::new(client);
|
||||
|
||||
// now let's prepare pool
|
||||
let pool = txpool::Pool::new(Default::default(), LightChainApi::new(
|
||||
client.clone(),
|
||||
fetcher.clone(),
|
||||
));
|
||||
let pool = Arc::new(pool);
|
||||
let best = longest_chain.best_chain().unwrap();
|
||||
|
||||
// let's prepare maintainer
|
||||
let maintainer = LightBasicPoolMaintainer::new(
|
||||
pool.clone(),
|
||||
client,
|
||||
fetcher,
|
||||
revalidate_time_period,
|
||||
revalidate_block_period,
|
||||
);
|
||||
prepare_maintainer(&*maintainer.revalidation_status);
|
||||
|
||||
// store the transaction in the pool
|
||||
block_on(pool.submit_one(
|
||||
&BlockId::hash(best.hash()),
|
||||
Transfer {
|
||||
amount: 5,
|
||||
nonce: 0,
|
||||
from: AccountKeyring::Alice.into(),
|
||||
to: Default::default(),
|
||||
}.into_signed_tx(),
|
||||
)).unwrap();
|
||||
|
||||
// and run maintain procedures
|
||||
block_on(maintainer.maintain(&BlockId::Number(0), &[]));
|
||||
|
||||
pool.status()
|
||||
}
|
||||
|
||||
// when revalidation is never required - nothing happens
|
||||
let fetcher = build_fetcher();
|
||||
//let maintainer = DefaultLightTransactionPoolMaintainer::new(client.clone(), fetcher.clone(), None, None);
|
||||
let status = with_fetcher_maintain(fetcher, None, None, |_revalidation_status| {});
|
||||
assert_eq!(status.ready, 1);
|
||||
|
||||
// when revalidation is scheduled by time - it is performed
|
||||
let fetcher = build_fetcher();
|
||||
let status = with_fetcher_maintain(fetcher, None, None, |revalidation_status|
|
||||
*revalidation_status.lock() = TxPoolRevalidationStatus::Scheduled(Some(Instant::now()), None)
|
||||
);
|
||||
assert_eq!(status.ready, 0);
|
||||
|
||||
// when revalidation is scheduled by block number - it is performed
|
||||
let fetcher = build_fetcher();
|
||||
let status = with_fetcher_maintain(fetcher, None, None, |revalidation_status|
|
||||
*revalidation_status.lock() = TxPoolRevalidationStatus::Scheduled(None, Some(0))
|
||||
);
|
||||
assert_eq!(status.ready, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_add_reverted_transactions_to_the_pool() {
|
||||
let (client, longest_chain) = TestClientBuilder::new().build_with_longest_chain();
|
||||
let client = Arc::new(client);
|
||||
let pool = txpool::Pool::new(Default::default(), FullChainApi::new(client.clone()));
|
||||
let pool = Arc::new(pool);
|
||||
let transaction = Transfer {
|
||||
amount: 5,
|
||||
nonce: 0,
|
||||
from: AccountKeyring::Alice.into(),
|
||||
to: Default::default(),
|
||||
}.into_signed_tx();
|
||||
let best = longest_chain.best_chain().unwrap();
|
||||
|
||||
// store the transaction in the pool
|
||||
block_on(pool.submit_one(&BlockId::hash(best.hash()), transaction.clone())).unwrap();
|
||||
|
||||
// import the block
|
||||
let mut builder = client.new_block(Default::default()).unwrap();
|
||||
builder.push(transaction.clone()).unwrap();
|
||||
let block = builder.bake().unwrap();
|
||||
let block1_hash = block.header().hash();
|
||||
let id = BlockId::hash(block1_hash.clone());
|
||||
client.import(BlockOrigin::Own, block).unwrap();
|
||||
|
||||
// fire notification - this should clean up the queue
|
||||
assert_eq!(pool.status().ready, 1);
|
||||
block_on(FullBasicPoolMaintainer::new(pool.clone(), client.clone()).maintain(&id, &[]));
|
||||
|
||||
// then
|
||||
assert_eq!(pool.status().ready, 0);
|
||||
assert_eq!(pool.status().future, 0);
|
||||
|
||||
// import second block
|
||||
let builder = client.new_block_at(&BlockId::hash(best.hash()), Default::default()).unwrap();
|
||||
let block = builder.bake().unwrap();
|
||||
let id = BlockId::hash(block.header().hash());
|
||||
client.import(BlockOrigin::Own, block).unwrap();
|
||||
|
||||
// fire notification - this should add the transaction back to the pool.
|
||||
block_on(FullBasicPoolMaintainer::new(pool.clone(), client).maintain(&id, &[block1_hash]));
|
||||
|
||||
// then
|
||||
assert_eq!(pool.status().ready, 1);
|
||||
assert_eq!(pool.status().future, 0);
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
# Transaction pool
|
||||
/client/transaction-pool/ @tomusdrw
|
||||
/primitives/transaction-pool/ @tomusdrw
|
||||
|
||||
# Offchain
|
||||
/client/offchain/ @tomusdrw
|
||||
|
||||
@@ -439,7 +439,7 @@ substrate-executor, substrate-finality-grandpa, substrate-keyring, substrate-key
|
||||
substrate-network-libp2p, substrate-primitives, substrate-rpc, substrate-rpc-servers,
|
||||
substrate-serializer, substrate-service, substrate-service-test, substrate-state-db,
|
||||
substrate-state-machine, substrate-telemetry, substrate-test-client,
|
||||
substrate-test-runtime, substrate-transaction-graph, substrate-transaction-pool,
|
||||
substrate-test-runtime, substrate-transaction-graph, sp-transaction-pool,
|
||||
substrate-trie
|
||||
* Substrate Runtime
|
||||
[source, shell]
|
||||
|
||||
@@ -16,27 +16,4 @@
|
||||
|
||||
//! A collection of higher lever helpers for offchain calls.
|
||||
|
||||
use crate::{
|
||||
traits,
|
||||
generic::BlockId,
|
||||
};
|
||||
|
||||
pub mod http;
|
||||
|
||||
/// 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 TransactionPool<Block: traits::Block>: 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<(), ()>;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "sp-transaction-pool-api"
|
||||
version = "2.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
derive_more = "0.15.0"
|
||||
futures = "0.3.1"
|
||||
log = "0.4.8"
|
||||
serde = { version = "1.0.101", features = ["derive"] }
|
||||
codec = { package = "parity-scale-codec", version = "1.0.0" }
|
||||
primitives = { package = "substrate-primitives", path = "../core" }
|
||||
sr-primitives = { path = "../sr-primitives" }
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "substrate-transaction-pool-runtime-api"
|
||||
name = "sp-transaction-pool-runtime-api"
|
||||
version = "2.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Transaction pool errors.
|
||||
|
||||
use sr_primitives::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, derive_more::Display, derive_more::From)]
|
||||
pub enum Error {
|
||||
/// Transaction is not verifiable yet, but might be in the future.
|
||||
#[display(fmt="Unknown transaction validity: {:?}", _0)]
|
||||
UnknownTransaction(UnknownTransaction),
|
||||
/// Transaction is invalid.
|
||||
#[display(fmt="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").
|
||||
#[display(fmt="The transaction does not provide any tags, so the pool can't identify it.")]
|
||||
NoTagsProvided,
|
||||
/// The transaction is temporarily banned.
|
||||
#[display(fmt="Temporarily Banned")]
|
||||
TemporarilyBanned,
|
||||
/// The transaction is already in the pool.
|
||||
#[display(fmt="[{:?}] Already imported", _0)]
|
||||
AlreadyImported(Box<dyn std::any::Any + Send>),
|
||||
/// The transaction cannot be imported cause it's a replacement and has too low priority.
|
||||
#[display(fmt="Too low priority ({} > {})", old, new)]
|
||||
TooLowPriority {
|
||||
/// Transaction already in the pool.
|
||||
old: Priority,
|
||||
/// Transaction entering the pool.
|
||||
new: Priority
|
||||
},
|
||||
/// Deps cycle etected and we couldn't import transaction.
|
||||
#[display(fmt="Cycle Detected")]
|
||||
CycleDetected,
|
||||
/// Transaction was dropped immediately after it got inserted.
|
||||
#[display(fmt="Transaction couldn't enter the pool because of the limit.")]
|
||||
ImmediatelyDropped,
|
||||
/// Invalid block id.
|
||||
InvalidBlockId(String),
|
||||
/// The pool is not accepting future transactions.
|
||||
#[display(fmt="The pool is not accepting future transactions")]
|
||||
RejectedFutureTransaction,
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
/// 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,328 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Transaction pool types.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
pub mod error;
|
||||
|
||||
pub use error::IntoPoolError;
|
||||
pub use sr_primitives::transaction_validity::{
|
||||
TransactionLongevity, TransactionPriority, TransactionTag,
|
||||
};
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
hash::Hash,
|
||||
sync::Arc,
|
||||
};
|
||||
use futures::{
|
||||
Future, Stream,
|
||||
channel::mpsc,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sr_primitives::{
|
||||
generic::BlockId,
|
||||
traits::{Block as BlockT, Member},
|
||||
};
|
||||
|
||||
/// 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.
|
||||
#[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,
|
||||
/// Transaction has been finalized in block with given hash.
|
||||
Finalized(BlockHash),
|
||||
/// Some state change (perhaps another transaction was included) rendered this transaction invalid.
|
||||
Usurped(Hash),
|
||||
/// The transaction has been broadcast to the given peers.
|
||||
Broadcast(Vec<String>),
|
||||
/// Transaction has been dropped from the pool because of the limit.
|
||||
Dropped,
|
||||
/// Transaction was detected as invalid.
|
||||
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 = mpsc::UnboundedReceiver<()>;
|
||||
|
||||
/// 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>>;
|
||||
|
||||
/// In-pool transaction interface.
|
||||
///
|
||||
/// The pool is container of transactions that are implementing this trait.
|
||||
/// See `sr_primitives::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_propagateable(&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<error::Error> + IntoPoolError;
|
||||
|
||||
/// Returns a future that imports a bunch of unverified transactions to the pool.
|
||||
fn submit_at(
|
||||
&self,
|
||||
at: &BlockId<Self::Block>,
|
||||
xts: impl IntoIterator<Item=TransactionFor<Self>> + 'static,
|
||||
) -> Box<dyn Future<Output=Result<
|
||||
Vec<Result<TxHash<Self>, Self::Error>>,
|
||||
Self::Error
|
||||
>> + Send + Unpin>;
|
||||
|
||||
/// Returns a future that imports one unverified transaction to the pool.
|
||||
fn submit_one(
|
||||
&self,
|
||||
at: &BlockId<Self::Block>,
|
||||
xt: TransactionFor<Self>,
|
||||
) -> Box<dyn Future<Output=Result<
|
||||
TxHash<Self>,
|
||||
Self::Error
|
||||
>> + Send + Unpin>;
|
||||
|
||||
/// 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>,
|
||||
xt: TransactionFor<Self>,
|
||||
) -> Box<dyn Future<Output=Result<Box<TransactionStatusStreamFor<Self>>, Self::Error>> + Send + Unpin>;
|
||||
|
||||
/// Remove transactions identified by given hashes (and dependent transactions) from the pool.
|
||||
fn remove_invalid(&self, hashes: &[TxHash<Self>]) -> Vec<Arc<Self::InPoolTransaction>>;
|
||||
|
||||
/// Returns pool status.
|
||||
fn status(&self) -> PoolStatus;
|
||||
|
||||
/// Get an iterator for ready transactions ordered by priority
|
||||
fn ready(&self) -> Box<dyn Iterator<Item=Arc<Self::InPoolTransaction>>>;
|
||||
|
||||
/// Return an event stream of transactions imported to the pool.
|
||||
fn import_notification_stream(&self) -> ImportNotificationStream;
|
||||
|
||||
/// Returns transaction hash
|
||||
fn hash_of(&self, xt: &TransactionFor<Self>) -> TxHash<Self>;
|
||||
|
||||
/// Notify the pool about transactions broadcast.
|
||||
fn on_broadcasted(&self, propagations: HashMap<TxHash<Self>, Vec<String>>);
|
||||
}
|
||||
|
||||
/// 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: TransactionPool> 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 = futures::executor::block_on(self.submit_one(&at, extrinsic));
|
||||
|
||||
result.map(|_| ())
|
||||
.map_err(|e| log::warn!(
|
||||
target: "txpool",
|
||||
"(offchain call) Error submitting a transaction to the pool: {:?}",
|
||||
e
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Transaction pool maintainer interface.
|
||||
pub trait TransactionPoolMaintainer: Send + Sync {
|
||||
/// Block type.
|
||||
type Block: BlockT;
|
||||
/// Transaction Hash type.
|
||||
type Hash: Hash + Eq + Member + Serialize;
|
||||
|
||||
/// Returns a future that performs maintenance procedures on the pool when
|
||||
/// with given hash is imported.
|
||||
fn maintain(
|
||||
&self,
|
||||
id: &BlockId<Self::Block>,
|
||||
retracted: &[Self::Hash],
|
||||
) -> Box<dyn Future<Output=()> + Send + Unpin>;
|
||||
}
|
||||
|
||||
/// Maintainable pool implementation.
|
||||
pub struct MaintainableTransactionPool<Pool, Maintainer> {
|
||||
pool: Pool,
|
||||
maintainer: Maintainer,
|
||||
}
|
||||
|
||||
impl<Pool, Maintainer> MaintainableTransactionPool<Pool, Maintainer> {
|
||||
/// Create new maintainable pool using underlying pool and maintainer.
|
||||
pub fn new(pool: Pool, maintainer: Maintainer) -> Self {
|
||||
MaintainableTransactionPool { pool, maintainer }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Pool, Maintainer> TransactionPool for MaintainableTransactionPool<Pool, Maintainer>
|
||||
where
|
||||
Pool: TransactionPool,
|
||||
Maintainer: Send + Sync,
|
||||
{
|
||||
type Block = Pool::Block;
|
||||
type Hash = Pool::Hash;
|
||||
type InPoolTransaction = Pool::InPoolTransaction;
|
||||
type Error = Pool::Error;
|
||||
|
||||
fn submit_at(
|
||||
&self,
|
||||
at: &BlockId<Self::Block>,
|
||||
xts: impl IntoIterator<Item=TransactionFor<Self>> + 'static,
|
||||
) -> Box<dyn Future<Output=Result<Vec<Result<TxHash<Self>, Self::Error>>, Self::Error>> + Send + Unpin> {
|
||||
self.pool.submit_at(at, xts)
|
||||
}
|
||||
|
||||
fn submit_one(
|
||||
&self,
|
||||
at: &BlockId<Self::Block>,
|
||||
xt: TransactionFor<Self>,
|
||||
) -> Box<dyn Future<Output=Result<TxHash<Self>, Self::Error>> + Send + Unpin> {
|
||||
self.pool.submit_one(at, xt)
|
||||
}
|
||||
|
||||
fn submit_and_watch(
|
||||
&self,
|
||||
at: &BlockId<Self::Block>,
|
||||
xt: TransactionFor<Self>,
|
||||
) -> Box<dyn Future<Output=Result<Box<TransactionStatusStreamFor<Self>>, Self::Error>> + Send + Unpin> {
|
||||
self.pool.submit_and_watch(at, xt)
|
||||
}
|
||||
|
||||
fn remove_invalid(&self, hashes: &[TxHash<Self>]) -> Vec<Arc<Self::InPoolTransaction>> {
|
||||
self.pool.remove_invalid(hashes)
|
||||
}
|
||||
|
||||
fn status(&self) -> PoolStatus {
|
||||
self.pool.status()
|
||||
}
|
||||
|
||||
fn ready(&self) -> Box<dyn Iterator<Item=Arc<Self::InPoolTransaction>>> {
|
||||
self.pool.ready()
|
||||
}
|
||||
|
||||
fn import_notification_stream(&self) -> ImportNotificationStream {
|
||||
self.pool.import_notification_stream()
|
||||
}
|
||||
|
||||
fn hash_of(&self, xt: &TransactionFor<Self>) -> TxHash<Self> {
|
||||
self.pool.hash_of(xt)
|
||||
}
|
||||
|
||||
fn on_broadcasted(&self, propagations: HashMap<TxHash<Self>, Vec<String>>) {
|
||||
self.pool.on_broadcasted(propagations)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Pool, Maintainer> TransactionPoolMaintainer for MaintainableTransactionPool<Pool, Maintainer>
|
||||
where
|
||||
Pool: Send + Sync,
|
||||
Maintainer: TransactionPoolMaintainer
|
||||
{
|
||||
type Block = Maintainer::Block;
|
||||
type Hash = Maintainer::Hash;
|
||||
|
||||
fn maintain(
|
||||
&self,
|
||||
id: &BlockId<Self::Block>,
|
||||
retracted: &[Self::Hash],
|
||||
) -> Box<dyn Future<Output=()> + Send + Unpin> {
|
||||
self.maintainer.maintain(id, retracted)
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! The Substrate test primitives to share
|
||||
//! The Substrate test primitives to share
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ frame-system-rpc-runtime-api = { path = "../../../frame/system/rpc/runtime-api",
|
||||
pallet-timestamp = { path = "../../../frame/timestamp", default-features = false }
|
||||
substrate-client = { path = "../../../client", optional = true }
|
||||
substrate-trie = { path = "../../../primitives/trie", default-features = false }
|
||||
transaction-pool-api = { package = "substrate-transaction-pool-runtime-api", path = "../../../primitives/transaction-pool/runtime-api", default-features = false }
|
||||
txpool-runtime-api = { package = "sp-transaction-pool-runtime-api", path = "../../../primitives/transaction-pool/runtime-api", default-features = false }
|
||||
trie-db = { version = "0.16.0", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
@@ -78,6 +78,6 @@ std = [
|
||||
"pallet-timestamp/std",
|
||||
"substrate-client",
|
||||
"substrate-trie/std",
|
||||
"transaction-pool-api/std",
|
||||
"txpool-runtime-api/std",
|
||||
"trie-db/std",
|
||||
]
|
||||
|
||||
@@ -14,3 +14,4 @@ sp-blockchain = { path = "../../../../primitives/blockchain" }
|
||||
codec = { package = "parity-scale-codec", version = "1.0.0" }
|
||||
client-api = { package = "substrate-client-api", path = "../../../../client/api" }
|
||||
client = { package = "substrate-client", path = "../../../../client/" }
|
||||
futures = "0.3.1"
|
||||
|
||||
@@ -30,7 +30,15 @@ pub use runtime;
|
||||
|
||||
use primitives::sr25519;
|
||||
use runtime::genesismap::{GenesisConfig, additional_storage_with_genesis};
|
||||
use sr_primitives::traits::{Block as BlockT, Header as HeaderT, Hash as HashT};
|
||||
use sr_primitives::traits::{Block as BlockT, Header as HeaderT, Hash as HashT, NumberFor};
|
||||
use client::{
|
||||
light::fetcher::{
|
||||
Fetcher,
|
||||
RemoteHeaderRequest, RemoteReadRequest, RemoteReadChildRequest,
|
||||
RemoteCallRequest, RemoteChangesRequest, RemoteBodyRequest,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/// A prelude to import in tests.
|
||||
pub mod prelude {
|
||||
@@ -247,6 +255,81 @@ impl<B> TestClientBuilderExt<B> for TestClientBuilder<
|
||||
}
|
||||
}
|
||||
|
||||
/// Type of optional fetch callback.
|
||||
type MaybeFetcherCallback<Req, Resp> = Option<Box<dyn Fn(Req) -> Result<Resp, sp_blockchain::Error> + Send + Sync>>;
|
||||
|
||||
/// Type of fetcher future result.
|
||||
type FetcherFutureResult<Resp> = futures::future::Ready<Result<Resp, sp_blockchain::Error>>;
|
||||
|
||||
/// Implementation of light client fetcher used in tests.
|
||||
#[derive(Default)]
|
||||
pub struct LightFetcher {
|
||||
call: MaybeFetcherCallback<RemoteCallRequest<runtime::Header>, Vec<u8>>,
|
||||
body: MaybeFetcherCallback<RemoteBodyRequest<runtime::Header>, Vec<runtime::Extrinsic>>,
|
||||
}
|
||||
|
||||
impl LightFetcher {
|
||||
/// Sets remote call callback.
|
||||
pub fn with_remote_call(
|
||||
self,
|
||||
call: MaybeFetcherCallback<RemoteCallRequest<runtime::Header>, Vec<u8>>,
|
||||
) -> Self {
|
||||
LightFetcher {
|
||||
call,
|
||||
body: self.body,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets remote body callback.
|
||||
pub fn with_remote_body(
|
||||
self,
|
||||
body: MaybeFetcherCallback<RemoteBodyRequest<runtime::Header>, Vec<runtime::Extrinsic>>,
|
||||
) -> Self {
|
||||
LightFetcher {
|
||||
call: self.call,
|
||||
body,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Fetcher<runtime::Block> for LightFetcher {
|
||||
type RemoteHeaderResult = FetcherFutureResult<runtime::Header>;
|
||||
type RemoteReadResult = FetcherFutureResult<HashMap<Vec<u8>, Option<Vec<u8>>>>;
|
||||
type RemoteCallResult = FetcherFutureResult<Vec<u8>>;
|
||||
type RemoteChangesResult = FetcherFutureResult<Vec<(NumberFor<runtime::Block>, u32)>>;
|
||||
type RemoteBodyResult = FetcherFutureResult<Vec<runtime::Extrinsic>>;
|
||||
|
||||
fn remote_header(&self, _: RemoteHeaderRequest<runtime::Header>) -> Self::RemoteHeaderResult {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn remote_read(&self, _: RemoteReadRequest<runtime::Header>) -> Self::RemoteReadResult {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn remote_read_child(&self, _: RemoteReadChildRequest<runtime::Header>) -> Self::RemoteReadResult {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn remote_call(&self, req: RemoteCallRequest<runtime::Header>) -> Self::RemoteCallResult {
|
||||
match self.call {
|
||||
Some(ref call) => futures::future::ready(call(req)),
|
||||
None => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn remote_changes(&self, _: RemoteChangesRequest<runtime::Header>) -> Self::RemoteChangesResult {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn remote_body(&self, req: RemoteBodyRequest<runtime::Header>) -> Self::RemoteBodyResult {
|
||||
match self.body {
|
||||
Some(ref body) => futures::future::ready(body(req)),
|
||||
None => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates new client instance used for tests.
|
||||
pub fn new() -> Client<Backend> {
|
||||
TestClientBuilder::new().build()
|
||||
@@ -275,3 +358,8 @@ pub fn new_light() -> (
|
||||
backend,
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates new light client fetcher used for tests.
|
||||
pub fn new_light_fetcher() -> LightFetcher {
|
||||
LightFetcher::default()
|
||||
}
|
||||
|
||||
@@ -477,7 +477,7 @@ cfg_if! {
|
||||
}
|
||||
}
|
||||
|
||||
impl transaction_pool_api::TaggedTransactionQueue<Block> for Runtime {
|
||||
impl txpool_runtime_api::TaggedTransactionQueue<Block> for Runtime {
|
||||
fn validate_transaction(utx: <Block as BlockT>::Extrinsic) -> TransactionValidity {
|
||||
if let Extrinsic::IncludeData(data) = utx {
|
||||
return Ok(ValidTransaction {
|
||||
@@ -662,7 +662,7 @@ cfg_if! {
|
||||
}
|
||||
}
|
||||
|
||||
impl transaction_pool_api::TaggedTransactionQueue<Block> for Runtime {
|
||||
impl txpool_runtime_api::TaggedTransactionQueue<Block> for Runtime {
|
||||
fn validate_transaction(utx: <Block as BlockT>::Extrinsic) -> TransactionValidity {
|
||||
if let Extrinsic::IncludeData(data) = utx {
|
||||
return Ok(ValidTransaction{
|
||||
|
||||
@@ -5,7 +5,9 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
client = { package = "substrate-client", path = "../../../../client/" }
|
||||
codec = { package = "parity-scale-codec", version = "1.0.0" }
|
||||
futures = "0.3.1"
|
||||
jsonrpc-core = "14.0.3"
|
||||
jsonrpc-core-client = "14.0.3"
|
||||
jsonrpc-derive = "14.0.3"
|
||||
@@ -15,10 +17,9 @@ sr-primitives = { path = "../../../../primitives/sr-primitives" }
|
||||
frame-system-rpc-runtime-api = { path = "../../../../frame/system/rpc/runtime-api" }
|
||||
substrate-primitives = { path = "../../../../primitives/core" }
|
||||
sp-blockchain = { path = "../../../../primitives/blockchain" }
|
||||
sc-transaction-graph = { path = "../../../../client/transaction-pool/graph" }
|
||||
txpool-api = { package = "sp-transaction-pool-api", path = "../../../../primitives/transaction-pool" }
|
||||
|
||||
[dev-dependencies]
|
||||
test-client = { package = "substrate-test-runtime-client", path = "../../../../test/utils/runtime/client" }
|
||||
sc-transaction-pool = { path = "../../../../client/transaction-pool" }
|
||||
env_logger = "0.7.0"
|
||||
futures = "0.3.1"
|
||||
txpool = { package = "sc-transaction-pool", path = "../../../../client/transaction-pool" }
|
||||
|
||||
@@ -18,20 +18,34 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use codec::{self, Codec, Encode};
|
||||
use sp_blockchain::HeaderBackend;
|
||||
use jsonrpc_core::{Result, Error, ErrorCode};
|
||||
use codec::{self, Codec, Decode, Encode};
|
||||
use client::{
|
||||
light::blockchain::{future_header, RemoteBlockchain},
|
||||
light::fetcher::{Fetcher, RemoteCallRequest},
|
||||
};
|
||||
use jsonrpc_core::{
|
||||
Error, ErrorCode,
|
||||
futures::future::{result, Future},
|
||||
};
|
||||
use jsonrpc_derive::rpc;
|
||||
use futures::future::{ready, TryFutureExt};
|
||||
use sp_blockchain::{
|
||||
HeaderBackend,
|
||||
Error as ClientError
|
||||
};
|
||||
use sr_primitives::{
|
||||
generic::BlockId,
|
||||
traits,
|
||||
};
|
||||
use substrate_primitives::hexdisplay::HexDisplay;
|
||||
use sc_transaction_graph::{self, ChainApi, Pool};
|
||||
use txpool_api::{TransactionPool, InPoolTransaction};
|
||||
|
||||
pub use frame_system_rpc_runtime_api::AccountNonceApi;
|
||||
pub use self::gen_client::Client as SystemClient;
|
||||
|
||||
/// Future that resolves to account nonce.
|
||||
pub type FutureResult<T> = Box<dyn Future<Item = T, Error = Error> + Send>;
|
||||
|
||||
/// System RPC methods.
|
||||
#[rpc]
|
||||
pub trait SystemApi<AccountId, Index> {
|
||||
@@ -41,22 +55,22 @@ pub trait SystemApi<AccountId, Index> {
|
||||
/// currently in the pool and if no transactions are found in the pool
|
||||
/// it fallbacks to query the index from the runtime (aka. state nonce).
|
||||
#[rpc(name = "system_accountNextIndex", alias("account_nextIndex"))]
|
||||
fn nonce(&self, account: AccountId) -> Result<Index>;
|
||||
fn nonce(&self, account: AccountId) -> FutureResult<Index>;
|
||||
}
|
||||
|
||||
const RUNTIME_ERROR: i64 = 1;
|
||||
|
||||
/// An implementation of System-specific RPC methods.
|
||||
pub struct System<P: ChainApi, C, B> {
|
||||
/// An implementation of System-specific RPC methods on full client.
|
||||
pub struct FullSystem<P: TransactionPool, C, B> {
|
||||
client: Arc<C>,
|
||||
pool: Arc<Pool<P>>,
|
||||
pool: Arc<P>,
|
||||
_marker: std::marker::PhantomData<B>,
|
||||
}
|
||||
|
||||
impl<P: ChainApi, C, B> System<P, C, B> {
|
||||
/// Create new `System` given client and transaction pool.
|
||||
pub fn new(client: Arc<C>, pool: Arc<Pool<P>>) -> Self {
|
||||
System {
|
||||
impl<P: TransactionPool, C, B> FullSystem<P, C, B> {
|
||||
/// Create new `FullSystem` given client and transaction pool.
|
||||
pub fn new(client: Arc<C>, pool: Arc<P>) -> Self {
|
||||
FullSystem {
|
||||
client,
|
||||
pool,
|
||||
_marker: Default::default(),
|
||||
@@ -64,74 +78,164 @@ impl<P: ChainApi, C, B> System<P, C, B> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, C, Block, AccountId, Index> SystemApi<AccountId, Index> for System<P, C, Block>
|
||||
impl<P, C, Block, AccountId, Index> SystemApi<AccountId, Index> for FullSystem<P, C, Block>
|
||||
where
|
||||
C: traits::ProvideRuntimeApi,
|
||||
C: HeaderBackend<Block>,
|
||||
C: Send + Sync + 'static,
|
||||
C::Api: AccountNonceApi<Block, AccountId, Index>,
|
||||
P: ChainApi + Sync + Send + 'static,
|
||||
P: TransactionPool + 'static,
|
||||
Block: traits::Block,
|
||||
AccountId: Clone + std::fmt::Display + Codec,
|
||||
Index: Clone + std::fmt::Display + Codec + traits::SimpleArithmetic,
|
||||
Index: Clone + std::fmt::Display + Codec + Send + traits::SimpleArithmetic + 'static,
|
||||
{
|
||||
fn nonce(&self, account: AccountId) -> Result<Index> {
|
||||
let api = self.client.runtime_api();
|
||||
let best = self.client.info().best_hash;
|
||||
let at = BlockId::hash(best);
|
||||
fn nonce(&self, account: AccountId) -> FutureResult<Index> {
|
||||
let get_nonce = || {
|
||||
let api = self.client.runtime_api();
|
||||
let best = self.client.info().best_hash;
|
||||
let at = BlockId::hash(best);
|
||||
|
||||
let nonce = api.account_nonce(&at, account.clone()).map_err(|e| Error {
|
||||
let nonce = api.account_nonce(&at, account.clone()).map_err(|e| Error {
|
||||
code: ErrorCode::ServerError(RUNTIME_ERROR),
|
||||
message: "Unable to query nonce.".into(),
|
||||
data: Some(format!("{:?}", e).into()),
|
||||
})?;
|
||||
|
||||
Ok(adjust_nonce(&*self.pool, account, nonce))
|
||||
};
|
||||
|
||||
Box::new(result(get_nonce()))
|
||||
}
|
||||
}
|
||||
|
||||
/// An implementation of System-specific RPC methods on light client.
|
||||
pub struct LightSystem<P: TransactionPool, C, F, Block> {
|
||||
client: Arc<C>,
|
||||
remote_blockchain: Arc<dyn RemoteBlockchain<Block>>,
|
||||
fetcher: Arc<F>,
|
||||
pool: Arc<P>,
|
||||
}
|
||||
|
||||
impl<P: TransactionPool, C, F, Block> LightSystem<P, C, F, Block> {
|
||||
/// Create new `LightSystem`.
|
||||
pub fn new(
|
||||
client: Arc<C>,
|
||||
remote_blockchain: Arc<dyn RemoteBlockchain<Block>>,
|
||||
fetcher: Arc<F>,
|
||||
pool: Arc<P>,
|
||||
) -> Self {
|
||||
LightSystem {
|
||||
client,
|
||||
remote_blockchain,
|
||||
fetcher,
|
||||
pool,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, C, F, Block, AccountId, Index> SystemApi<AccountId, Index> for LightSystem<P, C, F, Block>
|
||||
where
|
||||
P: TransactionPool + 'static,
|
||||
C: HeaderBackend<Block>,
|
||||
C: Send + Sync + 'static,
|
||||
F: Fetcher<Block> + 'static,
|
||||
Block: traits::Block,
|
||||
AccountId: Clone + std::fmt::Display + Codec + Send + 'static,
|
||||
Index: Clone + std::fmt::Display + Codec + Send + traits::SimpleArithmetic + 'static,
|
||||
{
|
||||
fn nonce(&self, account: AccountId) -> FutureResult<Index> {
|
||||
let best_hash = self.client.info().best_hash;
|
||||
let best_id = BlockId::hash(best_hash);
|
||||
let future_best_header = future_header(&*self.remote_blockchain, &*self.fetcher, best_id);
|
||||
let fetcher = self.fetcher.clone();
|
||||
let call_data = account.encode();
|
||||
let future_best_header = future_best_header
|
||||
.and_then(move |maybe_best_header| ready(
|
||||
match maybe_best_header {
|
||||
Some(best_header) => Ok(best_header),
|
||||
None => Err(ClientError::UnknownBlock(format!("{}", best_hash))),
|
||||
}
|
||||
));
|
||||
let future_nonce = future_best_header.and_then(move |best_header|
|
||||
fetcher.remote_call(RemoteCallRequest {
|
||||
block: best_hash,
|
||||
header: best_header,
|
||||
method: "AccountNonceApi_account_nonce".into(),
|
||||
call_data,
|
||||
retry_count: None,
|
||||
})
|
||||
).compat();
|
||||
let future_nonce = future_nonce.and_then(|nonce| Decode::decode(&mut &nonce[..])
|
||||
.map_err(|e| ClientError::CallResultDecode("Cannot decode account nonce", e)));
|
||||
let future_nonce = future_nonce.map_err(|e| Error {
|
||||
code: ErrorCode::ServerError(RUNTIME_ERROR),
|
||||
message: "Unable to query nonce.".into(),
|
||||
data: Some(format!("{:?}", e).into()),
|
||||
})?;
|
||||
});
|
||||
|
||||
log::debug!(target: "rpc", "State nonce for {}: {}", account, nonce);
|
||||
// Now we need to query the transaction pool
|
||||
// and find transactions originating from the same sender.
|
||||
//
|
||||
// Since extrinsics are opaque to us, we look for them using
|
||||
// `provides` tag. And increment the nonce if we find a transaction
|
||||
// that matches the current one.
|
||||
let mut current_nonce = nonce.clone();
|
||||
let mut current_tag = (account.clone(), nonce.clone()).encode();
|
||||
for tx in self.pool.ready() {
|
||||
log::debug!(
|
||||
target: "rpc",
|
||||
"Current nonce to {}, checking {} vs {:?}",
|
||||
current_nonce,
|
||||
HexDisplay::from(¤t_tag),
|
||||
tx.provides.iter().map(|x| format!("{}", HexDisplay::from(x))).collect::<Vec<_>>(),
|
||||
);
|
||||
// since transactions in `ready()` need to be ordered by nonce
|
||||
// it's fine to continue with current iterator.
|
||||
if tx.provides.get(0) == Some(¤t_tag) {
|
||||
current_nonce += traits::One::one();
|
||||
current_tag = (account.clone(), current_nonce.clone()).encode();
|
||||
}
|
||||
}
|
||||
let pool = self.pool.clone();
|
||||
let future_nonce = future_nonce.map(move |nonce| adjust_nonce(&*pool, account, nonce));
|
||||
|
||||
Ok(current_nonce)
|
||||
Box::new(future_nonce)
|
||||
}
|
||||
}
|
||||
|
||||
/// Adjust account nonce from state, so that tx with the nonce will be
|
||||
/// placed after all ready txpool transactions.
|
||||
fn adjust_nonce<P, AccountId, Index>(
|
||||
pool: &P,
|
||||
account: AccountId,
|
||||
nonce: Index,
|
||||
) -> Index where
|
||||
P: TransactionPool,
|
||||
AccountId: Clone + std::fmt::Display + Encode,
|
||||
Index: Clone + std::fmt::Display + Encode + traits::SimpleArithmetic + 'static,
|
||||
{
|
||||
log::debug!(target: "rpc", "State nonce for {}: {}", account, nonce);
|
||||
// Now we need to query the transaction pool
|
||||
// and find transactions originating from the same sender.
|
||||
//
|
||||
// Since extrinsics are opaque to us, we look for them using
|
||||
// `provides` tag. And increment the nonce if we find a transaction
|
||||
// that matches the current one.
|
||||
let mut current_nonce = nonce.clone();
|
||||
let mut current_tag = (account.clone(), nonce.clone()).encode();
|
||||
for tx in pool.ready() {
|
||||
log::debug!(
|
||||
target: "rpc",
|
||||
"Current nonce to {}, checking {} vs {:?}",
|
||||
current_nonce,
|
||||
HexDisplay::from(¤t_tag),
|
||||
tx.provides().iter().map(|x| format!("{}", HexDisplay::from(x))).collect::<Vec<_>>(),
|
||||
);
|
||||
// since transactions in `ready()` need to be ordered by nonce
|
||||
// it's fine to continue with current iterator.
|
||||
if tx.provides().get(0) == Some(¤t_tag) {
|
||||
current_nonce += traits::One::one();
|
||||
current_tag = (account.clone(), current_nonce.clone()).encode();
|
||||
}
|
||||
}
|
||||
|
||||
current_nonce
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use sc_transaction_pool;
|
||||
|
||||
use futures::executor::block_on;
|
||||
use test_client::{
|
||||
runtime::Transfer,
|
||||
AccountKeyring,
|
||||
};
|
||||
use txpool::{BasicPool, FullChainApi};
|
||||
|
||||
#[test]
|
||||
fn should_return_next_nonce_for_some_account() {
|
||||
// given
|
||||
let _ = env_logger::try_init();
|
||||
let client = Arc::new(test_client::new());
|
||||
let pool = Arc::new(Pool::new(Default::default(), sc_transaction_pool::FullChainApi::new(client.clone())));
|
||||
let pool = Arc::new(BasicPool::new(Default::default(), FullChainApi::new(client.clone())));
|
||||
|
||||
let new_transaction = |nonce: u64| {
|
||||
let t = Transfer {
|
||||
@@ -148,12 +252,12 @@ mod tests {
|
||||
let ext1 = new_transaction(1);
|
||||
block_on(pool.submit_one(&BlockId::number(0), ext1)).unwrap();
|
||||
|
||||
let accounts = System::new(client, pool);
|
||||
let accounts = FullSystem::new(client, pool);
|
||||
|
||||
// when
|
||||
let nonce = accounts.nonce(AccountKeyring::Alice.into());
|
||||
|
||||
// then
|
||||
assert_eq!(nonce.unwrap(), 2);
|
||||
assert_eq!(nonce.wait().unwrap(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user