mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-24 16:01:08 +00:00
Extrinsic pool (#182)
* Use latest version of txpool. * Initial version of the pool. * Fix abstraction. * Implement watchers and notifications. * Return hash from RPC. * Remove commented code. * Remove client dep. * Fix tests.
This commit is contained in:
Generated
+16
-3
@@ -229,6 +229,7 @@ dependencies = [
|
|||||||
"substrate-client 0.1.0",
|
"substrate-client 0.1.0",
|
||||||
"substrate-codec 0.1.0",
|
"substrate-codec 0.1.0",
|
||||||
"substrate-executor 0.1.0",
|
"substrate-executor 0.1.0",
|
||||||
|
"substrate-extrinsic-pool 0.1.0",
|
||||||
"substrate-primitives 0.1.0",
|
"substrate-primitives 0.1.0",
|
||||||
"substrate-rpc 0.1.0",
|
"substrate-rpc 0.1.0",
|
||||||
"substrate-rpc-servers 0.1.0",
|
"substrate-rpc-servers 0.1.0",
|
||||||
@@ -1429,16 +1430,15 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"ed25519 0.1.0",
|
"ed25519 0.1.0",
|
||||||
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"polkadot-api 0.1.0",
|
"polkadot-api 0.1.0",
|
||||||
"polkadot-primitives 0.1.0",
|
"polkadot-primitives 0.1.0",
|
||||||
"polkadot-runtime 0.1.0",
|
"polkadot-runtime 0.1.0",
|
||||||
"substrate-client 0.1.0",
|
"substrate-client 0.1.0",
|
||||||
"substrate-codec 0.1.0",
|
"substrate-codec 0.1.0",
|
||||||
|
"substrate-extrinsic-pool 0.1.0",
|
||||||
"substrate-primitives 0.1.0",
|
"substrate-primitives 0.1.0",
|
||||||
"substrate-rpc 0.1.0",
|
|
||||||
"substrate-runtime-primitives 0.1.0",
|
"substrate-runtime-primitives 0.1.0",
|
||||||
"transaction-pool 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1942,6 +1942,18 @@ dependencies = [
|
|||||||
"wasmi 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"wasmi 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "substrate-extrinsic-pool"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"substrate-primitives 0.1.0",
|
||||||
|
"transaction-pool 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "substrate-keyring"
|
name = "substrate-keyring"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -2025,6 +2037,7 @@ dependencies = [
|
|||||||
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"substrate-client 0.1.0",
|
"substrate-client 0.1.0",
|
||||||
"substrate-executor 0.1.0",
|
"substrate-executor 0.1.0",
|
||||||
|
"substrate-extrinsic-pool 0.1.0",
|
||||||
"substrate-primitives 0.1.0",
|
"substrate-primitives 0.1.0",
|
||||||
"substrate-state-machine 0.1.0",
|
"substrate-state-machine 0.1.0",
|
||||||
"substrate-test-client 0.1.0",
|
"substrate-test-client 0.1.0",
|
||||||
|
|||||||
@@ -22,22 +22,22 @@ members = [
|
|||||||
"polkadot/parachain",
|
"polkadot/parachain",
|
||||||
"polkadot/primitives",
|
"polkadot/primitives",
|
||||||
"polkadot/runtime",
|
"polkadot/runtime",
|
||||||
|
"polkadot/service",
|
||||||
"polkadot/statement-table",
|
"polkadot/statement-table",
|
||||||
"polkadot/transaction-pool",
|
"polkadot/transaction-pool",
|
||||||
"polkadot/service",
|
|
||||||
|
|
||||||
"substrate/bft",
|
"substrate/bft",
|
||||||
"substrate/client",
|
"substrate/client",
|
||||||
"substrate/client/db",
|
"substrate/client/db",
|
||||||
"substrate/codec",
|
"substrate/codec",
|
||||||
"substrate/environmental",
|
"substrate/environmental",
|
||||||
"substrate/executor",
|
"substrate/executor",
|
||||||
|
"substrate/extrinsic-pool",
|
||||||
"substrate/keyring",
|
"substrate/keyring",
|
||||||
"substrate/network",
|
|
||||||
"substrate/misbehavior-check",
|
"substrate/misbehavior-check",
|
||||||
|
"substrate/network",
|
||||||
"substrate/primitives",
|
"substrate/primitives",
|
||||||
"substrate/rpc-servers",
|
|
||||||
"substrate/rpc",
|
"substrate/rpc",
|
||||||
|
"substrate/rpc-servers",
|
||||||
"substrate/runtime-io",
|
"substrate/runtime-io",
|
||||||
"substrate/runtime-sandbox",
|
"substrate/runtime-sandbox",
|
||||||
"substrate/runtime-std",
|
"substrate/runtime-std",
|
||||||
@@ -55,10 +55,10 @@ members = [
|
|||||||
"substrate/state-machine",
|
"substrate/state-machine",
|
||||||
"substrate/test-runtime",
|
"substrate/test-runtime",
|
||||||
|
|
||||||
"demo/runtime",
|
|
||||||
"demo/primitives",
|
|
||||||
"demo/executor",
|
|
||||||
"demo/cli",
|
"demo/cli",
|
||||||
|
"demo/executor",
|
||||||
|
"demo/primitives",
|
||||||
|
"demo/runtime",
|
||||||
"safe-mix",
|
"safe-mix",
|
||||||
"subkey",
|
"subkey",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ tokio-core = "0.1.12"
|
|||||||
triehash = "0.1"
|
triehash = "0.1"
|
||||||
substrate-client = { path = "../../substrate/client" }
|
substrate-client = { path = "../../substrate/client" }
|
||||||
substrate-codec = { path = "../../substrate/codec" }
|
substrate-codec = { path = "../../substrate/codec" }
|
||||||
|
substrate-extrinsic-pool = { path = "../../substrate/extrinsic-pool" }
|
||||||
substrate-runtime-io = { path = "../../substrate/runtime-io" }
|
substrate-runtime-io = { path = "../../substrate/runtime-io" }
|
||||||
substrate-state-machine = { path = "../../substrate/state-machine" }
|
substrate-state-machine = { path = "../../substrate/state-machine" }
|
||||||
substrate-executor = { path = "../../substrate/executor" }
|
substrate-executor = { path = "../../substrate/executor" }
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ extern crate substrate_rpc;
|
|||||||
extern crate substrate_rpc_servers as rpc;
|
extern crate substrate_rpc_servers as rpc;
|
||||||
extern crate substrate_runtime_io as runtime_io;
|
extern crate substrate_runtime_io as runtime_io;
|
||||||
extern crate substrate_state_machine as state_machine;
|
extern crate substrate_state_machine as state_machine;
|
||||||
|
extern crate substrate_extrinsic_pool as extrinsic_pool;
|
||||||
extern crate demo_executor;
|
extern crate demo_executor;
|
||||||
extern crate demo_primitives;
|
extern crate demo_primitives;
|
||||||
extern crate demo_runtime;
|
extern crate demo_runtime;
|
||||||
@@ -53,11 +54,14 @@ use demo_runtime::{GenesisConfig, ConsensusConfig, CouncilConfig, DemocracyConfi
|
|||||||
SessionConfig, StakingConfig, BuildExternalities};
|
SessionConfig, StakingConfig, BuildExternalities};
|
||||||
use futures::{Future, Sink, Stream};
|
use futures::{Future, Sink, Stream};
|
||||||
|
|
||||||
|
|
||||||
struct DummyPool;
|
struct DummyPool;
|
||||||
impl substrate_rpc::author::AuthorApi for DummyPool {
|
impl extrinsic_pool::api::ExtrinsicPool for DummyPool {
|
||||||
fn submit_extrinsic(&self, _: primitives::block::Extrinsic) -> substrate_rpc::author::error::Result<()> {
|
type Error = extrinsic_pool::txpool::Error;
|
||||||
Err(substrate_rpc::author::error::ErrorKind::Unimplemented.into())
|
|
||||||
|
fn submit(&self, _: Vec<primitives::block::Extrinsic>)
|
||||||
|
-> Result<Vec<primitives::block::ExtrinsicHash>, Self::Error>
|
||||||
|
{
|
||||||
|
Err("unimplemented".into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,7 +160,7 @@ pub fn run<I, T>(args: I) -> error::Result<()> where
|
|||||||
let _rpc_servers = {
|
let _rpc_servers = {
|
||||||
let handler = || {
|
let handler = || {
|
||||||
let chain = rpc::apis::chain::Chain::new(client.clone(), core.remote());
|
let chain = rpc::apis::chain::Chain::new(client.clone(), core.remote());
|
||||||
rpc::rpc_handler(client.clone(), chain, DummyPool, DummySystem)
|
rpc::rpc_handler(client.clone(), chain, Arc::new(DummyPool), DummySystem)
|
||||||
};
|
};
|
||||||
let http_address = "127.0.0.1:9933".parse().unwrap();
|
let http_address = "127.0.0.1:9933".parse().unwrap();
|
||||||
let ws_address = "127.0.0.1:9944".parse().unwrap();
|
let ws_address = "127.0.0.1:9944".parse().unwrap();
|
||||||
|
|||||||
@@ -61,39 +61,11 @@ mod informant;
|
|||||||
use std::io;
|
use std::io;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use futures::sync::mpsc;
|
use futures::sync::mpsc;
|
||||||
use futures::{Sink, Future, Stream};
|
use futures::{Sink, Future, Stream};
|
||||||
use tokio_core::reactor;
|
use tokio_core::reactor;
|
||||||
use parking_lot::Mutex;
|
|
||||||
use service::ChainSpec;
|
use service::ChainSpec;
|
||||||
use primitives::block::Extrinsic;
|
|
||||||
|
|
||||||
struct RpcTransactionPool {
|
|
||||||
inner: Arc<Mutex<txpool::TransactionPool>>,
|
|
||||||
network: Arc<network::Service>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl substrate_rpc::author::AuthorApi for RpcTransactionPool {
|
|
||||||
fn submit_extrinsic(&self, xt: Extrinsic) -> substrate_rpc::author::error::Result<()> {
|
|
||||||
use primitives::hexdisplay::HexDisplay;
|
|
||||||
use polkadot_runtime::UncheckedExtrinsic;
|
|
||||||
use codec::Slicable;
|
|
||||||
|
|
||||||
info!("Extrinsic submitted: {}", HexDisplay::from(&xt.0));
|
|
||||||
let decoded = xt.using_encoded(|ref mut s| UncheckedExtrinsic::decode(s))
|
|
||||||
.ok_or(substrate_rpc::author::error::ErrorKind::InvalidFormat)?;
|
|
||||||
|
|
||||||
info!("Correctly formatted: {:?}", decoded);
|
|
||||||
|
|
||||||
self.inner.lock().import(decoded)
|
|
||||||
.map_err(|_| substrate_rpc::author::error::ErrorKind::PoolError)?;
|
|
||||||
|
|
||||||
self.network.trigger_repropagate();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Configuration(service::Configuration);
|
struct Configuration(service::Configuration);
|
||||||
|
|
||||||
@@ -238,11 +210,12 @@ fn run_until_exit<B, E>(mut core: reactor::Core, service: service::Service<B, E>
|
|||||||
|
|
||||||
let handler = || {
|
let handler = || {
|
||||||
let chain = rpc::apis::chain::Chain::new(service.client(), core.remote());
|
let chain = rpc::apis::chain::Chain::new(service.client(), core.remote());
|
||||||
let pool = RpcTransactionPool {
|
rpc::rpc_handler(
|
||||||
inner: service.transaction_pool(),
|
service.client(),
|
||||||
network: service.network(),
|
chain,
|
||||||
};
|
service.transaction_pool(),
|
||||||
rpc::rpc_handler(service.client(), chain, pool, Configuration(config.clone()))
|
Configuration(config.clone()),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
(
|
(
|
||||||
start_server(http_address, |address| rpc::start_http(address, handler())),
|
start_server(http_address, |address| rpc::start_http(address, handler())),
|
||||||
|
|||||||
@@ -78,7 +78,6 @@ use tokio_core::reactor::{Handle, Timeout, Interval};
|
|||||||
|
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use futures::future::{self, Shared};
|
use futures::future::{self, Shared};
|
||||||
use parking_lot::Mutex;
|
|
||||||
use collation::CollationFetch;
|
use collation::CollationFetch;
|
||||||
use dynamic_inclusion::DynamicInclusion;
|
use dynamic_inclusion::DynamicInclusion;
|
||||||
|
|
||||||
@@ -226,7 +225,7 @@ pub struct ProposerFactory<C, N, P> {
|
|||||||
/// The client instance.
|
/// The client instance.
|
||||||
pub client: Arc<C>,
|
pub client: Arc<C>,
|
||||||
/// The transaction pool.
|
/// The transaction pool.
|
||||||
pub transaction_pool: Arc<Mutex<TransactionPool>>,
|
pub transaction_pool: Arc<TransactionPool>,
|
||||||
/// The backing network handle.
|
/// The backing network handle.
|
||||||
pub network: N,
|
pub network: N,
|
||||||
/// Parachain collators.
|
/// Parachain collators.
|
||||||
@@ -319,7 +318,7 @@ pub struct Proposer<C: PolkadotApi, R, P> {
|
|||||||
random_seed: Hash,
|
random_seed: Hash,
|
||||||
router: R,
|
router: R,
|
||||||
table: Arc<SharedTable>,
|
table: Arc<SharedTable>,
|
||||||
transaction_pool: Arc<Mutex<TransactionPool>>,
|
transaction_pool: Arc<TransactionPool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, R, P> bft::Proposer for Proposer<C, R, P>
|
impl<C, R, P> bft::Proposer for Proposer<C, R, P>
|
||||||
@@ -495,17 +494,16 @@ impl<C, R, P> bft::Proposer for Proposer<C, R, P>
|
|||||||
use primitives::bft::{MisbehaviorKind, MisbehaviorReport};
|
use primitives::bft::{MisbehaviorKind, MisbehaviorReport};
|
||||||
use polkadot_runtime::{Call, Extrinsic, UncheckedExtrinsic, ConsensusCall};
|
use polkadot_runtime::{Call, Extrinsic, UncheckedExtrinsic, ConsensusCall};
|
||||||
|
|
||||||
|
|
||||||
let local_id = self.local_key.public().0;
|
let local_id = self.local_key.public().0;
|
||||||
let mut pool = self.transaction_pool.lock();
|
|
||||||
let mut next_index = {
|
let mut next_index = {
|
||||||
let readiness_evaluator = Ready::create(self.parent_id.clone(), &*self.client);
|
let readiness_evaluator = Ready::create(self.parent_id.clone(), &*self.client);
|
||||||
|
let cur_index = self.transaction_pool.cull_and_get_pending(readiness_evaluator, |pending| pending
|
||||||
pool.cull(None, readiness_evaluator.clone());
|
|
||||||
let cur_index = pool.pending(readiness_evaluator)
|
|
||||||
.filter(|tx| tx.as_ref().as_ref().signed == local_id)
|
.filter(|tx| tx.as_ref().as_ref().signed == local_id)
|
||||||
.last()
|
.last()
|
||||||
.map(|tx| Ok(tx.as_ref().as_ref().index))
|
.map(|tx| Ok(tx.as_ref().as_ref().index))
|
||||||
.unwrap_or_else(|| self.client.index(&self.parent_id, local_id));
|
.unwrap_or_else(|| self.client.index(&self.parent_id, local_id))
|
||||||
|
);
|
||||||
|
|
||||||
match cur_index {
|
match cur_index {
|
||||||
Ok(cur_index) => cur_index + 1,
|
Ok(cur_index) => cur_index + 1,
|
||||||
@@ -541,7 +539,7 @@ impl<C, R, P> bft::Proposer for Proposer<C, R, P>
|
|||||||
let signature = self.local_key.sign(&extrinsic.encode()).into();
|
let signature = self.local_key.sign(&extrinsic.encode()).into();
|
||||||
let uxt = UncheckedExtrinsic { extrinsic, signature };
|
let uxt = UncheckedExtrinsic { extrinsic, signature };
|
||||||
|
|
||||||
pool.import(uxt).expect("locally signed extrinsic is valid; qed");
|
self.transaction_pool.import_unchecked_extrinsic(uxt).expect("locally signed extrinsic is valid; qed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -609,7 +607,7 @@ pub struct CreateProposal<C: PolkadotApi, R, P: Collators> {
|
|||||||
parent_number: BlockNumber,
|
parent_number: BlockNumber,
|
||||||
parent_id: C::CheckedBlockId,
|
parent_id: C::CheckedBlockId,
|
||||||
client: Arc<C>,
|
client: Arc<C>,
|
||||||
transaction_pool: Arc<Mutex<TransactionPool>>,
|
transaction_pool: Arc<TransactionPool>,
|
||||||
collation: CollationFetch<P, C>,
|
collation: CollationFetch<P, C>,
|
||||||
router: R,
|
router: R,
|
||||||
table: Arc<SharedTable>,
|
table: Arc<SharedTable>,
|
||||||
@@ -631,37 +629,33 @@ impl<C, R, P> CreateProposal<C, R, P>
|
|||||||
candidates,
|
candidates,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let readiness_evaluator = Ready::create(self.parent_id.clone(), &*self.client);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut pool = self.transaction_pool.lock();
|
let readiness_evaluator = Ready::create(self.parent_id.clone(), &*self.client);
|
||||||
let mut unqueue_invalid = Vec::new();
|
let mut unqueue_invalid = Vec::new();
|
||||||
let mut pending_size = 0;
|
self.transaction_pool.cull_and_get_pending(readiness_evaluator, |pending_iterator| {
|
||||||
|
let mut pending_size = 0;
|
||||||
pool.cull(None, readiness_evaluator.clone());
|
for pending in pending_iterator {
|
||||||
for pending in pool.pending(readiness_evaluator.clone()) {
|
// skip and cull transactions which are too large.
|
||||||
// skip and cull transactions which are too large.
|
if pending.encoded_size() > MAX_TRANSACTIONS_SIZE {
|
||||||
if pending.encoded_size() > MAX_TRANSACTIONS_SIZE {
|
|
||||||
unqueue_invalid.push(pending.hash().clone());
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if pending_size + pending.encoded_size() >= MAX_TRANSACTIONS_SIZE { break }
|
|
||||||
|
|
||||||
match block_builder.push_extrinsic(pending.as_transaction().clone()) {
|
|
||||||
Ok(()) => {
|
|
||||||
pending_size += pending.encoded_size();
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
trace!(target: "transaction-pool", "Invalid transaction: {}", e);
|
|
||||||
unqueue_invalid.push(pending.hash().clone());
|
unqueue_invalid.push(pending.hash().clone());
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if pending_size + pending.encoded_size() >= MAX_TRANSACTIONS_SIZE { break }
|
||||||
|
|
||||||
|
match block_builder.push_extrinsic(pending.as_transaction().clone()) {
|
||||||
|
Ok(()) => {
|
||||||
|
pending_size += pending.encoded_size();
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
trace!(target: "transaction-pool", "Invalid transaction: {}", e);
|
||||||
|
unqueue_invalid.push(pending.hash().clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
for tx_hash in unqueue_invalid {
|
self.transaction_pool.remove(&unqueue_invalid, false);
|
||||||
pool.remove(&tx_hash, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let polkadot_block = block_builder.bake();
|
let polkadot_block = block_builder.bake();
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ use client::{BlockchainEvents, ChainHead};
|
|||||||
use ed25519;
|
use ed25519;
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use futures::{future, Canceled};
|
use futures::{future, Canceled};
|
||||||
use parking_lot::Mutex;
|
|
||||||
use polkadot_api::LocalPolkadotApi;
|
use polkadot_api::LocalPolkadotApi;
|
||||||
use polkadot_primitives::AccountId;
|
use polkadot_primitives::AccountId;
|
||||||
use polkadot_primitives::parachain::{Id as ParaId, BlockData, Extrinsic, CandidateReceipt};
|
use polkadot_primitives::parachain::{Id as ParaId, BlockData, Extrinsic, CandidateReceipt};
|
||||||
@@ -237,7 +236,7 @@ impl Service {
|
|||||||
client: Arc<C>,
|
client: Arc<C>,
|
||||||
api: Arc<A>,
|
api: Arc<A>,
|
||||||
network: Arc<net::ConsensusService>,
|
network: Arc<net::ConsensusService>,
|
||||||
transaction_pool: Arc<Mutex<TransactionPool>>,
|
transaction_pool: Arc<TransactionPool>,
|
||||||
parachain_empty_duration: Duration,
|
parachain_empty_duration: Duration,
|
||||||
key: ed25519::Pair,
|
key: ed25519::Pair,
|
||||||
) -> Service
|
) -> Service
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ pub use network::NetworkConfiguration;
|
|||||||
|
|
||||||
/// The chain specification (this should eventually be replaced by a more general JSON-based chain
|
/// The chain specification (this should eventually be replaced by a more general JSON-based chain
|
||||||
/// specification).
|
/// specification).
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum ChainSpec {
|
pub enum ChainSpec {
|
||||||
/// Whatever the current runtime is, with just Alice as an auth.
|
/// Whatever the current runtime is, with just Alice as an auth.
|
||||||
Development,
|
Development,
|
||||||
@@ -33,6 +33,7 @@ pub enum ChainSpec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Service configuration.
|
/// Service configuration.
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Configuration {
|
pub struct Configuration {
|
||||||
/// Node roles.
|
/// Node roles.
|
||||||
pub roles: Role,
|
pub roles: Role,
|
||||||
@@ -63,21 +64,3 @@ impl Default for Configuration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for Configuration {
|
|
||||||
fn clone(&self) -> Configuration {
|
|
||||||
Configuration {
|
|
||||||
roles: self.roles.clone(),
|
|
||||||
transaction_pool: transaction_pool::Options {
|
|
||||||
max_count: self.transaction_pool.max_count.clone(),
|
|
||||||
max_mem_usage: self.transaction_pool.max_mem_usage.clone(),
|
|
||||||
max_per_sender: self.transaction_pool.max_per_sender.clone(),
|
|
||||||
},
|
|
||||||
network: self.network.clone(),
|
|
||||||
keystore_path: self.keystore_path.clone(),
|
|
||||||
database_path: self.database_path.clone(),
|
|
||||||
keys: self.keys.clone(),
|
|
||||||
chain_spec: self.chain_spec.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -50,14 +50,14 @@ extern crate hex_literal;
|
|||||||
mod error;
|
mod error;
|
||||||
mod config;
|
mod config;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use parking_lot::Mutex;
|
|
||||||
use tokio_core::reactor::Core;
|
use tokio_core::reactor::Core;
|
||||||
use codec::Slicable;
|
use codec::Slicable;
|
||||||
use primitives::block::{Id as BlockId, Extrinsic, ExtrinsicHash, HeaderHash, Header};
|
use primitives::block::{Id as BlockId, ExtrinsicHash, HeaderHash, Header};
|
||||||
use primitives::{AuthorityId, hashing};
|
use primitives::{AuthorityId};
|
||||||
use transaction_pool::TransactionPool;
|
use transaction_pool::TransactionPool;
|
||||||
use substrate_executor::NativeExecutor;
|
use substrate_executor::NativeExecutor;
|
||||||
use polkadot_executor::Executor as LocalDispatch;
|
use polkadot_executor::Executor as LocalDispatch;
|
||||||
@@ -80,13 +80,13 @@ pub struct Service<B, E> {
|
|||||||
thread: Option<thread::JoinHandle<()>>,
|
thread: Option<thread::JoinHandle<()>>,
|
||||||
client: Arc<Client<B, E>>,
|
client: Arc<Client<B, E>>,
|
||||||
network: Arc<network::Service>,
|
network: Arc<network::Service>,
|
||||||
transaction_pool: Arc<Mutex<TransactionPool>>,
|
transaction_pool: Arc<TransactionPool>,
|
||||||
signal: Option<Signal>,
|
signal: Option<Signal>,
|
||||||
_consensus: Option<consensus::Service>,
|
_consensus: Option<consensus::Service>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TransactionPoolAdapter<B, E, A> where A: Send + Sync, E: Send + Sync {
|
struct TransactionPoolAdapter<B, E, A> where A: Send + Sync, E: Send + Sync {
|
||||||
pool: Arc<Mutex<TransactionPool>>,
|
pool: Arc<TransactionPool>,
|
||||||
client: Arc<Client<B, E>>,
|
client: Arc<Client<B, E>>,
|
||||||
api: Arc<A>,
|
api: Arc<A>,
|
||||||
}
|
}
|
||||||
@@ -108,19 +108,22 @@ impl<B, E, A> network::TransactionPool for TransactionPoolAdapter<B, E, A>
|
|||||||
};
|
};
|
||||||
|
|
||||||
let id = self.api.check_id(BlockId::Hash(best_block)).expect("Best block is always valid; qed.");
|
let id = self.api.check_id(BlockId::Hash(best_block)).expect("Best block is always valid; qed.");
|
||||||
let mut pool = self.pool.lock();
|
let ready = transaction_pool::Ready::create(id, &*self.api);
|
||||||
pool.cull(None, transaction_pool::Ready::create(id.clone(), &*self.api));
|
|
||||||
pool.pending(transaction_pool::Ready::create(id, &*self.api)).map(|t| {
|
self.pool.cull_and_get_pending(ready, |pending| pending
|
||||||
let hash = ::primitives::Hash::from(&t.hash()[..]);
|
.map(|t| {
|
||||||
let tx = codec::Slicable::encode(t.as_transaction());
|
let hash = ::primitives::Hash::from(&t.hash()[..]);
|
||||||
(hash, tx)
|
let tx = codec::Slicable::encode(t.as_transaction());
|
||||||
}).collect()
|
(hash, tx)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn import(&self, transaction: &[u8]) -> Option<ExtrinsicHash> {
|
fn import(&self, transaction: &[u8]) -> Option<ExtrinsicHash> {
|
||||||
if let Some(tx) = codec::Slicable::decode(&mut &transaction[..]) {
|
if let Some(uxt) = codec::Slicable::decode(&mut &transaction[..]) {
|
||||||
match self.pool.lock().import(tx) {
|
match self.pool.import_unchecked_extrinsic(uxt) {
|
||||||
Ok(t) => Some(t.hash()[..].into()),
|
Ok(xt) => Some(*xt.hash()),
|
||||||
Err(e) => match *e.kind() {
|
Err(e) => match *e.kind() {
|
||||||
transaction_pool::ErrorKind::AlreadyImported(hash) => Some(hash[..].into()),
|
transaction_pool::ErrorKind::AlreadyImported(hash) => Some(hash[..].into()),
|
||||||
_ => {
|
_ => {
|
||||||
@@ -134,6 +137,10 @@ impl<B, E, A> network::TransactionPool for TransactionPoolAdapter<B, E, A>
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_broadcasted(&self, propagations: HashMap<ExtrinsicHash, Vec<String>>) {
|
||||||
|
self.pool.on_broadcasted(propagations)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ChainConfig {
|
pub struct ChainConfig {
|
||||||
@@ -341,7 +348,7 @@ impl<B, E> Service<B, E>
|
|||||||
C: Fn(
|
C: Fn(
|
||||||
Arc<Client<B, E>>,
|
Arc<Client<B, E>>,
|
||||||
Arc<network::Service>,
|
Arc<network::Service>,
|
||||||
Arc<Mutex<TransactionPool>>,
|
Arc<TransactionPool>,
|
||||||
&Keystore
|
&Keystore
|
||||||
) -> Result<Option<consensus::Service>, error::Error>,
|
) -> Result<Option<consensus::Service>, error::Error>,
|
||||||
A: PolkadotApi + Send + Sync + 'static,
|
A: PolkadotApi + Send + Sync + 'static,
|
||||||
@@ -383,7 +390,7 @@ impl<B, E> Service<B, E>
|
|||||||
let api = api_creator(client.clone());
|
let api = api_creator(client.clone());
|
||||||
let best_header = client.best_block_header()?;
|
let best_header = client.best_block_header()?;
|
||||||
info!("Starting Polkadot. Best block is #{}", best_header.number);
|
info!("Starting Polkadot. Best block is #{}", best_header.number);
|
||||||
let transaction_pool = Arc::new(Mutex::new(TransactionPool::new(config.transaction_pool)));
|
let transaction_pool = Arc::new(TransactionPool::new(config.transaction_pool));
|
||||||
let transaction_pool_adapter = Arc::new(TransactionPoolAdapter {
|
let transaction_pool_adapter = Arc::new(TransactionPoolAdapter {
|
||||||
pool: transaction_pool.clone(),
|
pool: transaction_pool.clone(),
|
||||||
client: client.clone(),
|
client: client.clone(),
|
||||||
@@ -414,14 +421,28 @@ impl<B, E> Service<B, E>
|
|||||||
|
|
||||||
thread_barrier.wait();
|
thread_barrier.wait();
|
||||||
let mut core = Core::new().expect("tokio::Core could not be created");
|
let mut core = Core::new().expect("tokio::Core could not be created");
|
||||||
let events = client.import_notification_stream().for_each(move |notification| {
|
|
||||||
network.on_block_imported(notification.hash, ¬ification.header);
|
|
||||||
prune_imported(&*client, &*txpool, notification.hash);
|
|
||||||
|
|
||||||
Ok(())
|
// block notifications
|
||||||
});
|
let network1 = network.clone();
|
||||||
|
let txpool1 = txpool.clone();
|
||||||
|
let events = client.import_notification_stream()
|
||||||
|
.for_each(move |notification| {
|
||||||
|
network1.on_block_imported(notification.hash, ¬ification.header);
|
||||||
|
prune_imported(&*api, &*txpool1, notification.hash);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
core.handle().spawn(events);
|
core.handle().spawn(events);
|
||||||
|
|
||||||
|
// transaction notifications
|
||||||
|
let events = txpool.import_notification_stream()
|
||||||
|
// TODO [ToDr] Consider throttling?
|
||||||
|
.for_each(move |_| {
|
||||||
|
network.trigger_repropagate();
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
core.handle().spawn(events);
|
||||||
|
|
||||||
if let Err(e) = core.run(exit) {
|
if let Err(e) = core.run(exit) {
|
||||||
debug!("Polkadot service event loop shutdown with {:?}", e);
|
debug!("Polkadot service event loop shutdown with {:?}", e);
|
||||||
}
|
}
|
||||||
@@ -457,30 +478,22 @@ impl<B, E> Service<B, E>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get shared transaction pool instance.
|
/// Get shared transaction pool instance.
|
||||||
pub fn transaction_pool(&self) -> Arc<Mutex<TransactionPool>> {
|
pub fn transaction_pool(&self) -> Arc<TransactionPool> {
|
||||||
self.transaction_pool.clone()
|
self.transaction_pool.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prune_transactions(pool: &mut TransactionPool, extrinsics: &[Extrinsic]) {
|
|
||||||
for extrinsic in extrinsics {
|
|
||||||
let hash: _ = hashing::blake2_256(&extrinsic.encode()).into();
|
|
||||||
pool.remove(&hash, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Produce a task which prunes any finalized transactions from the pool.
|
/// Produce a task which prunes any finalized transactions from the pool.
|
||||||
pub fn prune_imported<B, E>(client: &Client<B, E>, pool: &Mutex<TransactionPool>, hash: HeaderHash)
|
pub fn prune_imported<A>(api: &A, pool: &TransactionPool, hash: HeaderHash)
|
||||||
where
|
where
|
||||||
B: Backend + Send + Sync,
|
A: PolkadotApi,
|
||||||
E: CallExecutor + Send + Sync,
|
|
||||||
client::error::Error: From<<<B as Backend>::State as state_machine::backend::Backend>::Error>
|
|
||||||
{
|
{
|
||||||
let id = BlockId::Hash(hash);
|
match api.check_id(BlockId::Hash(hash)) {
|
||||||
match client.body(&id) {
|
Ok(id) => {
|
||||||
Ok(Some(body)) => prune_transactions(&mut *pool.lock(), &body[..]),
|
let ready = transaction_pool::Ready::create(id, api);
|
||||||
Ok(None) => warn!("Missing imported block {:?}", hash),
|
pool.cull(None, ready);
|
||||||
Err(e) => warn!("Failed to fetch block: {:?}", e),
|
},
|
||||||
|
Err(e) => warn!("Failed to check block id: {:?}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,15 +4,14 @@ version = "0.1.0"
|
|||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4.0"
|
log = "0.3.0"
|
||||||
transaction-pool = "1.12.0"
|
|
||||||
error-chain = "0.11"
|
error-chain = "0.11"
|
||||||
polkadot-api = { path = "../api" }
|
polkadot-api = { path = "../api" }
|
||||||
polkadot-primitives = { path = "../primitives" }
|
polkadot-primitives = { path = "../primitives" }
|
||||||
polkadot-runtime = { path = "../runtime" }
|
polkadot-runtime = { path = "../runtime" }
|
||||||
substrate-client = { path = "../../substrate/client" }
|
substrate-client = { path = "../../substrate/client" }
|
||||||
substrate-rpc = { path = "../../substrate/rpc" }
|
substrate-codec = { path = "../../substrate/codec" }
|
||||||
|
substrate-extrinsic-pool = { path = "../../substrate/extrinsic-pool" }
|
||||||
substrate-primitives = { path = "../../substrate/primitives" }
|
substrate-primitives = { path = "../../substrate/primitives" }
|
||||||
substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" }
|
substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" }
|
||||||
substrate-codec = { path = "../../substrate/codec" }
|
|
||||||
ed25519 = { path = "../../substrate/ed25519" }
|
ed25519 = { path = "../../substrate/ed25519" }
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
// Copyright 2018 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Polkadot.
|
||||||
|
|
||||||
|
// Polkadot 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.
|
||||||
|
|
||||||
|
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use extrinsic_pool::{self, txpool};
|
||||||
|
use primitives::Hash;
|
||||||
|
use runtime::UncheckedExtrinsic;
|
||||||
|
|
||||||
|
error_chain! {
|
||||||
|
links {
|
||||||
|
Pool(txpool::Error, txpool::ErrorKind);
|
||||||
|
}
|
||||||
|
errors {
|
||||||
|
/// Unexpected extrinsic format submitted
|
||||||
|
InvalidExtrinsicFormat {
|
||||||
|
description("Invalid extrinsic format."),
|
||||||
|
display("Invalid extrinsic format."),
|
||||||
|
}
|
||||||
|
/// Attempted to queue an inherent transaction.
|
||||||
|
IsInherent(xt: UncheckedExtrinsic) {
|
||||||
|
description("Inherent transactions cannot be queued."),
|
||||||
|
display("Inehrent transactions cannot be queued."),
|
||||||
|
}
|
||||||
|
/// Attempted to queue a transaction with bad signature.
|
||||||
|
BadSignature(xt: UncheckedExtrinsic) {
|
||||||
|
description("Transaction had bad signature."),
|
||||||
|
display("Transaction had bad signature."),
|
||||||
|
}
|
||||||
|
/// Attempted to queue a transaction that is already in the pool.
|
||||||
|
AlreadyImported(hash: Hash) {
|
||||||
|
description("Transaction is already in the pool."),
|
||||||
|
display("Transaction {:?} is already in the pool.", hash),
|
||||||
|
}
|
||||||
|
/// Import error.
|
||||||
|
Import(err: Box<::std::error::Error + Send>) {
|
||||||
|
description("Error importing transaction"),
|
||||||
|
display("Error importing transaction: {}", err.description()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl extrinsic_pool::api::Error for Error {
|
||||||
|
fn into_pool_error(self) -> ::std::result::Result<txpool::Error, Self> {
|
||||||
|
match self {
|
||||||
|
Error(ErrorKind::Pool(e), c) => Ok(txpool::Error(e, c)),
|
||||||
|
e => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
// Copyright 2018 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Polkadot.
|
// This file is part of Polkadot.
|
||||||
|
|
||||||
// Polkadot is free software: you can redistribute it and/or modify
|
// Polkadot is free software: you can redistribute it and/or modify
|
||||||
@@ -16,12 +16,12 @@
|
|||||||
|
|
||||||
extern crate ed25519;
|
extern crate ed25519;
|
||||||
extern crate substrate_codec as codec;
|
extern crate substrate_codec as codec;
|
||||||
|
extern crate substrate_extrinsic_pool as extrinsic_pool;
|
||||||
extern crate substrate_primitives as substrate_primitives;
|
extern crate substrate_primitives as substrate_primitives;
|
||||||
extern crate substrate_runtime_primitives as substrate_runtime_primitives;
|
extern crate substrate_runtime_primitives;
|
||||||
extern crate polkadot_runtime as runtime;
|
extern crate polkadot_runtime as runtime;
|
||||||
extern crate polkadot_primitives as primitives;
|
extern crate polkadot_primitives as primitives;
|
||||||
extern crate polkadot_api;
|
extern crate polkadot_api;
|
||||||
extern crate transaction_pool;
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate error_chain;
|
extern crate error_chain;
|
||||||
@@ -29,19 +29,27 @@ extern crate error_chain;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
mod error;
|
||||||
use std::cmp::Ordering;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
cmp::Ordering,
|
||||||
|
collections::HashMap,
|
||||||
|
ops::Deref,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
use codec::Slicable;
|
||||||
|
use extrinsic_pool::{Pool, txpool::{self, Readiness, scoring::{Change, Choice}}};
|
||||||
use polkadot_api::PolkadotApi;
|
use polkadot_api::PolkadotApi;
|
||||||
use primitives::{AccountId, Timestamp, Hash};
|
|
||||||
use primitives::parachain::CandidateReceipt;
|
use primitives::parachain::CandidateReceipt;
|
||||||
|
use primitives::{AccountId, Timestamp, Hash};
|
||||||
use runtime::{Block, UncheckedExtrinsic, TimestampCall, ParachainsCall, Call};
|
use runtime::{Block, UncheckedExtrinsic, TimestampCall, ParachainsCall, Call};
|
||||||
|
use substrate_primitives::block::{Extrinsic, ExtrinsicHash};
|
||||||
|
use substrate_primitives::hexdisplay::HexDisplay;
|
||||||
use substrate_runtime_primitives::traits::{Bounded, Checkable};
|
use substrate_runtime_primitives::traits::{Bounded, Checkable};
|
||||||
use transaction_pool::{Transaction, Pool, Readiness};
|
|
||||||
use transaction_pool::scoring::{Change, Choice};
|
|
||||||
|
|
||||||
pub use transaction_pool::{Options, Status, LightStatus, NoopListener, VerifiedTransaction as VerifiedTransactionOps};
|
pub use extrinsic_pool::txpool::{Options, Status, LightStatus, VerifiedTransaction as VerifiedTransactionOps};
|
||||||
|
pub use error::{Error, ErrorKind, Result};
|
||||||
|
|
||||||
/// Useful functions for working with Polkadot blocks.
|
/// Useful functions for working with Polkadot blocks.
|
||||||
pub struct PolkadotBlock {
|
pub struct PolkadotBlock {
|
||||||
@@ -127,48 +135,17 @@ impl From<PolkadotBlock> for Block {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterator over pending transactions.
|
|
||||||
pub type PendingIterator<'a, C> =
|
|
||||||
transaction_pool::PendingIterator<'a, VerifiedTransaction, Ready<'a, C>, Scoring, NoopListener>;
|
|
||||||
|
|
||||||
error_chain! {
|
|
||||||
errors {
|
|
||||||
/// Attempted to queue an inherent transaction.
|
|
||||||
IsInherent(xt: UncheckedExtrinsic) {
|
|
||||||
description("Inherent transactions cannot be queued."),
|
|
||||||
display("Inehrent transactions cannot be queued."),
|
|
||||||
}
|
|
||||||
/// Attempted to queue a transaction with bad signature.
|
|
||||||
BadSignature(xt: UncheckedExtrinsic) {
|
|
||||||
description("Transaction had bad signature."),
|
|
||||||
display("Transaction had bad signature."),
|
|
||||||
}
|
|
||||||
/// Attempted to queue a transaction that is already in the pool.
|
|
||||||
AlreadyImported(hash: Hash) {
|
|
||||||
description("Transaction is already in the pool."),
|
|
||||||
display("Transaction {:?} is already in the pool.", hash),
|
|
||||||
}
|
|
||||||
/// Import error.
|
|
||||||
Import(err: Box<::std::error::Error + Send>) {
|
|
||||||
description("Error importing transaction"),
|
|
||||||
display("Error importing transaction: {}", err.description()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A verified transaction which should be includable and non-inherent.
|
/// A verified transaction which should be includable and non-inherent.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct VerifiedTransaction {
|
pub struct VerifiedTransaction {
|
||||||
inner: <UncheckedExtrinsic as Checkable>::Checked,
|
inner: <UncheckedExtrinsic as Checkable>::Checked,
|
||||||
hash: Hash,
|
hash: ExtrinsicHash,
|
||||||
address: AccountId,
|
|
||||||
insertion_id: u64,
|
|
||||||
encoded_size: usize,
|
encoded_size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VerifiedTransaction {
|
impl VerifiedTransaction {
|
||||||
/// Attempt to verify a transaction.
|
/// Attempt to verify a transaction.
|
||||||
fn create(xt: UncheckedExtrinsic, insertion_id: u64) -> Result<Self> {
|
fn create(xt: UncheckedExtrinsic) -> Result<Self> {
|
||||||
if !xt.is_signed() {
|
if !xt.is_signed() {
|
||||||
bail!(ErrorKind::IsInherent(xt))
|
bail!(ErrorKind::IsInherent(xt))
|
||||||
}
|
}
|
||||||
@@ -176,15 +153,11 @@ impl VerifiedTransaction {
|
|||||||
let message = codec::Slicable::encode(&xt);
|
let message = codec::Slicable::encode(&xt);
|
||||||
match xt.check() {
|
match xt.check() {
|
||||||
Ok(xt) => {
|
Ok(xt) => {
|
||||||
// TODO: make transaction-pool use generic types.
|
|
||||||
let hash = substrate_primitives::hashing::blake2_256(&message);
|
let hash = substrate_primitives::hashing::blake2_256(&message);
|
||||||
let address = xt.signed;
|
|
||||||
Ok(VerifiedTransaction {
|
Ok(VerifiedTransaction {
|
||||||
inner: xt,
|
inner: xt,
|
||||||
hash: hash.into(),
|
hash: hash.into(),
|
||||||
encoded_size: message.len(),
|
encoded_size: message.len(),
|
||||||
address,
|
|
||||||
insertion_id,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Err(xt) => Err(ErrorKind::BadSignature(xt).into()),
|
Err(xt) => Err(ErrorKind::BadSignature(xt).into()),
|
||||||
@@ -208,7 +181,7 @@ impl VerifiedTransaction {
|
|||||||
|
|
||||||
/// Get the account ID of the sender of this transaction.
|
/// Get the account ID of the sender of this transaction.
|
||||||
pub fn sender(&self) -> &AccountId {
|
pub fn sender(&self) -> &AccountId {
|
||||||
&self.address
|
&self.inner.signed
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get encoded size of the transaction.
|
/// Get encoded size of the transaction.
|
||||||
@@ -223,16 +196,16 @@ impl AsRef< <UncheckedExtrinsic as Checkable>::Checked > for VerifiedTransaction
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl transaction_pool::VerifiedTransaction for VerifiedTransaction {
|
impl txpool::VerifiedTransaction for VerifiedTransaction {
|
||||||
type Hash = Hash;
|
type Hash = Hash;
|
||||||
type Sender = AccountId;
|
type Sender = AccountId;
|
||||||
|
|
||||||
fn hash(&self) -> &Hash {
|
fn hash(&self) -> &Self::Hash {
|
||||||
&self.hash
|
&self.hash
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sender(&self) -> &AccountId {
|
fn sender(&self) -> &Self::Sender {
|
||||||
&self.address
|
&self.inner.signed
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mem_usage(&self) -> usize {
|
fn mem_usage(&self) -> usize {
|
||||||
@@ -244,7 +217,7 @@ impl transaction_pool::VerifiedTransaction for VerifiedTransaction {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Scoring;
|
pub struct Scoring;
|
||||||
|
|
||||||
impl transaction_pool::Scoring<VerifiedTransaction> for Scoring {
|
impl txpool::Scoring<VerifiedTransaction> for Scoring {
|
||||||
type Score = u64;
|
type Score = u64;
|
||||||
type Event = ();
|
type Event = ();
|
||||||
|
|
||||||
@@ -258,7 +231,7 @@ impl transaction_pool::Scoring<VerifiedTransaction> for Scoring {
|
|||||||
|
|
||||||
fn update_scores(
|
fn update_scores(
|
||||||
&self,
|
&self,
|
||||||
xts: &[Transaction<VerifiedTransaction>],
|
xts: &[txpool::Transaction<VerifiedTransaction>],
|
||||||
scores: &mut [Self::Score],
|
scores: &mut [Self::Score],
|
||||||
_change: Change<()>
|
_change: Change<()>
|
||||||
) {
|
) {
|
||||||
@@ -276,7 +249,7 @@ impl transaction_pool::Scoring<VerifiedTransaction> for Scoring {
|
|||||||
/// Readiness evaluator for polkadot transactions.
|
/// Readiness evaluator for polkadot transactions.
|
||||||
pub struct Ready<'a, T: 'a + PolkadotApi> {
|
pub struct Ready<'a, T: 'a + PolkadotApi> {
|
||||||
at_block: T::CheckedBlockId,
|
at_block: T::CheckedBlockId,
|
||||||
api_handle: &'a T,
|
api: &'a T,
|
||||||
known_indices: HashMap<AccountId, ::primitives::Index>,
|
known_indices: HashMap<AccountId, ::primitives::Index>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,7 +257,7 @@ impl<'a, T: 'a + PolkadotApi> Clone for Ready<'a, T> {
|
|||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Ready {
|
Ready {
|
||||||
at_block: self.at_block.clone(),
|
at_block: self.at_block.clone(),
|
||||||
api_handle: self.api_handle,
|
api: self.api,
|
||||||
known_indices: self.known_indices.clone(),
|
known_indices: self.known_indices.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -293,106 +266,85 @@ impl<'a, T: 'a + PolkadotApi> Clone for Ready<'a, T> {
|
|||||||
impl<'a, T: 'a + PolkadotApi> Ready<'a, T> {
|
impl<'a, T: 'a + PolkadotApi> Ready<'a, T> {
|
||||||
/// Create a new readiness evaluator at the given block. Requires that
|
/// Create a new readiness evaluator at the given block. Requires that
|
||||||
/// the ID has already been checked for local corresponding and available state.
|
/// the ID has already been checked for local corresponding and available state.
|
||||||
pub fn create(at: T::CheckedBlockId, client: &'a T) -> Self {
|
pub fn create(at: T::CheckedBlockId, api: &'a T) -> Self {
|
||||||
Ready {
|
Ready {
|
||||||
at_block: at,
|
at_block: at,
|
||||||
api_handle: client,
|
api,
|
||||||
known_indices: HashMap::new(),
|
known_indices: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: 'a + PolkadotApi> transaction_pool::Ready<VerifiedTransaction> for Ready<'a, T> {
|
impl<'a, T: 'a + PolkadotApi> txpool::Ready<VerifiedTransaction> for Ready<'a, T> {
|
||||||
fn is_ready(&mut self, xt: &VerifiedTransaction) -> Readiness {
|
fn is_ready(&mut self, xt: &VerifiedTransaction) -> Readiness {
|
||||||
let sender = xt.inner.signed;
|
let sender = xt.inner.signed;
|
||||||
trace!(target: "transaction-pool", "Checking readiness of {} (from {})", xt.hash, Hash::from(sender));
|
trace!(target: "transaction-pool", "Checking readiness of {} (from {})", xt.hash, Hash::from(sender));
|
||||||
|
|
||||||
// TODO: find a way to handle index error properly -- will need changes to
|
// TODO: find a way to handle index error properly -- will need changes to
|
||||||
// transaction-pool trait.
|
// transaction-pool trait.
|
||||||
let (api_handle, at_block) = (&self.api_handle, &self.at_block);
|
let (api, at_block) = (&self.api, &self.at_block);
|
||||||
let next_index = self.known_indices.entry(sender)
|
let next_index = self.known_indices.entry(sender)
|
||||||
.or_insert_with(|| api_handle.index(at_block, sender).ok().unwrap_or_else(Bounded::max_value));
|
.or_insert_with(|| api.index(at_block, sender).ok().unwrap_or_else(Bounded::max_value));
|
||||||
|
|
||||||
trace!(target: "transaction-pool", "Next index for sender is {}; xt index is {}", next_index, xt.inner.index);
|
trace!(target: "transaction-pool", "Next index for sender is {}; xt index is {}", next_index, xt.inner.index);
|
||||||
|
|
||||||
match xt.inner.index.cmp(&next_index) {
|
let result = match xt.inner.index.cmp(&next_index) {
|
||||||
Ordering::Greater => Readiness::Future,
|
Ordering::Greater => Readiness::Future,
|
||||||
Ordering::Equal => Readiness::Ready,
|
Ordering::Equal => Readiness::Ready,
|
||||||
Ordering::Less => Readiness::Stale, // TODO: this is not "stalled" but rather stale and can be discarded.
|
Ordering::Less => Readiness::Stale,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
// remember to increment `next_index`
|
||||||
|
*next_index = next_index.saturating_add(1);
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Verifier;
|
||||||
|
|
||||||
|
impl txpool::Verifier<Extrinsic> for Verifier {
|
||||||
|
type VerifiedTransaction = VerifiedTransaction;
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn verify_transaction(&self, xt: Extrinsic) -> Result<Self::VerifiedTransaction> {
|
||||||
|
info!("Extrinsic submitted: {}", HexDisplay::from(&xt.0));
|
||||||
|
let uxt = xt.using_encoded(|ref mut s| UncheckedExtrinsic::decode(s))
|
||||||
|
.ok_or_else(|| ErrorKind::InvalidExtrinsicFormat)?;
|
||||||
|
info!("Correctly formatted: {:?}", uxt);
|
||||||
|
VerifiedTransaction::create(uxt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The polkadot transaction pool.
|
/// The polkadot transaction pool.
|
||||||
///
|
///
|
||||||
/// Wraps a `transaction-pool::Pool`.
|
/// Wraps a `extrinsic_pool::Pool`.
|
||||||
pub struct TransactionPool {
|
pub struct TransactionPool {
|
||||||
inner: transaction_pool::Pool<VerifiedTransaction, Scoring>,
|
inner: Pool<Verifier, Scoring, Error>,
|
||||||
insertion_index: u64, // TODO: use AtomicU64 when it stabilizes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransactionPool {
|
impl TransactionPool {
|
||||||
/// Create a new transaction pool.
|
/// Create a new transaction pool.
|
||||||
pub fn new(options: Options) -> Self {
|
pub fn new(options: Options) -> Self {
|
||||||
TransactionPool {
|
TransactionPool {
|
||||||
inner: Pool::new(NoopListener, Scoring, options),
|
inner: Pool::new(options, Verifier, Scoring),
|
||||||
insertion_index: 0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify and import a transaction into the pool.
|
pub fn import_unchecked_extrinsic(&self, uxt: UncheckedExtrinsic) -> Result<Arc<VerifiedTransaction>> {
|
||||||
pub fn import(&mut self, xt: UncheckedExtrinsic) -> Result<Arc<VerifiedTransaction>> {
|
Ok(self.inner.import(VerifiedTransaction::create(uxt)?)?)
|
||||||
let insertion_index = self.insertion_index;
|
|
||||||
self.insertion_index += 1;
|
|
||||||
|
|
||||||
let verified = VerifiedTransaction::create(xt, insertion_index)?;
|
|
||||||
|
|
||||||
info!("Extrinsic verified {:?}. Importing...", verified);
|
|
||||||
|
|
||||||
// TODO: just use a foreign link when the error type is made public.
|
|
||||||
let hash = verified.hash.clone();
|
|
||||||
self.inner.import(verified)
|
|
||||||
.map_err(|e|
|
|
||||||
match e {
|
|
||||||
// TODO: make error types public in transaction_pool. For now just treat all errors as AlreadyImported
|
|
||||||
_ => ErrorKind::AlreadyImported(hash),
|
|
||||||
// transaction_pool::error::AlreadyImported(h) => ErrorKind::AlreadyImported(h),
|
|
||||||
// e => ErrorKind::Import(Box::new(e)),
|
|
||||||
})
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Clear the pool.
|
impl Deref for TransactionPool {
|
||||||
pub fn clear(&mut self) {
|
type Target = Pool<Verifier, Scoring, Error>;
|
||||||
self.inner.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove from the pool.
|
fn deref(&self) -> &Self::Target {
|
||||||
pub fn remove(&mut self, hash: &Hash, is_valid: bool) -> Option<Arc<VerifiedTransaction>> {
|
&self.inner
|
||||||
self.inner.remove(hash, is_valid)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Cull transactions from the queue.
|
|
||||||
pub fn cull<T: PolkadotApi>(&mut self, senders: Option<&[AccountId]>, ready: Ready<T>) -> usize {
|
|
||||||
self.inner.cull(senders, ready)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get an iterator of pending transactions.
|
|
||||||
pub fn pending<'a, T: 'a + PolkadotApi>(&'a self, ready: Ready<'a, T>) -> PendingIterator<'a, T> {
|
|
||||||
self.inner.pending(ready)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the full status of the queue (including readiness)
|
|
||||||
pub fn status<T: PolkadotApi>(&self, ready: Ready<T>) -> Status {
|
|
||||||
self.inner.status(ready)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns light status of the pool.
|
|
||||||
pub fn light_status(&self) -> LightStatus {
|
|
||||||
self.inner.light_status()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -344,7 +344,8 @@ impl<B, E> Client<B, E> where
|
|||||||
header: header,
|
header: header,
|
||||||
is_new_best: is_new_best,
|
is_new_best: is_new_best,
|
||||||
};
|
};
|
||||||
self.import_notification_sinks.lock().retain(|sink| !sink.unbounded_send(notification.clone()).is_err());
|
self.import_notification_sinks.lock()
|
||||||
|
.retain(|sink| sink.unbounded_send(notification.clone()).is_ok());
|
||||||
}
|
}
|
||||||
Ok(ImportResult::Queued)
|
Ok(ImportResult::Queued)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "substrate-extrinsic-pool"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
error-chain = "0.11"
|
||||||
|
futures = "0.1"
|
||||||
|
log = "0.3"
|
||||||
|
parking_lot = "0.4"
|
||||||
|
substrate-primitives = { path = "../primitives" }
|
||||||
|
transaction-pool = "1.12"
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
// Copyright 2018 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Polkadot.
|
||||||
|
|
||||||
|
// Polkadot 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.
|
||||||
|
|
||||||
|
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! External API for extrinsic pool.
|
||||||
|
|
||||||
|
use std::ops::Deref;
|
||||||
|
use txpool::{self, VerifiedTransaction};
|
||||||
|
use primitives::{
|
||||||
|
Hash,
|
||||||
|
block::{Extrinsic, ExtrinsicHash},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Extrinsic pool error.
|
||||||
|
pub trait Error: ::std::error::Error + Send + Sized {
|
||||||
|
/// Try to extract original `txpool::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) -> Result<txpool::Error, Self> { Err(self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for txpool::Error {
|
||||||
|
fn into_pool_error(self) -> Result<txpool::Error, Self> { Ok(self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extrinsic pool.
|
||||||
|
pub trait ExtrinsicPool: Send + Sync + 'static {
|
||||||
|
/// Error type
|
||||||
|
type Error: Error;
|
||||||
|
|
||||||
|
/// Submit a collection of extrinsics to the pool.
|
||||||
|
fn submit(&self, xt: Vec<Extrinsic>) -> Result<Vec<ExtrinsicHash>, Self::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blanket implementation for anything that `Derefs` to the pool.
|
||||||
|
impl<V, S, E, T> ExtrinsicPool for T where
|
||||||
|
T: Deref<Target=super::Pool<V, S, E>> + Send + Sync + 'static,
|
||||||
|
V: txpool::Verifier<Extrinsic>,
|
||||||
|
S: txpool::Scoring<V::VerifiedTransaction>,
|
||||||
|
V::VerifiedTransaction: txpool::VerifiedTransaction<Hash=Hash>,
|
||||||
|
E: From<V::Error>,
|
||||||
|
E: From<txpool::Error>,
|
||||||
|
E: Error,
|
||||||
|
{
|
||||||
|
type Error = E;
|
||||||
|
|
||||||
|
fn submit(&self, xt: Vec<Extrinsic>) -> Result<Vec<ExtrinsicHash>, Self::Error> {
|
||||||
|
self.deref().submit(xt).map(|result| result.into_iter().map(|xt| *xt.hash()).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
// Copyright 2018 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Polkadot.
|
||||||
|
|
||||||
|
// Polkadot 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.
|
||||||
|
|
||||||
|
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
|
//! Generic extrinsic pool.
|
||||||
|
|
||||||
|
extern crate futures;
|
||||||
|
extern crate parking_lot;
|
||||||
|
extern crate substrate_primitives as primitives;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
|
||||||
|
pub extern crate transaction_pool as txpool;
|
||||||
|
|
||||||
|
pub mod api;
|
||||||
|
|
||||||
|
mod listener;
|
||||||
|
mod pool;
|
||||||
|
mod watcher;
|
||||||
|
|
||||||
|
pub use self::pool::Pool;
|
||||||
|
pub use self::watcher::Watcher;
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
// Copyright 2018 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Polkadot.
|
||||||
|
|
||||||
|
// Polkadot 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.
|
||||||
|
|
||||||
|
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
sync::Arc,
|
||||||
|
collections::HashMap,
|
||||||
|
};
|
||||||
|
use primitives::Hash;
|
||||||
|
use txpool;
|
||||||
|
|
||||||
|
use watcher;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Listener {
|
||||||
|
watchers: HashMap<Hash, watcher::Sender>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Listener {
|
||||||
|
pub fn create_watcher<T: txpool::VerifiedTransaction<Hash=Hash>>(&mut self, xt: Arc<T>) -> watcher::Watcher {
|
||||||
|
let sender = self.watchers.entry(*xt.hash()).or_insert_with(watcher::Sender::default);
|
||||||
|
sender.new_watcher()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn broadcasted(&mut self, hash: &Hash, peers: Vec<String>) {
|
||||||
|
self.fire(hash, |watcher| watcher.broadcast(peers));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fire<F: FnOnce(&mut watcher::Sender)>(&mut self, hash: &Hash, fun: F) {
|
||||||
|
let clean = if let Some(h) = self.watchers.get_mut(hash) {
|
||||||
|
fun(h);
|
||||||
|
h.is_done()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
if clean {
|
||||||
|
self.watchers.remove(hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: txpool::VerifiedTransaction<Hash=Hash>> txpool::Listener<T> for Listener {
|
||||||
|
fn added(&mut self, tx: &Arc<T>, old: Option<&Arc<T>>) {
|
||||||
|
if let Some(old) = old {
|
||||||
|
let hash = tx.hash();
|
||||||
|
self.fire(old.hash(), |watcher| watcher.usurped(*hash));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dropped(&mut self, tx: &Arc<T>, by: Option<&T>) {
|
||||||
|
self.fire(tx.hash(), |watcher| match by {
|
||||||
|
Some(t) => watcher.usurped(*t.hash()),
|
||||||
|
None => watcher.dropped(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rejected(&mut self, tx: &Arc<T>, reason: &txpool::ErrorKind) {
|
||||||
|
warn!("Extrinsic rejected ({}): {:?}", reason, tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn invalid(&mut self, tx: &Arc<T>) {
|
||||||
|
warn!("Extrinsic invalid: {:?}", tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn canceled(&mut self, tx: &Arc<T>) {
|
||||||
|
warn!("Extrinsic canceled: {:?}", tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mined(&mut self, tx: &Arc<T>) {
|
||||||
|
// TODO [ToDr] latest block number?
|
||||||
|
let header_hash = 1.into();
|
||||||
|
self.fire(tx.hash(), |watcher| watcher.finalised(header_hash))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
// Copyright 2018 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Polkadot.
|
||||||
|
|
||||||
|
// Polkadot 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.
|
||||||
|
|
||||||
|
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
marker::PhantomData,
|
||||||
|
sync::{Arc, Weak},
|
||||||
|
};
|
||||||
|
|
||||||
|
use futures::sync::mpsc;
|
||||||
|
use parking_lot::{RwLock, Mutex};
|
||||||
|
use txpool;
|
||||||
|
use primitives::{Hash, block::Extrinsic};
|
||||||
|
|
||||||
|
use listener::Listener;
|
||||||
|
use watcher::Watcher;
|
||||||
|
|
||||||
|
/// Extrinsics pool.
|
||||||
|
pub struct Pool<V, S, E> where
|
||||||
|
V: txpool::Verifier<Extrinsic>,
|
||||||
|
S: txpool::Scoring<V::VerifiedTransaction>,
|
||||||
|
{
|
||||||
|
_error: Mutex<PhantomData<E>>,
|
||||||
|
pool: RwLock<txpool::Pool<
|
||||||
|
V::VerifiedTransaction,
|
||||||
|
S,
|
||||||
|
Listener,
|
||||||
|
>>,
|
||||||
|
verifier: V,
|
||||||
|
import_notification_sinks: Mutex<Vec<mpsc::UnboundedSender<Weak<V::VerifiedTransaction>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V, S, E> Pool<V, S, E> where
|
||||||
|
V: txpool::Verifier<Extrinsic>,
|
||||||
|
S: txpool::Scoring<V::VerifiedTransaction>,
|
||||||
|
V::VerifiedTransaction: txpool::VerifiedTransaction<Hash=Hash>,
|
||||||
|
E: From<V::Error>,
|
||||||
|
E: From<txpool::Error>,
|
||||||
|
{
|
||||||
|
/// Create a new transaction pool.
|
||||||
|
pub fn new(options: txpool::Options, verifier: V, scoring: S) -> Self {
|
||||||
|
Pool {
|
||||||
|
_error: Default::default(),
|
||||||
|
pool: RwLock::new(txpool::Pool::new(Listener::default(), scoring, options)),
|
||||||
|
verifier,
|
||||||
|
import_notification_sinks: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Imports a pre-verified extrinsic to the pool.
|
||||||
|
pub fn import(&self, xt: V::VerifiedTransaction) -> Result<Arc<V::VerifiedTransaction>, E> {
|
||||||
|
let result = self.pool.write().import(xt)?;
|
||||||
|
|
||||||
|
let weak = Arc::downgrade(&result);
|
||||||
|
self.import_notification_sinks.lock()
|
||||||
|
.retain(|sink| sink.unbounded_send(weak.clone()).is_ok());
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return an event stream of transactions imported to the pool.
|
||||||
|
pub fn import_notification_stream(&self) -> mpsc::UnboundedReceiver<Weak<V::VerifiedTransaction>> {
|
||||||
|
let (sink, stream) = mpsc::unbounded();
|
||||||
|
self.import_notification_sinks.lock().push(sink);
|
||||||
|
stream
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invoked when extrinsics are broadcasted.
|
||||||
|
pub fn on_broadcasted(&self, propagated: HashMap<Hash, Vec<String>>) {
|
||||||
|
for (hash, peers) in propagated.into_iter() {
|
||||||
|
self.pool.write().listener_mut().broadcasted(&hash, peers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Imports a bunch of extrinsics to the pool
|
||||||
|
pub fn submit(&self, xts: Vec<Extrinsic>) -> Result<Vec<Arc<V::VerifiedTransaction>>, E> {
|
||||||
|
xts
|
||||||
|
.into_iter()
|
||||||
|
.map(|xt| self.verifier.verify_transaction(xt))
|
||||||
|
.map(|xt| {
|
||||||
|
Ok(self.pool.write().import(xt?)?)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Import a single extrinsic and starts to watch their progress in the pool.
|
||||||
|
pub fn submit_and_watch(&self, xt: Extrinsic) -> Result<Watcher, E> {
|
||||||
|
let xt = self.submit(vec![xt])?.pop().expect("One extrinsic passed; one result returned; qed");
|
||||||
|
Ok(self.pool.write().listener_mut().create_watcher(xt))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove from the pool.
|
||||||
|
pub fn remove(&self, hashes: &[Hash], is_valid: bool) -> Vec<Option<Arc<V::VerifiedTransaction>>> {
|
||||||
|
let mut pool = self.pool.write();
|
||||||
|
let mut results = Vec::with_capacity(hashes.len());
|
||||||
|
for hash in hashes {
|
||||||
|
results.push(pool.remove(hash, is_valid));
|
||||||
|
}
|
||||||
|
results
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cull transactions from the queue.
|
||||||
|
pub fn cull<R>(&self, senders: Option<&[<V::VerifiedTransaction as txpool::VerifiedTransaction>::Sender]>, ready: R) -> usize where
|
||||||
|
R: txpool::Ready<V::VerifiedTransaction>,
|
||||||
|
{
|
||||||
|
self.pool.write().cull(senders, ready)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cull transactions from the queue and then compute the pending set.
|
||||||
|
pub fn cull_and_get_pending<R, F, T>(&self, ready: R, f: F) -> T where
|
||||||
|
R: txpool::Ready<V::VerifiedTransaction> + Clone,
|
||||||
|
F: FnOnce(txpool::PendingIterator<V::VerifiedTransaction, R, S, Listener>) -> T,
|
||||||
|
{
|
||||||
|
let mut pool = self.pool.write();
|
||||||
|
pool.cull(None, ready.clone());
|
||||||
|
f(pool.pending(ready))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the full status of the queue (including readiness)
|
||||||
|
pub fn status<R: txpool::Ready<V::VerifiedTransaction>>(&self, ready: R) -> txpool::Status {
|
||||||
|
self.pool.read().status(ready)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns light status of the pool.
|
||||||
|
pub fn light_status(&self) -> txpool::LightStatus {
|
||||||
|
self.pool.read().light_status()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
// Copyright 2018 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Polkadot.
|
||||||
|
|
||||||
|
// Polkadot 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.
|
||||||
|
|
||||||
|
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use futures::sync::mpsc;
|
||||||
|
use primitives::{
|
||||||
|
block::{HeaderHash, ExtrinsicHash}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Possible extrinsic status events
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Status {
|
||||||
|
/// Extrinsic has been finalised in block with given hash.
|
||||||
|
Finalised(HeaderHash),
|
||||||
|
/// Some state change (perhaps another extrinsic was included) rendered this extrinsic invalid.
|
||||||
|
Usurped(ExtrinsicHash),
|
||||||
|
/// 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 watcher.
|
||||||
|
///
|
||||||
|
/// Represents a stream of status updates for particular extrinsic.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Watcher {
|
||||||
|
receiver: mpsc::UnboundedReceiver<Status>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub(crate) struct Sender {
|
||||||
|
receivers: Vec<mpsc::UnboundedSender<Status>>,
|
||||||
|
finalised: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sender {
|
||||||
|
/// Add a new watcher to this sender object.
|
||||||
|
pub fn new_watcher(&mut self) -> Watcher {
|
||||||
|
let (tx, receiver) = mpsc::unbounded();
|
||||||
|
self.receivers.push(tx);
|
||||||
|
Watcher {
|
||||||
|
receiver,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Some state change (perhaps another extrinsic was included) rendered this extrinsic invalid.
|
||||||
|
pub fn usurped(&mut self, hash: ExtrinsicHash) {
|
||||||
|
self.send(Status::Usurped(hash))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extrinsic has been finalised in block with given hash.
|
||||||
|
pub fn finalised(&mut self, hash: HeaderHash) {
|
||||||
|
self.send(Status::Finalised(hash));
|
||||||
|
self.finalised = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transaction has been dropped from the pool because of the limit.
|
||||||
|
pub fn dropped(&mut self) {
|
||||||
|
self.send(Status::Dropped);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The extrinsic has been broadcast to the given peers.
|
||||||
|
pub fn broadcast(&mut self, peers: Vec<String>) {
|
||||||
|
self.send(Status::Broadcast(peers))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the are no more listeners for this extrinsic or it was finalised.
|
||||||
|
pub fn is_done(&self) -> bool {
|
||||||
|
self.finalised || self.receivers.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send(&mut self, status: Status) {
|
||||||
|
self.receivers.retain(|sender| sender.unbounded_send(status.clone()).is_ok())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.?
|
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.?
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet, BTreeMap};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::{mem, cmp};
|
use std::{mem, cmp};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time;
|
use std::time;
|
||||||
@@ -24,7 +24,7 @@ use serde_json;
|
|||||||
use primitives::block::{HeaderHash, ExtrinsicHash, Number as BlockNumber, Header, Id as BlockId};
|
use primitives::block::{HeaderHash, ExtrinsicHash, Number as BlockNumber, Header, Id as BlockId};
|
||||||
use primitives::{Hash, blake2_256};
|
use primitives::{Hash, blake2_256};
|
||||||
use runtime_support::Hashable;
|
use runtime_support::Hashable;
|
||||||
use network::{PeerId, NodeId};
|
use network::PeerId;
|
||||||
|
|
||||||
use message::{self, Message};
|
use message::{self, Message};
|
||||||
use sync::{ChainSync, Status as SyncStatus, SyncState};
|
use sync::{ChainSync, Status as SyncStatus, SyncState};
|
||||||
@@ -103,15 +103,6 @@ pub struct PeerInfo {
|
|||||||
pub best_number: BlockNumber,
|
pub best_number: BlockNumber,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transaction stats
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct TransactionStats {
|
|
||||||
/// Block number where this TX was first seen.
|
|
||||||
pub first_seen: u64,
|
|
||||||
/// Peers it was propagated to.
|
|
||||||
pub propagated_to: BTreeMap<NodeId, usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Protocol {
|
impl Protocol {
|
||||||
/// Create a new instance.
|
/// Create a new instance.
|
||||||
pub fn new(config: ProtocolConfig, chain: Arc<Client>, on_demand: Option<Arc<OnDemandService>>, transaction_pool: Arc<TransactionPool>) -> error::Result<Protocol> {
|
pub fn new(config: ProtocolConfig, chain: Arc<Client>, on_demand: Option<Arc<OnDemandService>>, transaction_pool: Arc<TransactionPool>) -> error::Result<Protocol> {
|
||||||
@@ -463,15 +454,31 @@ impl Protocol {
|
|||||||
|
|
||||||
let transactions = self.transaction_pool.transactions();
|
let transactions = self.transaction_pool.transactions();
|
||||||
|
|
||||||
|
let mut propagated_to = HashMap::new();
|
||||||
let mut peers = self.peers.write();
|
let mut peers = self.peers.write();
|
||||||
for (peer_id, ref mut peer) in peers.iter_mut() {
|
for (peer_id, ref mut peer) in peers.iter_mut() {
|
||||||
let to_send: Vec<_> = transactions.iter().filter_map(|&(hash, ref t)|
|
let (hashes, to_send): (Vec<_>, Vec<_>) = transactions
|
||||||
if peer.known_transactions.insert(hash.clone()) { Some(t.clone()) } else { None }).collect();
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.filter(|&(hash, _)| peer.known_transactions.insert(hash))
|
||||||
|
.unzip();
|
||||||
|
|
||||||
if !to_send.is_empty() {
|
if !to_send.is_empty() {
|
||||||
|
let node_id = io.peer_session_info(*peer_id).map(|info| match info.id {
|
||||||
|
Some(id) => format!("{}@{:x}", info.remote_address, id),
|
||||||
|
None => info.remote_address.clone(),
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(id) = node_id {
|
||||||
|
for hash in hashes {
|
||||||
|
propagated_to.entry(hash).or_insert_with(Vec::new).push(id.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
trace!(target: "sync", "Sending {} transactions to {}", to_send.len(), peer_id);
|
trace!(target: "sync", "Sending {} transactions to {}", to_send.len(), peer_id);
|
||||||
self.send_message(io, *peer_id, Message::Transactions(to_send));
|
self.send_message(io, *peer_id, Message::Transactions(to_send));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.transaction_pool.on_broadcasted(propagated_to);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send Status message
|
/// Send Status message
|
||||||
@@ -551,10 +558,6 @@ impl Protocol {
|
|||||||
self.on_demand.as_ref().map(|s| s.on_remote_response(io, peer_id, response));
|
self.on_demand.as_ref().map(|s| s.on_remote_response(io, peer_id, response));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transactions_stats(&self) -> BTreeMap<ExtrinsicHash, TransactionStats> {
|
|
||||||
BTreeMap::new()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn chain(&self) -> &Client {
|
pub fn chain(&self) -> &Client {
|
||||||
&*self.chain
|
&*self.chain
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,8 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.?
|
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.?
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::collections::{BTreeMap};
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use futures::sync::{oneshot, mpsc};
|
use futures::sync::{oneshot, mpsc};
|
||||||
@@ -26,7 +26,7 @@ use primitives::block::{ExtrinsicHash, Header, HeaderHash};
|
|||||||
use primitives::Hash;
|
use primitives::Hash;
|
||||||
use core_io::{TimerToken};
|
use core_io::{TimerToken};
|
||||||
use io::NetSyncIo;
|
use io::NetSyncIo;
|
||||||
use protocol::{Protocol, ProtocolStatus, PeerInfo as ProtocolPeerInfo, TransactionStats};
|
use protocol::{Protocol, ProtocolStatus, PeerInfo as ProtocolPeerInfo};
|
||||||
use config::{ProtocolConfig};
|
use config::{ProtocolConfig};
|
||||||
use error::Error;
|
use error::Error;
|
||||||
use chain::Client;
|
use chain::Client;
|
||||||
@@ -75,8 +75,6 @@ pub trait SyncProvider: Send + Sync {
|
|||||||
fn peers(&self) -> Vec<PeerInfo>;
|
fn peers(&self) -> Vec<PeerInfo>;
|
||||||
/// Get this node id if available.
|
/// Get this node id if available.
|
||||||
fn node_id(&self) -> Option<String>;
|
fn node_id(&self) -> Option<String>;
|
||||||
/// Returns propagation count for pending transactions.
|
|
||||||
fn transactions_stats(&self) -> BTreeMap<ExtrinsicHash, TransactionStats>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transaction pool interface
|
/// Transaction pool interface
|
||||||
@@ -85,6 +83,8 @@ pub trait TransactionPool: Send + Sync {
|
|||||||
fn transactions(&self) -> Vec<(ExtrinsicHash, Vec<u8>)>;
|
fn transactions(&self) -> Vec<(ExtrinsicHash, Vec<u8>)>;
|
||||||
/// Import a transction into the pool.
|
/// Import a transction into the pool.
|
||||||
fn import(&self, transaction: &[u8]) -> Option<ExtrinsicHash>;
|
fn import(&self, transaction: &[u8]) -> Option<ExtrinsicHash>;
|
||||||
|
/// Notify the pool about transactions broadcast.
|
||||||
|
fn on_broadcasted(&self, propagations: HashMap<ExtrinsicHash, Vec<String>>);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ConsensusService
|
/// ConsensusService
|
||||||
@@ -249,10 +249,6 @@ impl SyncProvider for Service {
|
|||||||
fn node_id(&self) -> Option<String> {
|
fn node_id(&self) -> Option<String> {
|
||||||
self.network.external_url()
|
self.network.external_url()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transactions_stats(&self) -> BTreeMap<ExtrinsicHash, TransactionStats> {
|
|
||||||
self.handler.protocol.transactions_stats()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ConsensusService
|
/// ConsensusService
|
||||||
|
|||||||
@@ -204,6 +204,8 @@ impl TransactionPool for EmptyTransactionPool {
|
|||||||
fn import(&self, _transaction: &[u8]) -> Option<ExtrinsicHash> {
|
fn import(&self, _transaction: &[u8]) -> Option<ExtrinsicHash> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_broadcasted(&self, _: HashMap<ExtrinsicHash, Vec<String>>) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TestNet {
|
pub struct TestNet {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ log = "0.3"
|
|||||||
parking_lot = "0.4"
|
parking_lot = "0.4"
|
||||||
substrate-client = { path = "../client" }
|
substrate-client = { path = "../client" }
|
||||||
substrate-executor = { path = "../executor" }
|
substrate-executor = { path = "../executor" }
|
||||||
|
substrate-extrinsic-pool = { path = "../extrinsic-pool" }
|
||||||
substrate-primitives = { path = "../primitives" }
|
substrate-primitives = { path = "../primitives" }
|
||||||
substrate-state-machine = { path = "../state-machine" }
|
substrate-state-machine = { path = "../state-machine" }
|
||||||
tokio-core = "0.1.12"
|
tokio-core = "0.1.12"
|
||||||
|
|||||||
@@ -16,12 +16,12 @@
|
|||||||
|
|
||||||
//! Authoring RPC module errors.
|
//! Authoring RPC module errors.
|
||||||
|
|
||||||
use client;
|
use extrinsic_pool::txpool;
|
||||||
use rpc;
|
use rpc;
|
||||||
|
|
||||||
error_chain! {
|
error_chain! {
|
||||||
links {
|
links {
|
||||||
Client(client::error::Error, client::error::ErrorKind) #[doc = "Client error"];
|
Pool(txpool::Error, txpool::ErrorKind) #[doc = "Pool error"];
|
||||||
}
|
}
|
||||||
errors {
|
errors {
|
||||||
/// Not implemented yet
|
/// Not implemented yet
|
||||||
@@ -29,15 +29,10 @@ error_chain! {
|
|||||||
description("not yet implemented"),
|
description("not yet implemented"),
|
||||||
display("Method Not Implemented"),
|
display("Method Not Implemented"),
|
||||||
}
|
}
|
||||||
/// Invalid format
|
/// Verification error
|
||||||
InvalidFormat {
|
Verification(e: Box<::std::error::Error + Send>) {
|
||||||
description("invalid format"),
|
description("extrinsic verification error"),
|
||||||
display("Invalid format for the extrinsic data"),
|
display("Extrinsic verification error: {}", e.description()),
|
||||||
}
|
|
||||||
/// Some error with the pool since the import failed.
|
|
||||||
PoolError {
|
|
||||||
description("pool import failed"),
|
|
||||||
display("Pool import failed"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,6 +45,7 @@ impl From<Error> for rpc::Error {
|
|||||||
message: "Not implemented yet".into(),
|
message: "Not implemented yet".into(),
|
||||||
data: None,
|
data: None,
|
||||||
},
|
},
|
||||||
|
// TODO [ToDr] Unwrap Pool errors.
|
||||||
_ => rpc::Error::internal_error(),
|
_ => rpc::Error::internal_error(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,9 @@
|
|||||||
|
|
||||||
//! Substrate block-author/full-node API.
|
//! Substrate block-author/full-node API.
|
||||||
|
|
||||||
use primitives::block::Extrinsic;
|
use std::sync::Arc;
|
||||||
|
use primitives::block::{Extrinsic, ExtrinsicHash};
|
||||||
|
use extrinsic_pool::api::{Error, ExtrinsicPool};
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|
||||||
@@ -30,6 +32,20 @@ build_rpc_trait! {
|
|||||||
pub trait AuthorApi {
|
pub trait AuthorApi {
|
||||||
/// Submit extrinsic for inclusion in block.
|
/// Submit extrinsic for inclusion in block.
|
||||||
#[rpc(name = "author_submitExtrinsic")]
|
#[rpc(name = "author_submitExtrinsic")]
|
||||||
fn submit_extrinsic(&self, Extrinsic) -> Result<()>;
|
fn submit_extrinsic(&self, Extrinsic) -> Result<ExtrinsicHash>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> AuthorApi for Arc<T> where
|
||||||
|
T: ExtrinsicPool,
|
||||||
|
{
|
||||||
|
fn submit_extrinsic(&self, xt: Extrinsic) -> Result<ExtrinsicHash> {
|
||||||
|
self
|
||||||
|
.submit(vec![xt])
|
||||||
|
.map(|mut res| res.pop().expect("One extrinsic passed; one result back; qed"))
|
||||||
|
.map_err(|e| e.into_pool_error()
|
||||||
|
.map(Into::into)
|
||||||
|
.unwrap_or_else(|e| error::ErrorKind::Verification(Box::new(e)).into())
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,37 +15,53 @@
|
|||||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use super::error::*;
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::{fmt, sync::Arc};
|
||||||
|
use extrinsic_pool::api;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use primitives::block;
|
use primitives::block;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct DummyTxPool {
|
struct DummyTxPool {
|
||||||
submitted: Vec<block::Extrinsic>,
|
submitted: Mutex<Vec<block::Extrinsic>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AuthorApi for Arc<Mutex<DummyTxPool>> {
|
#[derive(Debug)]
|
||||||
|
struct Error;
|
||||||
|
impl api::Error for Error {}
|
||||||
|
impl ::std::error::Error for Error {
|
||||||
|
fn description(&self) -> &str { "Error" }
|
||||||
|
}
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt::Debug::fmt(self, fmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl api::ExtrinsicPool for DummyTxPool {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
/// Submit extrinsic for inclusion in block.
|
/// Submit extrinsic for inclusion in block.
|
||||||
fn submit_extrinsic(&self, xt: Extrinsic) -> Result<()> {
|
fn submit(&self, xt: Vec<Extrinsic>) -> ::std::result::Result<Vec<ExtrinsicHash>, Self::Error> {
|
||||||
let mut s = self.lock();
|
let mut submitted = self.submitted.lock();
|
||||||
if s.submitted.len() < 1 {
|
if submitted.len() < 1 {
|
||||||
s.submitted.push(xt);
|
let hashes = xt.iter().map(|_xt| 1.into()).collect();
|
||||||
Ok(())
|
submitted.extend(xt);
|
||||||
|
Ok(hashes)
|
||||||
} else {
|
} else {
|
||||||
Err(ErrorKind::PoolError.into())
|
Err(Error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn submit_transaction_should_not_cause_error() {
|
fn submit_transaction_should_not_cause_error() {
|
||||||
let p = Arc::new(Mutex::new(DummyTxPool::default()));
|
let p = Arc::new(DummyTxPool::default());
|
||||||
|
let hash: ExtrinsicHash = 1.into();
|
||||||
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
AuthorApi::submit_extrinsic(&p, block::Extrinsic(vec![])),
|
AuthorApi::submit_extrinsic(&p, block::Extrinsic(vec![])),
|
||||||
Ok(())
|
Ok(hash)
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
AuthorApi::submit_extrinsic(&p, block::Extrinsic(vec![])).is_err()
|
AuthorApi::submit_extrinsic(&p, block::Extrinsic(vec![])).is_err()
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ extern crate jsonrpc_core as rpc;
|
|||||||
extern crate jsonrpc_pubsub;
|
extern crate jsonrpc_pubsub;
|
||||||
extern crate parking_lot;
|
extern crate parking_lot;
|
||||||
extern crate substrate_client as client;
|
extern crate substrate_client as client;
|
||||||
|
extern crate substrate_extrinsic_pool as extrinsic_pool;
|
||||||
extern crate substrate_primitives as primitives;
|
extern crate substrate_primitives as primitives;
|
||||||
extern crate substrate_state_machine as state_machine;
|
extern crate substrate_state_machine as state_machine;
|
||||||
extern crate tokio_core;
|
extern crate tokio_core;
|
||||||
|
|||||||
Reference in New Issue
Block a user