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:
Tomasz Drwięga
2018-10-12 13:09:35 +02:00
committed by Gav Wood
parent 2404d3c89f
commit 671b0e0007
48 changed files with 1234 additions and 1524 deletions
+2 -2
View File
@@ -17,14 +17,14 @@
//! Authoring RPC module errors.
use client;
use transaction_pool;
use transaction_pool::txpool;
use rpc;
use errors;
error_chain! {
links {
Pool(transaction_pool::Error, transaction_pool::ErrorKind) #[doc = "Pool error"];
Pool(txpool::error::Error, txpool::error::ErrorKind) #[doc = "Pool error"];
Client(client::error::Error, client::error::ErrorKind) #[doc = "Client error"];
}
errors {
+13 -14
View File
@@ -21,15 +21,15 @@ use std::sync::Arc;
use client::{self, Client};
use codec::Decode;
use transaction_pool::{
Pool,
IntoPoolError,
ChainApi as PoolChainApi,
watcher::Status,
VerifiedTransaction,
AllExtrinsics,
ExHash,
ExtrinsicFor,
HashOf,
txpool::{
ChainApi as PoolChainApi,
BlockHash,
ExHash,
ExtrinsicFor,
IntoPoolError,
Pool,
watcher::Status,
},
};
use jsonrpc_macros::pubsub;
use jsonrpc_pubsub::SubscriptionId;
@@ -103,7 +103,7 @@ impl<B, E, P> Author<B, E, P> where
}
}
impl<B, E, P> AuthorApi<ExHash<P>, HashOf<P::Block>, ExtrinsicFor<P>, AllExtrinsics<P>> for Author<B, E, P> where
impl<B, E, P> AuthorApi<ExHash<P>, BlockHash<P>, ExtrinsicFor<P>, Vec<ExtrinsicFor<P>>> for Author<B, E, P> where
B: client::backend::Backend<<P as PoolChainApi>::Block, Blake2Hasher> + Send + Sync + 'static,
E: client::CallExecutor<<P as PoolChainApi>::Block, Blake2Hasher> + Send + Sync + 'static,
P: PoolChainApi + Sync + Send + 'static,
@@ -124,14 +124,13 @@ impl<B, E, P> AuthorApi<ExHash<P>, HashOf<P::Block>, ExtrinsicFor<P>, AllExtrins
.map(Into::into)
.unwrap_or_else(|e| error::ErrorKind::Verification(Box::new(e)).into())
)
.map(|ex| ex.hash().clone())
}
fn pending_extrinsics(&self) -> Result<AllExtrinsics<P>> {
Ok(self.pool.all())
fn pending_extrinsics(&self) -> Result<Vec<ExtrinsicFor<P>>> {
Ok(self.pool.all(usize::max_value()))
}
fn watch_extrinsic(&self, _metadata: Self::Metadata, subscriber: pubsub::Subscriber<Status<ExHash<P>, HashOf<P::Block>>>, xt: Bytes) {
fn watch_extrinsic(&self, _metadata: Self::Metadata, subscriber: pubsub::Subscriber<Status<ExHash<P>, BlockHash<P>>>, xt: Bytes) {
let submit = || -> Result<_> {
let best_block_hash = self.client.info()?.chain.best_hash;
let dxt = <<P as PoolChainApi>::Block as traits::Block>::Extrinsic::decode(&mut &xt[..]).ok_or(error::Error::from(error::ErrorKind::BadFormat))?;
+52 -100
View File
@@ -16,126 +16,66 @@
use super::*;
use std::{sync::Arc, result::Result};
use std::sync::Arc;
use codec::Encode;
use transaction_pool::{VerifiedTransaction, scoring, Transaction, ChainApi, Error as PoolError,
Readiness, ExtrinsicFor, VerifiedFor};
use test_client::runtime::{Block, Extrinsic, Transfer};
use transaction_pool::{
txpool::Pool,
ChainApi,
};
use primitives::H256;
use test_client::keyring::Keyring;
use test_client::runtime::{Extrinsic, Transfer};
use test_client;
use tokio::runtime;
use runtime_primitives::{traits, generic::BlockId};
#[derive(Clone, Debug)]
pub struct Verified
{
sender: u64,
hash: u64,
}
impl VerifiedTransaction for Verified {
type Hash = u64;
type Sender = u64;
fn hash(&self) -> &Self::Hash { &self.hash }
fn sender(&self) -> &Self::Sender { &self.sender }
fn mem_usage(&self) -> usize { 256 }
}
struct TestApi;
impl ChainApi for TestApi {
type Block = Block;
type Hash = u64;
type Sender = u64;
type Error = PoolError;
type VEx = Verified;
type Score = u64;
type Event = ();
type Ready = ();
fn latest_hash(&self) -> <Block as traits::Block>::Hash {
1.into()
}
fn verify_transaction(&self, _at: &BlockId<Block>, uxt: &ExtrinsicFor<Self>) -> Result<Self::VEx, Self::Error> {
Ok(Verified {
sender: uxt.transfer.from[31] as u64,
hash: uxt.transfer.nonce,
})
}
fn is_ready(&self, _at: &BlockId<Block>, _c: &mut Self::Ready, _xt: &VerifiedFor<Self>) -> Readiness {
Readiness::Ready
}
fn ready(&self) -> Self::Ready { }
fn compare(old: &VerifiedFor<Self>, other: &VerifiedFor<Self>) -> ::std::cmp::Ordering {
old.verified.hash().cmp(&other.verified.hash())
}
fn choose(_old: &VerifiedFor<Self>, _new: &VerifiedFor<Self>) -> scoring::Choice {
scoring::Choice::ReplaceOld
}
fn update_scores(xts: &[Transaction<VerifiedFor<Self>>], scores: &mut [Self::Score], _change: scoring::Change<()>) {
for i in 0..xts.len() {
scores[i] = xts[i].verified.sender
}
}
fn should_replace(_old: &VerifiedFor<Self>, _new: &VerifiedFor<Self>) -> scoring::Choice {
scoring::Choice::ReplaceOld
}
}
type DummyTxPool = Pool<TestApi>;
fn uxt(sender: u64, hash: u64) -> Extrinsic {
Extrinsic {
signature: Default::default(),
transfer: Transfer {
amount: Default::default(),
nonce: hash,
from: From::from(sender),
to: Default::default(),
}
}
fn uxt(sender: Keyring, nonce: u64) -> Extrinsic {
let tx = Transfer {
amount: Default::default(),
nonce,
from: sender.to_raw_public().into(),
to: Default::default(),
};
let signature = Keyring::from_raw_public(tx.from.0).unwrap().sign(&tx.encode()).into();
Extrinsic { transfer: tx, signature }
}
#[test]
fn submit_transaction_should_not_cause_error() {
let runtime = runtime::Runtime::new().unwrap();
let client = Arc::new(test_client::new());
let p = Author {
client: Arc::new(test_client::new()),
pool: Arc::new(DummyTxPool::new(Default::default(), TestApi)),
client: client.clone(),
pool: Arc::new(Pool::new(Default::default(), ChainApi::new(client))),
subscriptions: Subscriptions::new(runtime.executor()),
};
let h: H256 = hex!("e10ad66bce51ef3e2a1167934ce3740d2d8c703810f9b314e89f2e783f75e826").into();
assert_matches!(
AuthorApi::submit_extrinsic(&p, uxt(5, 1).encode().into()),
Ok(1)
AuthorApi::submit_extrinsic(&p, uxt(Keyring::Alice, 1).encode().into()),
Ok(h2) if h == h2
);
assert!(
AuthorApi::submit_extrinsic(&p, uxt(5, 1).encode().into()).is_err()
AuthorApi::submit_extrinsic(&p, uxt(Keyring::Alice, 1).encode().into()).is_err()
);
}
#[test]
fn submit_rich_transaction_should_not_cause_error() {
let runtime = runtime::Runtime::new().unwrap();
let client = Arc::new(test_client::new());
let p = Author {
client: Arc::new(test_client::new()),
pool: Arc::new(DummyTxPool::new(Default::default(), TestApi)),
client: client.clone(),
pool: Arc::new(Pool::new(Default::default(), ChainApi::new(client.clone()))),
subscriptions: Subscriptions::new(runtime.executor()),
};
let h: H256 = hex!("fccc48291473c53746cd267cf848449edd7711ee6511fba96919d5f9f4859e4f").into();
assert_matches!(
AuthorApi::submit_rich_extrinsic(&p, uxt(5, 0)),
Ok(0)
AuthorApi::submit_rich_extrinsic(&p, uxt(Keyring::Alice, 0)),
Ok(h2) if h == h2
);
assert!(
AuthorApi::submit_rich_extrinsic(&p, uxt(5, 0)).is_err()
AuthorApi::submit_rich_extrinsic(&p, uxt(Keyring::Alice, 0)).is_err()
);
}
@@ -143,40 +83,52 @@ fn submit_rich_transaction_should_not_cause_error() {
fn should_watch_extrinsic() {
//given
let mut runtime = runtime::Runtime::new().unwrap();
let pool = Arc::new(DummyTxPool::new(Default::default(), TestApi));
let client = Arc::new(test_client::new());
let pool = Arc::new(Pool::new(Default::default(), ChainApi::new(client.clone())));
let p = Author {
client: Arc::new(test_client::new()),
client,
pool: pool.clone(),
subscriptions: Subscriptions::new(runtime.executor()),
};
let (subscriber, id_rx, data) = ::jsonrpc_macros::pubsub::Subscriber::new_test("test");
// when
p.watch_extrinsic(Default::default(), subscriber, uxt(5, 5).encode().into());
p.watch_extrinsic(Default::default(), subscriber, uxt(Keyring::Alice, 0).encode().into());
// then
assert_eq!(runtime.block_on(id_rx), Ok(Ok(1.into())));
// check notifications
AuthorApi::submit_rich_extrinsic(&p, uxt(5, 1)).unwrap();
let replacement = {
let tx = Transfer {
amount: 5,
nonce: 0,
from: Keyring::Alice.to_raw_public().into(),
to: Default::default(),
};
let signature = Keyring::from_raw_public(tx.from.0).unwrap().sign(&tx.encode()).into();
Extrinsic { transfer: tx, signature }
};
AuthorApi::submit_rich_extrinsic(&p, replacement).unwrap();
assert_eq!(
runtime.block_on(data.into_future()).unwrap().0,
Some(r#"{"jsonrpc":"2.0","method":"test","params":{"result":{"usurped":1},"subscription":1}}"#.into())
Some(r#"{"jsonrpc":"2.0","method":"test","params":{"result":{"usurped":"0xed454dcee51431679c2559403187a56567fded1fc50b6ae3aada87c1d412df5c"},"subscription":1}}"#.into())
);
}
#[test]
fn should_return_pending_extrinsics() {
let runtime = runtime::Runtime::new().unwrap();
let pool = Arc::new(DummyTxPool::new(Default::default(), TestApi));
let client = Arc::new(test_client::new());
let pool = Arc::new(Pool::new(Default::default(), ChainApi::new(client.clone())));
let p = Author {
client: Arc::new(test_client::new()),
client,
pool: pool.clone(),
subscriptions: Subscriptions::new(runtime.executor()),
};
let ex = uxt(5, 1);
let ex = uxt(Keyring::Alice, 0);
AuthorApi::submit_rich_extrinsic(&p, ex.clone()).unwrap();
assert_matches!(
p.pending_extrinsics(),
Ok(ref expected) if expected.get(&5) == Some(&vec![ex])
Ok(ref expected) if expected == &vec![ex]
);
}