From 7af8604cbe0d6047a03fc2a8e8257cef0614c46f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 30 May 2019 13:50:23 +0200 Subject: [PATCH] Add RPC to remove and ban transactions from the pool. (#2732) --- substrate/core/rpc/src/author/hash.rs | 30 ++++++++++++++ substrate/core/rpc/src/author/mod.rs | 40 +++++++++++++++---- substrate/core/rpc/src/author/tests.rs | 28 +++++++++++++ .../core/transaction-pool/graph/src/pool.rs | 3 +- 4 files changed, 91 insertions(+), 10 deletions(-) create mode 100644 substrate/core/rpc/src/author/hash.rs diff --git a/substrate/core/rpc/src/author/hash.rs b/substrate/core/rpc/src/author/hash.rs new file mode 100644 index 0000000000..052c479834 --- /dev/null +++ b/substrate/core/rpc/src/author/hash.rs @@ -0,0 +1,30 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use primitives::Bytes; +use serde::Deserialize; + +/// RPC Extrinsic or hash +/// +/// Allows to refer to extrinsics either by their raw representation or by it's hash. +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum ExtrinsicOrHash { + /// The hash of the extrinsic. + Hash(Hash), + /// Raw extrinsic bytes. + Extrinsic(Bytes), +} diff --git a/substrate/core/rpc/src/author/mod.rs b/substrate/core/rpc/src/author/mod.rs index ae160f1d1a..59641e8466 100644 --- a/substrate/core/rpc/src/author/mod.rs +++ b/substrate/core/rpc/src/author/mod.rs @@ -18,9 +18,15 @@ use std::sync::Arc; -use log::warn; use client::{self, Client}; +use crate::rpc::futures::{Sink, Stream, Future}; +use crate::subscriptions::Subscriptions; +use jsonrpc_derive::rpc; +use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId}; +use log::warn; use parity_codec::{Encode, Decode}; +use primitives::{Bytes, Blake2Hasher, H256}; +use runtime_primitives::{generic, traits}; use transaction_pool::{ txpool::{ ChainApi as PoolChainApi, @@ -31,14 +37,9 @@ use transaction_pool::{ watcher::Status, }, }; -use jsonrpc_derive::rpc; -use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId}; -use primitives::{Bytes, Blake2Hasher, H256}; -use crate::rpc::futures::{Sink, Stream, Future}; -use runtime_primitives::{generic, traits}; -use crate::subscriptions::Subscriptions; pub mod error; +mod hash; #[cfg(test)] mod tests; @@ -59,6 +60,10 @@ pub trait AuthorApi { #[rpc(name = "author_pendingExtrinsics")] fn pending_extrinsics(&self) -> Result>; + /// Remove given extrinsic from the pool and temporarily ban it to prevent reimporting. + #[rpc(name = "author_removeExtrinsic")] + fn remove_extrinsic(&self, bytes_or_hash: Vec>) -> Result>; + /// Submit an extrinsic to watch. #[pubsub(subscription = "author_extrinsicUpdate", subscribe, name = "author_submitAndWatchExtrinsic")] fn watch_extrinsic(&self, metadata: Self::Metadata, subscriber: Subscriber>, bytes: Bytes); @@ -72,7 +77,7 @@ pub trait AuthorApi { pub struct Author where P: PoolChainApi + Sync + Send + 'static { /// Substrate client client: Arc::Block, RA>>, - /// Extrinsic pool + /// Transactions pool pool: Arc>, /// Subscriptions manager subscriptions: Subscriptions, @@ -118,6 +123,25 @@ impl AuthorApi, BlockHash

> for Author whe Ok(self.pool.ready().map(|tx| tx.data.encode().into()).collect()) } + fn remove_extrinsic(&self, bytes_or_hash: Vec>>) -> Result>> { + let hashes = bytes_or_hash.into_iter() + .map(|x| match x { + hash::ExtrinsicOrHash::Hash(h) => Ok(h), + hash::ExtrinsicOrHash::Extrinsic(bytes) => { + let xt = Decode::decode(&mut &bytes[..]).ok_or(error::Error::BadFormat)?; + Ok(self.pool.hash_of(&xt)) + }, + }) + .collect::>>()?; + + Ok( + self.pool.remove_invalid(&hashes) + .into_iter() + .map(|tx| tx.hash.clone()) + .collect() + ) + } + fn watch_extrinsic(&self, _metadata: Self::Metadata, subscriber: Subscriber, BlockHash

>>, xt: Bytes) { let submit = || -> Result<_> { let best_block_hash = self.client.info()?.chain.best_hash; diff --git a/substrate/core/rpc/src/author/tests.rs b/substrate/core/rpc/src/author/tests.rs index 4d0277f7d6..4c6a724acd 100644 --- a/substrate/core/rpc/src/author/tests.rs +++ b/substrate/core/rpc/src/author/tests.rs @@ -137,3 +137,31 @@ fn should_return_pending_extrinsics() { Ok(ref expected) if *expected == vec![Bytes(ex.encode())] ); } + +#[test] +fn should_remove_extrinsics() { + let runtime = runtime::Runtime::new().unwrap(); + let client = Arc::new(test_client::new()); + let pool = Arc::new(Pool::new(Default::default(), ChainApi::new(client.clone()))); + let p = Author { + client, + pool: pool.clone(), + subscriptions: Subscriptions::new(runtime.executor()), + }; + let ex1 = uxt(AccountKeyring::Alice, 0); + p.submit_extrinsic(ex1.encode().into()).unwrap(); + let ex2 = uxt(AccountKeyring::Alice, 1); + p.submit_extrinsic(ex2.encode().into()).unwrap(); + let ex3 = uxt(AccountKeyring::Bob, 0); + let hash3 = p.submit_extrinsic(ex3.encode().into()).unwrap(); + assert_eq!(pool.status().ready, 3); + + // now remove all 3 + let removed = p.remove_extrinsic(vec![ + hash::ExtrinsicOrHash::Hash(hash3), + // Removing this one will also remove ex2 + hash::ExtrinsicOrHash::Extrinsic(ex1.encode().into()), + ]).unwrap(); + + assert_eq!(removed.len(), 3); +} diff --git a/substrate/core/transaction-pool/graph/src/pool.rs b/substrate/core/transaction-pool/graph/src/pool.rs index 61e234f156..4498598aee 100644 --- a/substrate/core/transaction-pool/graph/src/pool.rs +++ b/substrate/core/transaction-pool/graph/src/pool.rs @@ -417,8 +417,7 @@ impl Pool { } /// Returns transaction hash - #[cfg(test)] - fn hash_of(&self, xt: &ExtrinsicFor) -> ExHash { + pub fn hash_of(&self, xt: &ExtrinsicFor) -> ExHash { self.api.hash_and_length(xt).0 } }