Enumeratable accounts (#195)

* Merge remote-tracking branch 'origin/master' into gav-xts-dont-panic

* Update wasm.

* consensus, session and staking all panic-safe.

* Democracy doesn't panic in apply.

* Fix tests.

* Extra helper macro, council depanicked.

* Fix one test.

* Fix up all council tests. No panics!

* Council voting depanicked.

* Dispatch returns result.

* session & staking tests updated

* Fix democracy tests.

* Fix council tests.

* Fix up polkadot parachains in runtime

* Fix borked merge

* More Slicable support

Support general `Option` and array types.

* Basic storage types.

* Existential deposit for contract creation

* Basic implemnetation along with removals

* Fix tests.

* externalities builder fix.

* Tests.

* Fix up the runtime.

* Fix tests.

* Add generic `Address` type.

* Initial function integration of Address into Extrinsic.

* Fix build

* All tests compile.

* Fix (some) tests.

* Fix signing.

* Push error.

* transfer can accept Address

* Make Address generic over AccountIndex

* Fix test

* Make Council use Address for dispatch.

* Fix build

* Bend over backwards to support braindead derive.

* Repot some files.

* Fix tests.

* Fix grumbles

* Remove Default bound

* Fix build for new nightly.

* Make `apply_extrinsic` never panic, return useful Result.

* More merge hell

* Doesn't build, but might do soon

* Serde woes

* get substrate-runtime-staking compiling

* Polkadot builds again!

* Fix all build.

* Fix tests & binaries.

* Reserve some extra initial byte values of address for future format changes

* Make semantic of `ReservedBalance` clear.

* Fix panic handler.

* Integrate other balance transformations into the new model

Fix up staking tests.

* Fix runtime tests.

* Fix panic build.

* Tests for demonstrating interaction between balance types.

* Repot some runtime code

* Fix checkedblock in non-std builds

* Get rid of `DoLookup` phantom.

* Attempt to make transaction_pool work with lookups.

* Remove vscode settings

* New attempt at making transaction pool work.

* It builds again!

* --all builds

* Fix tests.

* New build.

* Test account nonce reset.

* polkadot transaction pool tests/framework.

* Address grumbles.

* Revert bad `map_or`

* Rebuild binaries, workaround.

* Avoid casting to usize early.

* reenable sync tests
This commit is contained in:
Gav Wood
2018-06-18 20:23:41 +02:00
committed by GitHub
parent 8c4281c11a
commit 769af90ce8
20 changed files with 663 additions and 235 deletions
+13 -3
View File
@@ -16,7 +16,7 @@
use extrinsic_pool::{self, txpool};
use primitives::Hash;
use runtime::UncheckedExtrinsic;
use runtime::{Address, UncheckedExtrinsic};
error_chain! {
links {
@@ -34,9 +34,9 @@ error_chain! {
display("Inehrent transactions cannot be queued."),
}
/// Attempted to queue a transaction with bad signature.
BadSignature(xt: UncheckedExtrinsic) {
BadSignature(e: &'static str) {
description("Transaction had bad signature."),
display("Transaction had bad signature."),
display("Transaction had bad signature: {}", e),
}
/// Attempted to queue a transaction that is already in the pool.
AlreadyImported(hash: Hash) {
@@ -48,6 +48,16 @@ error_chain! {
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 is not yet checked.
NotReady {
description("Indexed address is unverified"),
display("Indexed address is unverified"),
}
}
}
+385 -63
View File
@@ -22,6 +22,10 @@ extern crate substrate_runtime_primitives;
extern crate polkadot_runtime as runtime;
extern crate polkadot_primitives as primitives;
extern crate polkadot_api;
extern crate parking_lot;
#[cfg(test)]
extern crate substrate_keyring;
#[macro_use]
extern crate error_chain;
@@ -33,18 +37,20 @@ mod error;
use std::{
cmp::Ordering,
collections::HashMap,
collections::{hash_map::Entry, HashMap},
ops::Deref,
sync::Arc,
result
};
use parking_lot::Mutex;
use codec::Slicable;
use extrinsic_pool::{Pool, txpool::{self, Readiness, scoring::{Change, Choice}}};
use extrinsic_pool::api::ExtrinsicPool;
use polkadot_api::PolkadotApi;
use primitives::{AccountId, Hash, UncheckedExtrinsic as FutureProofUncheckedExtrinsic};
use runtime::UncheckedExtrinsic;
use substrate_runtime_primitives::traits::{Bounded, Checkable, BlakeTwo256, Hashing};
use primitives::{AccountId, AccountIndex, Hash, Index, UncheckedExtrinsic as FutureProofUncheckedExtrinsic};
use runtime::{Address, RawAddress, UncheckedExtrinsic};
use substrate_runtime_primitives::traits::{Bounded, Checkable, Hashing, BlakeTwo256};
pub use extrinsic_pool::txpool::{Options, Status, LightStatus, VerifiedTransaction as VerifiedTransactionOps};
pub use error::{Error, ErrorKind, Result};
@@ -53,37 +59,68 @@ pub use error::{Error, ErrorKind, Result};
pub type CheckedExtrinsic = <UncheckedExtrinsic as Checkable>::Checked;
/// A verified transaction which should be includable and non-inherent.
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct VerifiedTransaction {
inner: CheckedExtrinsic,
original: UncheckedExtrinsic,
// `create()` will leave this as `Some` only if the `Address` is an `AccountId`, otherwise a
// call to `polish` is needed.
inner: Mutex<Option<CheckedExtrinsic>>,
hash: Hash,
encoded_size: usize,
}
impl Clone for VerifiedTransaction {
fn clone(&self) -> Self {
VerifiedTransaction {
original: self.original.clone(),
inner: Mutex::new(self.inner.lock().clone()),
hash: self.hash.clone(),
encoded_size: self.encoded_size.clone(),
}
}
}
impl VerifiedTransaction {
/// Attempt to verify a transaction.
fn create(xt: UncheckedExtrinsic) -> Result<Self> {
if !xt.is_signed() {
bail!(ErrorKind::IsInherent(xt))
fn create(original: UncheckedExtrinsic) -> Result<Self> {
if !original.is_signed() {
bail!(ErrorKind::IsInherent(original))
}
const UNAVAILABLE_MESSAGE: &'static str = "chain state not available";
let (encoded_size, hash) = original.using_encoded(|e| (e.len(), BlakeTwo256::hash(e)));
let lookup = |a| match a {
RawAddress::Id(i) => Ok(i),
_ => Err(UNAVAILABLE_MESSAGE),
};
let inner = Mutex::new(match original.clone().check(lookup) {
Ok(xt) => Some(xt),
Err(e) if e == UNAVAILABLE_MESSAGE => None,
Err(e) => bail!(ErrorKind::BadSignature(e)),
});
Ok(VerifiedTransaction { original, inner, hash, encoded_size })
}
let message = Slicable::encode(&xt);
match xt.check() {
Ok(xt) => {
let hash = BlakeTwo256::hash(&message);
Ok(VerifiedTransaction {
inner: xt,
hash: hash.into(),
encoded_size: message.len(),
})
}
Err(xt) => Err(ErrorKind::BadSignature(xt).into()),
}
/// If this transaction isn't really verified, verify it and morph it into a really verified
/// transaction.
pub fn polish<F>(&self, lookup: F) -> Result<()> where
F: FnOnce(Address) -> result::Result<AccountId, &'static str> + Send + Sync
{
let inner: result::Result<CheckedExtrinsic, Error> = self.original
.clone()
.check(lookup)
.map_err(|e| ErrorKind::BadSignature(e).into());
*self.inner.lock() = Some(inner?);
Ok(())
}
/// Is this transaction *really* verified?
pub fn is_really_verified(&self) -> bool {
self.inner.lock().is_some()
}
/// Access the underlying transaction.
pub fn as_transaction(&self) -> &UncheckedExtrinsic {
self.as_ref().as_unchecked()
&self.original
}
/// Convert to primitive unchecked extrinsic.
@@ -93,8 +130,8 @@ impl VerifiedTransaction {
}
/// Consume the verified transaciton, yielding the unchecked counterpart.
pub fn into_inner(self) -> CheckedExtrinsic {
self.inner
pub fn into_inner(self) -> Result<CheckedExtrinsic> {
self.inner.lock().clone().ok_or_else(|| ErrorKind::NotReady.into())
}
/// Get the 256-bit hash of this transaction.
@@ -103,8 +140,13 @@ impl VerifiedTransaction {
}
/// Get the account ID of the sender of this transaction.
pub fn sender(&self) -> &AccountId {
&self.inner.signed
pub fn sender(&self) -> Result<AccountId> {
self.inner.lock().as_ref().map(|i| i.signed.clone()).ok_or_else(|| ErrorKind::NotReady.into())
}
/// Get the account ID of the sender of this transaction.
pub fn index(&self) -> Index {
self.original.extrinsic.index
}
/// Get encoded size of the transaction.
@@ -113,22 +155,16 @@ impl VerifiedTransaction {
}
}
impl AsRef<CheckedExtrinsic> for VerifiedTransaction {
fn as_ref(&self) -> &CheckedExtrinsic {
&self.inner
}
}
impl txpool::VerifiedTransaction for VerifiedTransaction {
type Hash = Hash;
type Sender = AccountId;
type Sender = Address;
fn hash(&self) -> &Self::Hash {
&self.hash
}
fn sender(&self) -> &Self::Sender {
&self.inner.signed
self.original.sender()
}
fn mem_usage(&self) -> usize {
@@ -145,7 +181,7 @@ impl txpool::Scoring<VerifiedTransaction> for Scoring {
type Event = ();
fn compare(&self, old: &VerifiedTransaction, other: &VerifiedTransaction) -> Ordering {
old.inner.index.cmp(&other.inner.index)
old.index().cmp(&other.index())
}
fn choose(&self, _old: &VerifiedTransaction, _new: &VerifiedTransaction) -> Choice {
@@ -173,17 +209,8 @@ impl txpool::Scoring<VerifiedTransaction> for Scoring {
pub struct Ready<'a, T: 'a + PolkadotApi> {
at_block: T::CheckedBlockId,
api: &'a T,
known_indices: HashMap<AccountId, ::primitives::Index>,
}
impl<'a, T: 'a + PolkadotApi> Clone for Ready<'a, T> {
fn clone(&self) -> Self {
Ready {
at_block: self.at_block.clone(),
api: self.api,
known_indices: self.known_indices.clone(),
}
}
known_nonces: HashMap<AccountId, (::primitives::Index, bool)>,
known_indexes: HashMap<AccountIndex, AccountId>,
}
impl<'a, T: 'a + PolkadotApi> Ready<'a, T> {
@@ -193,34 +220,85 @@ impl<'a, T: 'a + PolkadotApi> Ready<'a, T> {
Ready {
at_block: at,
api,
known_indices: HashMap::new(),
known_nonces: HashMap::new(),
known_indexes: HashMap::new(),
}
}
}
impl<'a, T: 'a + PolkadotApi> txpool::Ready<VerifiedTransaction> for Ready<'a, T> {
impl<'a, T: 'a + PolkadotApi> Clone for Ready<'a, T> {
fn clone(&self) -> Self {
Ready {
at_block: self.at_block.clone(),
api: self.api,
known_nonces: self.known_nonces.clone(),
known_indexes: self.known_indexes.clone(),
}
}
}
impl<'a, T: 'a + PolkadotApi> txpool::Ready<VerifiedTransaction> for Ready<'a, T>
{
fn is_ready(&mut self, xt: &VerifiedTransaction) -> Readiness {
let sender = xt.inner.signed;
if !xt.is_really_verified() {
let id = match xt.original.extrinsic.signed.clone() {
RawAddress::Id(id) => id.clone(), // should never happen, since we're not verified.
RawAddress::Index(i) => match self.known_indexes.entry(i) {
Entry::Occupied(e) => e.get().clone(),
Entry::Vacant(e) => {
let (api, at_block) = (&self.api, &self.at_block);
if let Some(id) = api.lookup(at_block, RawAddress::Index(i))
.ok()
.and_then(|o| o)
{
e.insert(id.clone());
id
} else {
// Invalid index.
// return stale in order to get the pool to throw it away.
return Readiness::Stale
}
}
},
};
if VerifiedTransaction::polish(xt, move |_| Ok(id)).is_err() {
// Invalid signature.
// return stale in order to get the pool to throw it away.
return Readiness::Stale
}
}
// guaranteed to be properly verified at this point.
let sender = xt.sender().expect("only way to get here is `is_really_verified` or successful `polish`; either guarantees `is_really_verified`; `sender` is `Ok` if `is_really_verified`; qed");
trace!(target: "transaction-pool", "Checking readiness of {} (from {})", xt.hash, Hash::from(sender));
let is_index_sender = match xt.original.extrinsic.signed { RawAddress::Index(_) => false, _ => true };
// TODO: find a way to handle index error properly -- will need changes to
// transaction-pool trait.
let (api, at_block) = (&self.api, &self.at_block);
let next_index = self.known_indices.entry(sender)
.or_insert_with(|| api.index(at_block, sender).ok().unwrap_or_else(Bounded::max_value));
let get_nonce = || api.index(at_block, sender).ok().unwrap_or_else(Bounded::max_value);
let (next_nonce, was_index_sender) = self.known_nonces.entry(sender).or_insert_with(|| (get_nonce(), is_index_sender));
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_nonce, xt.original.extrinsic.index);
let result = match xt.inner.index.cmp(&next_index) {
Ordering::Greater => Readiness::Future,
Ordering::Equal => Readiness::Ready,
Ordering::Less => Readiness::Stale,
};
// remember to increment `next_index`
*next_index = next_index.saturating_add(1);
result
if *was_index_sender == is_index_sender || get_nonce() == *next_nonce {
match xt.original.extrinsic.index.cmp(&next_nonce) {
Ordering::Greater => Readiness::Future,
Ordering::Less => Readiness::Stale,
Ordering::Equal => {
// remember to increment `next_nonce`
// TODO: this won't work perfectly since accounts can now be killed, returning the nonce
// to zero.
*next_nonce = next_nonce.saturating_add(1);
Readiness::Ready
}
}
} else {
// ignore for now.
Readiness::Future
}
}
}
@@ -251,8 +329,9 @@ impl TransactionPool {
}
}
// TODO: remove. This is pointless - just use `submit()` directly.
pub fn import_unchecked_extrinsic(&self, uxt: UncheckedExtrinsic) -> Result<Arc<VerifiedTransaction>> {
Ok(self.inner.import(VerifiedTransaction::create(uxt)?)?)
self.inner.submit(vec![uxt]).map(|mut v| v.swap_remove(0))
}
}
@@ -279,3 +358,246 @@ impl ExtrinsicPool<FutureProofUncheckedExtrinsic, Hash> for TransactionPool {
.collect()
}
}
#[cfg(test)]
mod tests {
use super::{TransactionPool, Ready};
use substrate_keyring::Keyring::{self, *};
use codec::Slicable;
use polkadot_api::{PolkadotApi, BlockBuilder, CheckedBlockId, Result};
use primitives::{AccountId, AccountIndex, Block, BlockId, Hash, Index, SessionKey, Timestamp,
UncheckedExtrinsic as FutureProofUncheckedExtrinsic};
use runtime::{RawAddress, Call, TimestampCall, BareExtrinsic, Extrinsic, UncheckedExtrinsic};
use primitives::parachain::{CandidateReceipt, DutyRoster, Id as ParaId};
use substrate_runtime_primitives::{MaybeUnsigned, generic};
struct TestBlockBuilder;
impl BlockBuilder for TestBlockBuilder {
fn push_extrinsic(&mut self, _extrinsic: FutureProofUncheckedExtrinsic) -> Result<()> { unimplemented!() }
fn bake(self) -> Result<Block> { unimplemented!() }
}
#[derive(Clone)]
struct TestCheckedBlockId(pub BlockId);
impl CheckedBlockId for TestCheckedBlockId {
fn block_id(&self) -> &BlockId { &self.0 }
}
fn number_of(at: &TestCheckedBlockId) -> u32 {
match at.0 {
generic::BlockId::Number(n) => n as u32,
_ => 0,
}
}
#[derive(Clone)]
struct TestPolkadotApi;
impl PolkadotApi for TestPolkadotApi {
type CheckedBlockId = TestCheckedBlockId;
type BlockBuilder = TestBlockBuilder;
fn check_id(&self, id: BlockId) -> Result<TestCheckedBlockId> { Ok(TestCheckedBlockId(id)) }
fn session_keys(&self, _at: &TestCheckedBlockId) -> Result<Vec<SessionKey>> { unimplemented!() }
fn validators(&self, _at: &TestCheckedBlockId) -> Result<Vec<AccountId>> { unimplemented!() }
fn random_seed(&self, _at: &TestCheckedBlockId) -> Result<Hash> { unimplemented!() }
fn duty_roster(&self, _at: &TestCheckedBlockId) -> Result<DutyRoster> { unimplemented!() }
fn timestamp(&self, _at: &TestCheckedBlockId) -> Result<u64> { unimplemented!() }
fn evaluate_block(&self, _at: &TestCheckedBlockId, _block: Block) -> Result<bool> { unimplemented!() }
fn active_parachains(&self, _at: &TestCheckedBlockId) -> Result<Vec<ParaId>> { unimplemented!() }
fn parachain_code(&self, _at: &TestCheckedBlockId, _parachain: ParaId) -> Result<Option<Vec<u8>>> { unimplemented!() }
fn parachain_head(&self, _at: &TestCheckedBlockId, _parachain: ParaId) -> Result<Option<Vec<u8>>> { unimplemented!() }
fn build_block(&self, _at: &TestCheckedBlockId, _timestamp: Timestamp, _new_heads: Vec<CandidateReceipt>) -> Result<Self::BlockBuilder> { unimplemented!() }
fn inherent_extrinsics(&self, _at: &TestCheckedBlockId, _timestamp: Timestamp, _new_heads: Vec<CandidateReceipt>) -> Result<Vec<Vec<u8>>> { unimplemented!() }
fn index(&self, _at: &TestCheckedBlockId, _account: AccountId) -> Result<Index> {
Ok((_account[0] as u32) + number_of(_at))
}
fn lookup(&self, _at: &TestCheckedBlockId, _address: RawAddress<AccountId, AccountIndex>) -> Result<Option<AccountId>> {
match _address {
RawAddress::Id(i) => Ok(Some(i)),
RawAddress::Index(i) => Ok(match (i < 8, i + (number_of(_at) as u64) % 8) {
(false, _) => None,
(_, 0) => Some(Alice.to_raw_public().into()),
(_, 1) => Some(Bob.to_raw_public().into()),
(_, 2) => Some(Charlie.to_raw_public().into()),
(_, 3) => Some(Dave.to_raw_public().into()),
(_, 4) => Some(Eve.to_raw_public().into()),
(_, 5) => Some(Ferdie.to_raw_public().into()),
(_, 6) => Some(One.to_raw_public().into()),
(_, 7) => Some(Two.to_raw_public().into()),
_ => None,
}),
}
}
}
fn uxt(who: Keyring, nonce: Index, use_id: bool) -> UncheckedExtrinsic {
let sxt = BareExtrinsic {
signed: who.to_raw_public().into(),
index: nonce,
function: Call::Timestamp(TimestampCall::set(0)),
};
let sig = sxt.using_encoded(|e| who.sign(e));
UncheckedExtrinsic::new(Extrinsic {
signed: if use_id { RawAddress::Id(sxt.signed) } else { RawAddress::Index(
match who {
Alice => 0,
Bob => 1,
Charlie => 2,
Dave => 3,
Eve => 4,
Ferdie => 5,
One => 6,
Two => 7,
}
)},
index: sxt.index,
function: sxt.function,
}, MaybeUnsigned(sig.into())).using_encoded(|e| UncheckedExtrinsic::decode(&mut &e[..])).unwrap()
}
#[test]
fn id_submission_should_work() {
let pool = TransactionPool::new(Default::default());
pool.submit(vec![uxt(Alice, 209, true)]).unwrap();
let ready = Ready::create(TestPolkadotApi.check_id(BlockId::number(0)).unwrap(), &TestPolkadotApi);
let pending: Vec<_> = pool.cull_and_get_pending(ready, |p| p.map(|a| (a.sender().ok(), a.index())).collect());
assert_eq!(pending, vec![(Some(Alice.to_raw_public().into()), 209)]);
}
#[test]
fn index_submission_should_work() {
let pool = TransactionPool::new(Default::default());
pool.submit(vec![uxt(Alice, 209, false)]).unwrap();
let ready = Ready::create(TestPolkadotApi.check_id(BlockId::number(0)).unwrap(), &TestPolkadotApi);
let pending: Vec<_> = pool.cull_and_get_pending(ready, |p| p.map(|a| (a.sender().ok(), a.index())).collect());
assert_eq!(pending, vec![(Some(Alice.to_raw_public().into()), 209)]);
}
#[test]
fn multiple_id_submission_should_work() {
let pool = TransactionPool::new(Default::default());
pool.submit(vec![uxt(Alice, 209, true)]).unwrap();
pool.submit(vec![uxt(Alice, 210, true)]).unwrap();
let ready = Ready::create(TestPolkadotApi.check_id(BlockId::number(0)).unwrap(), &TestPolkadotApi);
let pending: Vec<_> = pool.cull_and_get_pending(ready, |p| p.map(|a| (a.sender().ok(), a.index())).collect());
assert_eq!(pending, vec![(Some(Alice.to_raw_public().into()), 209), (Some(Alice.to_raw_public().into()), 210)]);
}
#[test]
fn multiple_index_submission_should_work() {
let pool = TransactionPool::new(Default::default());
pool.submit(vec![uxt(Alice, 209, false)]).unwrap();
pool.submit(vec![uxt(Alice, 210, false)]).unwrap();
let ready = Ready::create(TestPolkadotApi.check_id(BlockId::number(0)).unwrap(), &TestPolkadotApi);
let pending: Vec<_> = pool.cull_and_get_pending(ready, |p| p.map(|a| (a.sender().ok(), a.index())).collect());
assert_eq!(pending, vec![(Some(Alice.to_raw_public().into()), 209), (Some(Alice.to_raw_public().into()), 210)]);
}
#[test]
fn id_based_early_nonce_should_be_culled() {
let pool = TransactionPool::new(Default::default());
pool.submit(vec![uxt(Alice, 208, true)]).unwrap();
let ready = Ready::create(TestPolkadotApi.check_id(BlockId::number(0)).unwrap(), &TestPolkadotApi);
let pending: Vec<_> = pool.cull_and_get_pending(ready, |p| p.map(|a| (a.sender().ok(), a.index())).collect());
assert_eq!(pending, vec![]);
}
#[test]
fn index_based_early_nonce_should_be_culled() {
let pool = TransactionPool::new(Default::default());
pool.submit(vec![uxt(Alice, 208, false)]).unwrap();
let ready = Ready::create(TestPolkadotApi.check_id(BlockId::number(0)).unwrap(), &TestPolkadotApi);
let pending: Vec<_> = pool.cull_and_get_pending(ready, |p| p.map(|a| (a.sender().ok(), a.index())).collect());
assert_eq!(pending, vec![]);
}
#[test]
fn id_based_late_nonce_should_be_queued() {
let pool = TransactionPool::new(Default::default());
let ready = || Ready::create(TestPolkadotApi.check_id(BlockId::number(0)).unwrap(), &TestPolkadotApi);
pool.submit(vec![uxt(Alice, 210, true)]).unwrap();
let pending: Vec<_> = pool.cull_and_get_pending(ready(), |p| p.map(|a| (a.sender().ok(), a.index())).collect());
assert_eq!(pending, vec![]);
pool.submit(vec![uxt(Alice, 209, true)]).unwrap();
let pending: Vec<_> = pool.cull_and_get_pending(ready(), |p| p.map(|a| (a.sender().ok(), a.index())).collect());
assert_eq!(pending, vec![(Some(Alice.to_raw_public().into()), 209), (Some(Alice.to_raw_public().into()), 210)]);
}
#[test]
fn index_based_late_nonce_should_be_queued() {
let pool = TransactionPool::new(Default::default());
let ready = || Ready::create(TestPolkadotApi.check_id(BlockId::number(0)).unwrap(), &TestPolkadotApi);
pool.submit(vec![uxt(Alice, 210, false)]).unwrap();
let pending: Vec<_> = pool.cull_and_get_pending(ready(), |p| p.map(|a| (a.sender().ok(), a.index())).collect());
assert_eq!(pending, vec![]);
pool.submit(vec![uxt(Alice, 209, false)]).unwrap();
let pending: Vec<_> = pool.cull_and_get_pending(ready(), |p| p.map(|a| (a.sender().ok(), a.index())).collect());
assert_eq!(pending, vec![(Some(Alice.to_raw_public().into()), 209), (Some(Alice.to_raw_public().into()), 210)]);
}
#[test]
fn index_then_id_submission_should_make_progress() {
let pool = TransactionPool::new(Default::default());
pool.submit(vec![uxt(Alice, 209, false)]).unwrap();
pool.submit(vec![uxt(Alice, 210, true)]).unwrap();
let ready = Ready::create(TestPolkadotApi.check_id(BlockId::number(0)).unwrap(), &TestPolkadotApi);
let pending: Vec<_> = pool.cull_and_get_pending(ready, |p| p.map(|a| (a.sender().ok(), a.index())).collect());
assert_eq!(pending, vec![
(Some(Alice.to_raw_public().into()), 209)
]);
}
#[test]
fn id_then_index_submission_should_make_progress() {
let pool = TransactionPool::new(Default::default());
pool.submit(vec![uxt(Alice, 209, true)]).unwrap();
pool.submit(vec![uxt(Alice, 210, false)]).unwrap();
let ready = Ready::create(TestPolkadotApi.check_id(BlockId::number(0)).unwrap(), &TestPolkadotApi);
let pending: Vec<_> = pool.cull_and_get_pending(ready, |p| p.map(|a| (a.sender().ok(), a.index())).collect());
assert_eq!(pending, vec![
(Some(Alice.to_raw_public().into()), 209)
]);
}
#[test]
fn index_change_should_result_in_second_tx_culled_or_future() {
let pool = TransactionPool::new(Default::default());
pool.submit(vec![uxt(Alice, 209, false)]).unwrap();
pool.submit(vec![uxt(Alice, 210, false)]).unwrap();
let ready = Ready::create(TestPolkadotApi.check_id(BlockId::number(0)).unwrap(), &TestPolkadotApi);
let pending: Vec<_> = pool.cull_and_get_pending(ready, |p| p.map(|a| (a.sender().ok(), a.index())).collect());
assert_eq!(pending, vec![
(Some(Alice.to_raw_public().into()), 209),
(Some(Alice.to_raw_public().into()), 210)
]);
// first xt is mined, but that has a side-effect of switching index 0 from Alice to Bob.
// second xt now invalid signature, so it fails.
// there is no way of reporting this back to the queue right now (TODO). this should cause
// the queue to flush all information regarding the sender index/account.
// after this, a re-evaluation of the second's readiness should result in it being thrown
// out (or maybe placed in future queue).
/*
// TODO: uncomment once the new queue design is in.
let ready = Ready::create(TestPolkadotApi.check_id(BlockId::number(1)).unwrap(), &TestPolkadotApi);
let pending: Vec<_> = pool.cull_and_get_pending(ready, |p| p.map(|a| (a.sender().ok(), a.index())).collect());
assert_eq!(pending, vec![]);
*/
}
}