mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 20:31:13 +00:00
Tagged transaction queue integration (#893)
* Make the graph generic. * Adapting pool API for the graph. * Merge pool & graph. * Restructure. * Fix test of transaction pool. * Get rid of node/transaction-pool. * Compilation fixes. * Test7 * Fix compilation of tests. * Revert runtime changes. * Add validate_transaction to test-runtime. * Fix RPC tests. * Add clearing of the old transactions. * Trigger pool events. * Use new queue API. * Fix wasm build, re-export Hasher. * No warning if validate transaction fails. * Get rid of Into<u64> and use As
This commit is contained in:
@@ -4,22 +4,22 @@ version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
futures = "0.1.17"
|
||||
parking_lot = "0.4"
|
||||
tokio = "0.1.7"
|
||||
error-chain = "0.12"
|
||||
log = "0.4"
|
||||
exit-future = "0.1"
|
||||
rhododendron = "0.3"
|
||||
futures = "0.1.17"
|
||||
log = "0.4"
|
||||
node-primitives = { path = "../primitives" }
|
||||
node-runtime = { path = "../runtime" }
|
||||
node-transaction-pool = { path = "../transaction-pool" }
|
||||
substrate-bft = { path = "../../core/bft" }
|
||||
parity-codec = "2.0"
|
||||
substrate-primitives = { path = "../../core/primitives" }
|
||||
substrate-client = { path = "../../core/client" }
|
||||
parking_lot = "0.4"
|
||||
rhododendron = "0.3"
|
||||
sr-primitives = { path = "../../core/sr-primitives" }
|
||||
srml-system = { path = "../../srml/system" }
|
||||
substrate-bft = { path = "../../core/bft" }
|
||||
substrate-client = { path = "../../core/client" }
|
||||
substrate-primitives = { path = "../../core/primitives" }
|
||||
substrate-transaction-pool = { path = "../../core/transaction-pool" }
|
||||
tokio = "0.1.7"
|
||||
|
||||
[dev-dependencies]
|
||||
substrate-keyring = { path = "../../core/keyring" }
|
||||
|
||||
@@ -16,25 +16,25 @@
|
||||
|
||||
//! This service uses BFT consensus provided by the substrate.
|
||||
|
||||
extern crate parking_lot;
|
||||
extern crate node_transaction_pool as transaction_pool;
|
||||
extern crate node_runtime;
|
||||
extern crate node_primitives;
|
||||
|
||||
extern crate substrate_bft as bft;
|
||||
extern crate parity_codec as codec;
|
||||
extern crate substrate_primitives as primitives;
|
||||
extern crate sr_primitives as runtime_primitives;
|
||||
extern crate srml_system;
|
||||
extern crate substrate_bft as bft;
|
||||
extern crate substrate_client as client;
|
||||
extern crate substrate_primitives as primitives;
|
||||
extern crate substrate_transaction_pool as transaction_pool;
|
||||
|
||||
extern crate exit_future;
|
||||
extern crate tokio;
|
||||
extern crate futures;
|
||||
extern crate parking_lot;
|
||||
extern crate rhododendron;
|
||||
extern crate tokio;
|
||||
|
||||
#[macro_use]
|
||||
extern crate error_chain;
|
||||
extern crate futures;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
@@ -51,10 +51,10 @@ use codec::{Decode, Encode};
|
||||
use node_primitives::{AccountId, Timestamp, SessionKey, InherentData};
|
||||
use node_runtime::Runtime;
|
||||
use primitives::{AuthorityId, ed25519, Blake2Hasher};
|
||||
use runtime_primitives::traits::{Block as BlockT, Hash as HashT, Header as HeaderT, As};
|
||||
use runtime_primitives::traits::{Block as BlockT, Hash as HashT, Header as HeaderT, As, BlockNumberToHash};
|
||||
use runtime_primitives::generic::{BlockId, Era};
|
||||
use srml_system::Trait as SystemT;
|
||||
use transaction_pool::{TransactionPool, Client as TPClient};
|
||||
use transaction_pool::txpool::{self, Pool as TransactionPool};
|
||||
use tokio::runtime::TaskExecutor;
|
||||
use tokio::timer::Delay;
|
||||
|
||||
@@ -95,7 +95,7 @@ pub trait AuthoringApi:
|
||||
/// The block used for this API type.
|
||||
type Block: BlockT;
|
||||
/// The error used by this API type.
|
||||
type Error;
|
||||
type Error: std::error::Error;
|
||||
|
||||
/// Build a block on top of the given, with inherent extrinsics pre-pushed.
|
||||
fn build_block<F: FnMut(&mut BlockBuilder<Self::Block>) -> ()>(
|
||||
@@ -166,13 +166,14 @@ pub trait Network {
|
||||
}
|
||||
|
||||
/// Proposer factory.
|
||||
pub struct ProposerFactory<N, C> where
|
||||
C: AuthoringApi + TPClient,
|
||||
pub struct ProposerFactory<N, C, A> where
|
||||
C: AuthoringApi,
|
||||
A: txpool::ChainApi,
|
||||
{
|
||||
/// The client instance.
|
||||
pub client: Arc<C>,
|
||||
/// The transaction pool.
|
||||
pub transaction_pool: Arc<TransactionPool<C>>,
|
||||
pub transaction_pool: Arc<TransactionPool<A>>,
|
||||
/// The backing network handle.
|
||||
pub network: N,
|
||||
/// handle to remote task executor
|
||||
@@ -183,14 +184,15 @@ pub struct ProposerFactory<N, C> where
|
||||
pub force_delay: Timestamp,
|
||||
}
|
||||
|
||||
impl<N, C> bft::Environment<<C as AuthoringApi>::Block> for ProposerFactory<N, C> where
|
||||
impl<N, C, A> bft::Environment<<C as AuthoringApi>::Block> for ProposerFactory<N, C, A> where
|
||||
N: Network<Block=<C as AuthoringApi>::Block>,
|
||||
C: AuthoringApi + TPClient<Block=<C as AuthoringApi>::Block>,
|
||||
C: AuthoringApi + BlockNumberToHash,
|
||||
A: txpool::ChainApi<Block=<C as AuthoringApi>::Block>,
|
||||
<<C as AuthoringApi>::Block as BlockT>::Hash:
|
||||
Into<<Runtime as SystemT>::Hash> + PartialEq<primitives::H256> + Into<primitives::H256>,
|
||||
Error: From<<C as AuthoringApi>::Error>
|
||||
{
|
||||
type Proposer = Proposer<C>;
|
||||
type Proposer = Proposer<C, A>;
|
||||
type Input = N::Input;
|
||||
type Output = N::Output;
|
||||
type Error = Error;
|
||||
@@ -240,7 +242,7 @@ impl<N, C> bft::Environment<<C as AuthoringApi>::Block> for ProposerFactory<N, C
|
||||
}
|
||||
|
||||
/// The proposer logic.
|
||||
pub struct Proposer<C: AuthoringApi + TPClient> {
|
||||
pub struct Proposer<C: AuthoringApi, A: txpool::ChainApi> {
|
||||
client: Arc<C>,
|
||||
start: Instant,
|
||||
local_key: Arc<ed25519::Pair>,
|
||||
@@ -248,13 +250,13 @@ pub struct Proposer<C: AuthoringApi + TPClient> {
|
||||
parent_id: BlockId<<C as AuthoringApi>::Block>,
|
||||
parent_number: <<<C as AuthoringApi>::Block as BlockT>::Header as HeaderT>::Number,
|
||||
random_seed: <<C as AuthoringApi>::Block as BlockT>::Hash,
|
||||
transaction_pool: Arc<TransactionPool<C>>,
|
||||
transaction_pool: Arc<TransactionPool<A>>,
|
||||
offline: SharedOfflineTracker,
|
||||
validators: Vec<AccountId>,
|
||||
minimum_timestamp: u64,
|
||||
}
|
||||
|
||||
impl<C: AuthoringApi + TPClient> Proposer<C> {
|
||||
impl<C: AuthoringApi, A: txpool::ChainApi> Proposer<C, A> {
|
||||
fn primary_index(&self, round_number: usize, len: usize) -> usize {
|
||||
use primitives::uint::U256;
|
||||
|
||||
@@ -265,8 +267,9 @@ impl<C: AuthoringApi + TPClient> Proposer<C> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> bft::Proposer<<C as AuthoringApi>::Block> for Proposer<C> where
|
||||
C: AuthoringApi + TPClient<Block=<C as AuthoringApi>::Block>,
|
||||
impl<C, A> bft::Proposer<<C as AuthoringApi>::Block> for Proposer<C, A> where
|
||||
C: AuthoringApi + BlockNumberToHash,
|
||||
A: txpool::ChainApi<Block=<C as AuthoringApi>::Block>,
|
||||
<<C as AuthoringApi>::Block as BlockT>::Hash:
|
||||
Into<<Runtime as SystemT>::Hash> + PartialEq<primitives::H256> + Into<primitives::H256>,
|
||||
error::Error: From<<C as AuthoringApi>::Error>
|
||||
@@ -307,27 +310,26 @@ impl<C> bft::Proposer<<C as AuthoringApi>::Block> for Proposer<C> where
|
||||
inherent_data,
|
||||
|block_builder| {
|
||||
let mut unqueue_invalid = Vec::new();
|
||||
let result = self.transaction_pool.cull_and_get_pending(&BlockId::hash(self.parent_hash), |pending_iterator| {
|
||||
self.transaction_pool.ready(|pending_iterator| {
|
||||
let mut pending_size = 0;
|
||||
for pending in pending_iterator {
|
||||
if pending_size + pending.verified.encoded_size() >= MAX_TRANSACTIONS_SIZE { break }
|
||||
// TODO [ToDr] Probably get rid of it, and validate in runtime.
|
||||
let encoded_size = pending.data.raw.encode().len();
|
||||
if pending_size + encoded_size >= MAX_TRANSACTIONS_SIZE { break }
|
||||
|
||||
match block_builder.push_extrinsic(pending.original.clone()) {
|
||||
match block_builder.push_extrinsic(pending.data.raw.clone()) {
|
||||
Ok(()) => {
|
||||
pending_size += pending.verified.encoded_size();
|
||||
pending_size += encoded_size;
|
||||
}
|
||||
Err(e) => {
|
||||
trace!(target: "transaction-pool", "Invalid transaction: {}", e);
|
||||
unqueue_invalid.push(pending.verified.hash().clone());
|
||||
unqueue_invalid.push(pending.hash.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if let Err(e) = result {
|
||||
warn!("Unable to get the pending set: {:?}", e);
|
||||
}
|
||||
|
||||
self.transaction_pool.remove(&unqueue_invalid, false);
|
||||
self.transaction_pool.remove_invalid(&unqueue_invalid);
|
||||
})?;
|
||||
|
||||
info!("Proposing block [number: {}; hash: {}; parent_hash: {}; extrinsics: [{}]]",
|
||||
@@ -440,24 +442,22 @@ impl<C> bft::Proposer<<C as AuthoringApi>::Block> for Proposer<C> where
|
||||
use runtime_primitives::bft::{MisbehaviorKind, MisbehaviorReport};
|
||||
use node_runtime::{Call, UncheckedExtrinsic, ConsensusCall};
|
||||
|
||||
let local_id = self.local_key.public().0.into();
|
||||
let mut next_index = {
|
||||
let cur_index = self.transaction_pool.cull_and_get_pending(&BlockId::hash(self.parent_hash), |pending| pending
|
||||
.filter(|tx| tx.verified.sender == local_id)
|
||||
.last()
|
||||
.map(|tx| Ok(tx.verified.index()))
|
||||
.unwrap_or_else(|| self.client.account_nonce(&self.parent_id, &local_id))
|
||||
.map_err(Error::from)
|
||||
);
|
||||
let local_id = self.local_key.public().0;
|
||||
// let cur_index = self.transaction_pool.cull_and_get_pending(&BlockId::hash(self.parent_hash), |pending| pending
|
||||
// .filter(|tx| tx.verified.sender == local_id)
|
||||
// .last()
|
||||
// .map(|tx| Ok(tx.verified.index()))
|
||||
// .unwrap_or_else(|| self.client.account_nonce(&self.parent_id, local_id))
|
||||
// .map_err(Error::from)
|
||||
// );
|
||||
// TODO [ToDr] Use pool data
|
||||
let cur_index: Result<u64> = self.client.account_nonce(&self.parent_id, &local_id).map_err(Error::from);
|
||||
|
||||
match cur_index {
|
||||
Ok(Ok(cur_index)) => cur_index + 1,
|
||||
Ok(Err(e)) => {
|
||||
warn!(target: "consensus", "Error computing next transaction index: {}", e);
|
||||
return;
|
||||
}
|
||||
Ok(cur_index) => cur_index + 1,
|
||||
Err(e) => {
|
||||
warn!(target: "consensus", "Error computing next transaction index: {}", e);
|
||||
warn!(target: "consensus", "Error computing next transaction index: {:?}", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -488,8 +488,9 @@ impl<C> bft::Proposer<<C as AuthoringApi>::Block> for Proposer<C> where
|
||||
};
|
||||
let uxt: <<C as AuthoringApi>::Block as BlockT>::Extrinsic = Decode::decode(&mut extrinsic.encode().as_slice()).expect("Encoded extrinsic is valid");
|
||||
let hash = BlockId::<<C as AuthoringApi>::Block>::hash(self.parent_hash);
|
||||
self.transaction_pool.submit_one(&hash, uxt)
|
||||
.expect("locally signed extrinsic is valid; qed");
|
||||
if let Err(e) = self.transaction_pool.submit_one(&hash, uxt) {
|
||||
warn!("Error importing misbehavior report: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,9 +26,9 @@ use bft::{self, BftService};
|
||||
use client::{BlockchainEvents, ChainHead, BlockBody};
|
||||
use ed25519;
|
||||
use futures::prelude::*;
|
||||
use transaction_pool::{TransactionPool, Client as TPClient};
|
||||
use transaction_pool::txpool::{Pool as TransactionPool, ChainApi as PoolChainApi};
|
||||
use primitives;
|
||||
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT};
|
||||
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, BlockNumberToHash};
|
||||
|
||||
use tokio::executor::current_thread::TaskExecutor as LocalThreadHandle;
|
||||
use tokio::runtime::TaskExecutor as ThreadPoolHandle;
|
||||
@@ -72,18 +72,19 @@ pub struct Service {
|
||||
|
||||
impl Service {
|
||||
/// Create and start a new instance.
|
||||
pub fn new<A, C, N>(
|
||||
pub fn new<A, P, C, N>(
|
||||
client: Arc<C>,
|
||||
api: Arc<A>,
|
||||
network: N,
|
||||
transaction_pool: Arc<TransactionPool<A>>,
|
||||
transaction_pool: Arc<TransactionPool<P>>,
|
||||
thread_pool: ThreadPoolHandle,
|
||||
key: ed25519::Pair,
|
||||
block_delay: u64,
|
||||
) -> Service
|
||||
where
|
||||
A: AuthoringApi + TPClient<Block = <A as AuthoringApi>::Block> + 'static,
|
||||
error::Error: From<<A as AuthoringApi>::Error>,
|
||||
A: AuthoringApi + BlockNumberToHash + 'static,
|
||||
P: PoolChainApi<Block = <A as AuthoringApi>::Block> + 'static,
|
||||
C: BlockchainEvents<<A as AuthoringApi>::Block>
|
||||
+ ChainHead<<A as AuthoringApi>::Block>
|
||||
+ BlockBody<<A as AuthoringApi>::Block>,
|
||||
|
||||
@@ -4,27 +4,27 @@ version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
parking_lot = "0.4"
|
||||
error-chain = "0.12"
|
||||
hex-literal = "0.1"
|
||||
lazy_static = "1.0"
|
||||
log = "0.4"
|
||||
slog = "^2"
|
||||
tokio = "0.1.7"
|
||||
hex-literal = "0.1"
|
||||
parity-codec = { version = "2.0" }
|
||||
node-consensus = { path = "../consensus" }
|
||||
node-executor = { path = "../executor" }
|
||||
node-network = { path = "../network" }
|
||||
node-primitives = { path = "../primitives" }
|
||||
node-runtime = { path = "../runtime" }
|
||||
node-executor = { path = "../executor" }
|
||||
node-consensus = { path = "../consensus" }
|
||||
node-network = { path = "../network" }
|
||||
node-transaction-pool = { path = "../transaction-pool" }
|
||||
parity-codec = { version = "2.0" }
|
||||
parking_lot = "0.4"
|
||||
slog = "^2"
|
||||
sr-io = { path = "../../core/sr-io" }
|
||||
sr-primitives = { path = "../../core/sr-primitives" }
|
||||
substrate-primitives = { path = "../../core/primitives" }
|
||||
substrate-network = { path = "../../core/network" }
|
||||
substrate-client = { path = "../../core/client" }
|
||||
substrate-network = { path = "../../core/network" }
|
||||
substrate-primitives = { path = "../../core/primitives" }
|
||||
substrate-service = { path = "../../core/service" }
|
||||
substrate-telemetry = { path = "../../core/telemetry" }
|
||||
substrate-transaction-pool = { path = "../../core/transaction-pool" }
|
||||
tokio = "0.1.7"
|
||||
|
||||
[dev-dependencies]
|
||||
substrate-service-test = { path = "../../core/service/test" }
|
||||
|
||||
@@ -22,12 +22,12 @@ extern crate node_primitives;
|
||||
extern crate node_runtime;
|
||||
extern crate node_executor;
|
||||
extern crate node_network;
|
||||
extern crate node_transaction_pool as transaction_pool;
|
||||
extern crate node_consensus as consensus;
|
||||
extern crate substrate_primitives as primitives;
|
||||
extern crate substrate_network as network;
|
||||
extern crate substrate_client as client;
|
||||
extern crate substrate_network as network;
|
||||
extern crate substrate_primitives as primitives;
|
||||
extern crate substrate_service as service;
|
||||
extern crate substrate_transaction_pool as transaction_pool;
|
||||
extern crate parity_codec as codec;
|
||||
extern crate tokio;
|
||||
#[cfg(test)]
|
||||
@@ -51,13 +51,12 @@ pub mod chain_spec;
|
||||
|
||||
use std::sync::Arc;
|
||||
use codec::Decode;
|
||||
use transaction_pool::TransactionPool;
|
||||
use transaction_pool::txpool::{Pool as TransactionPool};
|
||||
use node_primitives::{Block, Hash, Timestamp, BlockId};
|
||||
use node_runtime::{GenesisConfig, BlockPeriod, StorageValue, Runtime};
|
||||
use client::Client;
|
||||
use consensus::AuthoringApi;
|
||||
use node_network::{Protocol as DemoProtocol, consensus::ConsensusNetwork};
|
||||
use transaction_pool::Client as TPApi;
|
||||
use tokio::runtime::TaskExecutor;
|
||||
use service::FactoryFullConfiguration;
|
||||
use primitives::{Blake2Hasher, storage::StorageKey, twox_128};
|
||||
@@ -75,7 +74,7 @@ pub type NetworkService = network::Service<Block, <Factory as service::ServiceFa
|
||||
/// A collection of type to generalise specific components over full / light client.
|
||||
pub trait Components: service::Components {
|
||||
/// Demo API.
|
||||
type Api: 'static + AuthoringApi + TPApi + Send + Sync;
|
||||
type Api: 'static + AuthoringApi + Send + Sync;
|
||||
/// Client backend.
|
||||
type Backend: 'static + client::backend::Backend<Block, Blake2Hasher>;
|
||||
/// Client executor.
|
||||
@@ -109,21 +108,21 @@ impl service::ServiceFactory for Factory {
|
||||
type ExtrinsicHash = Hash;
|
||||
type NetworkProtocol = DemoProtocol;
|
||||
type RuntimeDispatch = node_executor::Executor;
|
||||
type FullTransactionPoolApi = transaction_pool::ChainApi<service::FullClient<Self>>;
|
||||
type LightTransactionPoolApi = transaction_pool::ChainApi<service::LightClient<Self>>;
|
||||
type FullTransactionPoolApi = transaction_pool::ChainApi<service::FullBackend<Self>, service::FullExecutor<Self>, Block>;
|
||||
type LightTransactionPoolApi = transaction_pool::ChainApi<service::LightBackend<Self>, service::LightExecutor<Self>, Block>;
|
||||
type Genesis = GenesisConfig;
|
||||
type Configuration = CustomConfiguration;
|
||||
type FullService = Service<service::FullComponents<Self>>;
|
||||
type LightService = Service<service::LightComponents<Self>>;
|
||||
|
||||
fn build_full_transaction_pool(config: TransactionPoolOptions, client: Arc<service::FullClient<Self>>)
|
||||
-> Result<TransactionPool<service::FullClient<Self>>, Error>
|
||||
-> Result<TransactionPool<Self::FullTransactionPoolApi>, Error>
|
||||
{
|
||||
Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client)))
|
||||
}
|
||||
|
||||
fn build_light_transaction_pool(config: TransactionPoolOptions, client: Arc<service::LightClient<Self>>)
|
||||
-> Result<TransactionPool<service::LightClient<Self>>, Error>
|
||||
-> Result<TransactionPool<Self::LightTransactionPoolApi>, Error>
|
||||
{
|
||||
Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client)))
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
[package]
|
||||
name = "node-transaction-pool"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
error-chain = "0.12"
|
||||
node-primitives = { path = "../primitives" }
|
||||
node-runtime = { path = "../runtime" }
|
||||
substrate-client = { path = "../../core/client" }
|
||||
parity-codec = "2.0"
|
||||
substrate-keyring = { path = "../../core/keyring" }
|
||||
substrate-transaction-pool = { path = "../../core/transaction-pool" }
|
||||
substrate-primitives = { path = "../../core/primitives" }
|
||||
sr-primitives = { path = "../../core/sr-primitives" }
|
||||
@@ -1,73 +0,0 @@
|
||||
// Copyright 2018 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 transaction_pool;
|
||||
use primitives::Hash;
|
||||
use runtime::{Address, UncheckedExtrinsic};
|
||||
use client;
|
||||
|
||||
error_chain! {
|
||||
links {
|
||||
Client(client::error::Error, client::error::ErrorKind);
|
||||
Pool(transaction_pool::Error, transaction_pool::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("Inherent transactions cannot be queued."),
|
||||
}
|
||||
/// Attempted to queue a transaction with bad signature.
|
||||
BadSignature(e: &'static str) {
|
||||
description("Transaction had bad signature."),
|
||||
display("Transaction had bad signature: {}", e),
|
||||
}
|
||||
/// 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()),
|
||||
}
|
||||
/// Runtime failure.
|
||||
UnrecognisedAddress(who: Address) {
|
||||
description("Unrecognised address in extrinsic"),
|
||||
display("Unrecognised address in extrinsic: {}", who),
|
||||
}
|
||||
/// Extrinsic too large
|
||||
TooLarge(got: usize, max: usize) {
|
||||
description("Extrinsic too large"),
|
||||
display("Extrinsic is too large ({} > {})", got, max),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl transaction_pool::IntoPoolError for Error {
|
||||
fn into_pool_error(self) -> ::std::result::Result<transaction_pool::Error, Self> {
|
||||
match self {
|
||||
Error(ErrorKind::Pool(e), c) => Ok(transaction_pool::Error(e, c)),
|
||||
e => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,275 +0,0 @@
|
||||
// Copyright 2018 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/>.
|
||||
|
||||
extern crate substrate_client as client;
|
||||
extern crate parity_codec as codec;
|
||||
extern crate substrate_transaction_pool as transaction_pool;
|
||||
extern crate substrate_primitives;
|
||||
extern crate sr_primitives;
|
||||
extern crate node_runtime as runtime;
|
||||
extern crate node_primitives as primitives;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate substrate_keyring;
|
||||
|
||||
#[macro_use]
|
||||
extern crate error_chain;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
mod error;
|
||||
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::HashMap,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use client::{Client as SubstrateClient, CallExecutor};
|
||||
use client::runtime_api::OldTxQueue;
|
||||
use transaction_pool::{Readiness, scoring::{Change, Choice}, VerifiedFor, ExtrinsicFor};
|
||||
use primitives::{AccountId, Hash, Index};
|
||||
use runtime::{Address, UncheckedExtrinsic};
|
||||
use substrate_primitives::{Blake2Hasher};
|
||||
use sr_primitives::generic::BlockId;
|
||||
use sr_primitives::traits::{
|
||||
Bounded, Checkable, Block as BlockT, Hash as HashT, Header as HeaderT, BlakeTwo256, Lookup, CurrentHeight,
|
||||
BlockNumberToHash
|
||||
};
|
||||
|
||||
pub use transaction_pool::{Options, Status, LightStatus, VerifiedTransaction as VerifiedTransactionOps};
|
||||
pub use error::{Error, ErrorKind, Result};
|
||||
|
||||
/// Maximal size of a single encoded extrinsic.
|
||||
const MAX_TRANSACTION_SIZE: usize = 4 * 1024 * 1024;
|
||||
|
||||
/// Local client abstraction for the transaction-pool.
|
||||
pub trait Client:
|
||||
Send
|
||||
+ Sync
|
||||
+ CurrentHeight<BlockNumber=<<<Self as Client>::Block as BlockT>::Header as HeaderT>::Number>
|
||||
+ BlockNumberToHash<BlockNumber=<<<Self as Client>::Block as BlockT>::Header as HeaderT>::Number, Hash=<<Self as Client>::Block as BlockT>::Hash>
|
||||
+ OldTxQueue<<Self as Client>::Block>
|
||||
{
|
||||
/// The block used for this API type.
|
||||
type Block: BlockT;
|
||||
}
|
||||
|
||||
impl<B, E, Block> Client for SubstrateClient<B, E, Block> where
|
||||
B: client::backend::Backend<Block, Blake2Hasher> + Send + Sync + 'static,
|
||||
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + Clone + 'static,
|
||||
Block: BlockT,
|
||||
{
|
||||
type Block = Block;
|
||||
}
|
||||
|
||||
/// Type alias for the transaction pool.
|
||||
pub type TransactionPool<C> = transaction_pool::Pool<ChainApi<C>>;
|
||||
|
||||
/// A verified transaction which should be includable and non-inherent.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct VerifiedTransaction {
|
||||
/// Transaction hash.
|
||||
pub hash: Hash,
|
||||
/// Transaction sender.
|
||||
pub sender: AccountId,
|
||||
/// Transaction index.
|
||||
pub index: Index,
|
||||
encoded_size: usize,
|
||||
}
|
||||
|
||||
impl VerifiedTransaction {
|
||||
/// Get the 256-bit hash of this transaction.
|
||||
pub fn hash(&self) -> &Hash {
|
||||
&self.hash
|
||||
}
|
||||
|
||||
/// Get the account ID of the sender of this transaction.
|
||||
pub fn index(&self) -> Index {
|
||||
self.index
|
||||
}
|
||||
|
||||
/// Get encoded size of the transaction.
|
||||
pub fn encoded_size(&self) -> usize {
|
||||
self.encoded_size
|
||||
}
|
||||
}
|
||||
|
||||
impl transaction_pool::VerifiedTransaction for VerifiedTransaction {
|
||||
type Hash = Hash;
|
||||
type Sender = AccountId;
|
||||
|
||||
fn hash(&self) -> &Self::Hash {
|
||||
&self.hash
|
||||
}
|
||||
|
||||
fn sender(&self) -> &Self::Sender {
|
||||
&self.sender
|
||||
}
|
||||
|
||||
fn mem_usage(&self) -> usize {
|
||||
self.encoded_size // TODO
|
||||
}
|
||||
}
|
||||
|
||||
/// The transaction pool logic.
|
||||
pub struct ChainApi<C: Client> {
|
||||
api: Arc<C>,
|
||||
}
|
||||
|
||||
impl<C: Client> ChainApi<C> {
|
||||
/// Create a new instance.
|
||||
pub fn new(api: Arc<C>) -> Self {
|
||||
ChainApi {
|
||||
api,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// "Chain" context (used for checking transactions) which uses data local to our node/transaction pool.
|
||||
///
|
||||
/// This is due for removal when #721 lands
|
||||
pub struct LocalContext<'a, A: 'a>(&'a Arc<A>);
|
||||
impl<'a, C: 'a + Client> CurrentHeight for LocalContext<'a, C> {
|
||||
type BlockNumber = <C as CurrentHeight>::BlockNumber;
|
||||
fn current_height(&self) -> Self::BlockNumber {
|
||||
self.0.current_height()
|
||||
}
|
||||
}
|
||||
impl<'a, C: 'a + Client> BlockNumberToHash for LocalContext<'a, C> {
|
||||
type BlockNumber = <C as BlockNumberToHash>::BlockNumber;
|
||||
type Hash = <C as BlockNumberToHash>::Hash;
|
||||
fn block_number_to_hash(&self, n: Self::BlockNumber) -> Option<Self::Hash> {
|
||||
self.0.block_number_to_hash(n)
|
||||
}
|
||||
}
|
||||
impl<'a, C: 'a + Client> Lookup for LocalContext<'a, C> {
|
||||
type Source = Address;
|
||||
type Target = AccountId;
|
||||
fn lookup(&self, a: Address) -> ::std::result::Result<AccountId, &'static str> {
|
||||
self.0.lookup_address(&BlockId::number(self.current_height()), &a).unwrap_or(None).ok_or("error with lookup")
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Client> transaction_pool::ChainApi for ChainApi<C> {
|
||||
type Block = C::Block;
|
||||
type Hash = Hash;
|
||||
type Sender = AccountId;
|
||||
type VEx = VerifiedTransaction;
|
||||
type Ready = HashMap<AccountId, u64>;
|
||||
type Error = Error;
|
||||
type Score = u64;
|
||||
type Event = ();
|
||||
|
||||
fn latest_hash(&self) -> <C::Block as BlockT>::Hash {
|
||||
self.api.block_number_to_hash(self.api.current_height()).expect("Latest block number always has a hash; qed")
|
||||
}
|
||||
|
||||
fn verify_transaction(&self, _at: &BlockId<Self::Block>, xt: &ExtrinsicFor<Self>) -> Result<Self::VEx> {
|
||||
let encoded = xt.encode();
|
||||
let uxt = UncheckedExtrinsic::decode(&mut encoded.as_slice()).ok_or_else(|| ErrorKind::InvalidExtrinsicFormat)?;
|
||||
if !uxt.is_signed() {
|
||||
bail!(ErrorKind::IsInherent(uxt))
|
||||
}
|
||||
|
||||
let (encoded_size, hash) = (encoded.len(), BlakeTwo256::hash(&encoded));
|
||||
if encoded_size > MAX_TRANSACTION_SIZE {
|
||||
bail!(ErrorKind::TooLarge(encoded_size, MAX_TRANSACTION_SIZE));
|
||||
}
|
||||
|
||||
debug!(target: "transaction-pool", "Transaction submitted: {}", ::substrate_primitives::hexdisplay::HexDisplay::from(&encoded));
|
||||
let checked = uxt.clone().check(&LocalContext(&self.api))?;
|
||||
let (sender, index) = checked.signed.expect("function previously bailed unless uxt.is_signed(); qed");
|
||||
|
||||
|
||||
if encoded_size < 1024 {
|
||||
debug!(target: "transaction-pool", "Transaction verified: {} => {:?}", hash, uxt);
|
||||
} else {
|
||||
debug!(target: "transaction-pool", "Transaction verified: {} ({} bytes is too large to display)", hash, encoded_size);
|
||||
}
|
||||
|
||||
Ok(VerifiedTransaction {
|
||||
index,
|
||||
sender,
|
||||
hash,
|
||||
encoded_size,
|
||||
})
|
||||
}
|
||||
|
||||
fn ready(&self) -> Self::Ready {
|
||||
HashMap::default()
|
||||
}
|
||||
|
||||
fn is_ready(&self, at: &BlockId<Self::Block>, known_nonces: &mut Self::Ready, xt: &VerifiedFor<Self>) -> Readiness {
|
||||
let sender = xt.verified.sender().clone();
|
||||
trace!(target: "transaction-pool", "Checking readiness of {} (from {})", xt.verified.hash, sender);
|
||||
|
||||
// TODO: find a way to handle index error properly -- will need changes to
|
||||
// transaction-pool trait.
|
||||
let api = &self.api;
|
||||
let next_index = known_nonces.entry(sender)
|
||||
.or_insert_with(|| api.account_nonce(at, &sender).ok().unwrap_or_else(Bounded::max_value));
|
||||
|
||||
trace!(target: "transaction-pool", "Next index for sender is {}; xt index is {}", next_index, xt.verified.index);
|
||||
|
||||
let result = match xt.verified.index.cmp(&next_index) {
|
||||
// TODO: this won't work perfectly since accounts can now be killed, returning the nonce
|
||||
// to zero.
|
||||
// We should detect if the index was reset and mark all transactions as `Stale` for cull to work correctly.
|
||||
// Otherwise those transactions will keep occupying the queue.
|
||||
// Perhaps we could mark as stale if `index - state_index` > X?
|
||||
Ordering::Greater => Readiness::Future,
|
||||
Ordering::Equal => Readiness::Ready,
|
||||
// TODO [ToDr] Should mark transactions referencing too old blockhash as `Stale` as well.
|
||||
Ordering::Less => Readiness::Stale,
|
||||
};
|
||||
|
||||
// remember to increment `next_index`
|
||||
*next_index = next_index.saturating_add(1);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn compare(old: &VerifiedFor<Self>, other: &VerifiedFor<Self>) -> Ordering {
|
||||
old.verified.index().cmp(&other.verified.index())
|
||||
}
|
||||
|
||||
fn choose(old: &VerifiedFor<Self>, new: &VerifiedFor<Self>) -> Choice {
|
||||
if old.verified.index() == new.verified.index() {
|
||||
return Choice::ReplaceOld;
|
||||
}
|
||||
Choice::InsertNew
|
||||
}
|
||||
|
||||
fn update_scores(
|
||||
xts: &[transaction_pool::Transaction<VerifiedFor<Self>>],
|
||||
scores: &mut [Self::Score],
|
||||
_change: Change<()>
|
||||
) {
|
||||
for i in 0..xts.len() {
|
||||
// all the same score since there are no fees.
|
||||
// TODO: prioritize things like misbehavior or fishermen reports
|
||||
scores[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn should_replace(_old: &VerifiedFor<Self>, _new: &VerifiedFor<Self>) -> Choice {
|
||||
// Don't allow new transactions if we are reaching the limit.
|
||||
Choice::RejectNew
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user