diff --git a/polkadot/api/Cargo.toml b/polkadot/api/Cargo.toml index 860536ac95..5a883cfd2c 100644 --- a/polkadot/api/Cargo.toml +++ b/polkadot/api/Cargo.toml @@ -11,6 +11,7 @@ polkadot-primitives = { path = "../primitives" } substrate-codec = { path = "../../substrate/codec" } substrate-runtime-io = { path = "../../substrate/runtime-io" } substrate-runtime-executive = { path = "../../substrate/runtime/executive" } +substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" } substrate-client = { path = "../../substrate/client" } substrate-primitives = { path = "../../substrate/primitives" } substrate-executor = { path = "../../substrate/executor" } diff --git a/polkadot/api/src/full.rs b/polkadot/api/src/full.rs index 101f59f5a7..a4203eac75 100644 --- a/polkadot/api/src/full.rs +++ b/polkadot/api/src/full.rs @@ -23,6 +23,8 @@ use polkadot_executor::Executor as LocalDispatch; use substrate_executor::{NativeExecutionDispatch, NativeExecutor}; use state_machine; +use runtime::Address; +use runtime_primitives::traits::AuxLookup; use primitives::{AccountId, Block, Header, BlockId, Hash, Index, SessionKey, Timestamp, UncheckedExtrinsic}; use primitives::parachain::{CandidateReceipt, DutyRoster, Id as ParaId}; @@ -135,7 +137,11 @@ impl> PolkadotApi for Client Result { - with_runtime!(self, at, || ::runtime::System::account_index(account)) + with_runtime!(self, at, || ::runtime::System::account_nonce(account)) + } + + fn lookup(&self, at: &Self::CheckedBlockId, address: Address) -> Result> { + with_runtime!(self, at, || <::runtime::Staking as AuxLookup>::lookup(address).ok()) } fn active_parachains(&self, at: &CheckedId) -> Result> { diff --git a/polkadot/api/src/lib.rs b/polkadot/api/src/lib.rs index c92f568f7b..81e3b02420 100644 --- a/polkadot/api/src/lib.rs +++ b/polkadot/api/src/lib.rs @@ -26,6 +26,7 @@ extern crate substrate_client as client; extern crate substrate_executor as substrate_executor; extern crate substrate_runtime_executive; extern crate substrate_primitives; +extern crate substrate_runtime_primitives as runtime_primitives; extern crate substrate_state_machine as state_machine; #[macro_use] @@ -37,7 +38,9 @@ extern crate substrate_keyring as keyring; pub mod full; pub mod light; -use primitives::{AccountId, Block, BlockId, Hash, Index, SessionKey, Timestamp, UncheckedExtrinsic}; +use primitives::{AccountId, Block, BlockId, Hash, Index, SessionKey, Timestamp, + UncheckedExtrinsic}; +use runtime::Address; use primitives::parachain::{CandidateReceipt, DutyRoster, Id as ParaId}; error_chain! { @@ -119,9 +122,12 @@ pub trait PolkadotApi { /// Get the timestamp registered at a block. fn timestamp(&self, at: &Self::CheckedBlockId) -> Result; - /// Get the index of an account at a block. + /// Get the nonce (né index) of an account at a block. fn index(&self, at: &Self::CheckedBlockId, account: AccountId) -> Result; + /// Get the account id of an address at a block. + fn lookup(&self, at: &Self::CheckedBlockId, address: Address) -> Result>; + /// Get the active parachains at a block. fn active_parachains(&self, at: &Self::CheckedBlockId) -> Result>; diff --git a/polkadot/api/src/light.rs b/polkadot/api/src/light.rs index 560c186699..6a4f1f0322 100644 --- a/polkadot/api/src/light.rs +++ b/polkadot/api/src/light.rs @@ -22,6 +22,7 @@ use client::{Client, CallExecutor}; use codec::Slicable; use state_machine; use primitives::{AccountId, Block, BlockId, Hash, Index, SessionKey, Timestamp, UncheckedExtrinsic}; +use runtime::Address; use primitives::parachain::{CandidateReceipt, DutyRoster, Id as ParaId}; use full::CheckedId; use {PolkadotApi, BlockBuilder, RemotePolkadotApi, CheckedBlockId, Result, ErrorKind}; @@ -84,6 +85,10 @@ impl, E: CallExecutor> PolkadotApi for RemotePolkadotAp Err(ErrorKind::UnknownRuntime.into()) } + fn lookup(&self, _at: &CheckedId, _address: Address) -> Result> { + Err(ErrorKind::UnknownRuntime.into()) + } + fn active_parachains(&self, _at: &Self::CheckedBlockId) -> Result> { Err(ErrorKind::UnknownRuntime.into()) } diff --git a/polkadot/consensus/src/lib.rs b/polkadot/consensus/src/lib.rs index 2e23b256b2..6146e6d87d 100644 --- a/polkadot/consensus/src/lib.rs +++ b/polkadot/consensus/src/lib.rs @@ -72,6 +72,7 @@ use runtime_support::Hashable; use polkadot_api::PolkadotApi; use polkadot_primitives::{Hash, Block, BlockId, BlockNumber, Header, Timestamp}; use polkadot_primitives::parachain::{Id as ParaId, Chain, DutyRoster, BlockData, Extrinsic as ParachainExtrinsic, CandidateReceipt}; +use polkadot_runtime::BareExtrinsic; use primitives::AuthorityId; use transaction_pool::{Ready, TransactionPool}; use tokio_core::reactor::{Handle, Timeout, Interval}; @@ -502,9 +503,9 @@ impl bft::Proposer for Proposer let mut next_index = { 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 - .filter(|tx| tx.as_ref().as_ref().signed == local_id) + .filter(|tx| tx.sender().map(|s| s == local_id).unwrap_or(false)) .last() - .map(|tx| Ok(tx.as_ref().as_ref().index)) + .map(|tx| Ok(tx.index())) .unwrap_or_else(|| self.client.index(&self.parent_id, local_id)) ); @@ -531,7 +532,7 @@ impl bft::Proposer for Proposer => MisbehaviorKind::BftDoubleCommit(round as u32, (h1, s1.signature), (h2, s2.signature)), } }; - let extrinsic = Extrinsic { + let extrinsic = BareExtrinsic { signed: local_id, index: next_index, function: Call::Consensus(ConsensusCall::report_misbehavior(report)), @@ -540,7 +541,13 @@ impl bft::Proposer for Proposer next_index += 1; let signature = MaybeUnsigned(self.local_key.sign(&extrinsic.encode()).into()); - let uxt = UncheckedExtrinsic { extrinsic, signature }; + + let extrinsic = Extrinsic { + signed: extrinsic.signed.into(), + index: extrinsic.index, + function: extrinsic.function, + }; + let uxt = UncheckedExtrinsic::new(extrinsic, signature); self.transaction_pool.import_unchecked_extrinsic(uxt) .expect("locally signed extrinsic is valid; qed"); diff --git a/polkadot/parachain/test-chains/basic_add/src/lib.rs b/polkadot/parachain/test-chains/basic_add/src/lib.rs index 1ef898f1dc..9d0a8a0d74 100644 --- a/polkadot/parachain/test-chains/basic_add/src/lib.rs +++ b/polkadot/parachain/test-chains/basic_add/src/lib.rs @@ -17,7 +17,7 @@ //! Basic parachain that adds a number as part of its state. #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(alloc, core_intrinsics, lang_items, panic_implementation))] +#![cfg_attr(not(feature = "std"), feature(alloc, core_intrinsics, global_allocator, lang_items, panic_implementation, core_panic_info))] #[cfg(not(feature = "std"))] extern crate alloc; diff --git a/polkadot/parachain/test-chains/basic_add/src/wasm.rs b/polkadot/parachain/test-chains/basic_add/src/wasm.rs index 3af2dd39a9..edf53dc46f 100644 --- a/polkadot/parachain/test-chains/basic_add/src/wasm.rs +++ b/polkadot/parachain/test-chains/basic_add/src/wasm.rs @@ -16,15 +16,14 @@ //! Defines WASM module logic. -use core::intrinsics; - +use core::{intrinsics, panic}; use parachain::{self, ValidationResult}; use parachain::codec::Slicable; use super::{HeadData, BlockData}; #[panic_implementation] #[no_mangle] -pub fn rust_begin_panic(_info: &::core::panic::PanicInfo) -> ! { +pub fn panic(_info: &panic::PanicInfo) -> ! { unsafe { intrinsics::abort() } diff --git a/polkadot/parachain/tests/res/basic_add.wasm b/polkadot/parachain/tests/res/basic_add.wasm index 709c111d27..c32910f0ad 100755 Binary files a/polkadot/parachain/tests/res/basic_add.wasm and b/polkadot/parachain/tests/res/basic_add.wasm differ diff --git a/polkadot/primitives/src/lib.rs b/polkadot/primitives/src/lib.rs index 4deded2969..f7861a8337 100644 --- a/polkadot/primitives/src/lib.rs +++ b/polkadot/primitives/src/lib.rs @@ -67,6 +67,10 @@ pub type BlockNumber = u64; /// Alias to Ed25519 pubkey that identifies an account on the relay chain. pub type AccountId = primitives::hash::H256; +/// The type for looking up accounts. We don't expect more than 4 billion of them, but you +/// never know... +pub type AccountIndex = u64; + /// The Ed25519 pub key of an session that belongs to an authority of the relay chain. This is /// exactly equivalent to what the substrate calls an "authority". pub type SessionKey = primitives::AuthorityId; diff --git a/polkadot/runtime/src/checked_block.rs b/polkadot/runtime/src/checked_block.rs new file mode 100644 index 0000000000..4ea7bd0e88 --- /dev/null +++ b/polkadot/runtime/src/checked_block.rs @@ -0,0 +1,108 @@ +// Copyright 2017 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 . + +//! Typesafe block interaction. + +use super::{Call, Block, TIMESTAMP_SET_POSITION, PARACHAINS_SET_POSITION}; +use timestamp::Call as TimestampCall; +use parachains::Call as ParachainsCall; +use primitives::parachain::CandidateReceipt; + +/// Provides a type-safe wrapper around a structurally valid block. +pub struct CheckedBlock { + inner: Block, + file_line: Option<(&'static str, u32)>, +} + +impl CheckedBlock { + /// Create a new checked block. Fails if the block is not structurally valid. + pub fn new(block: Block) -> Result { + let has_timestamp = block.extrinsics.get(TIMESTAMP_SET_POSITION as usize).map_or(false, |xt| { + !xt.is_signed() && match xt.extrinsic.function { + Call::Timestamp(TimestampCall::set(_)) => true, + _ => false, + } + }); + + if !has_timestamp { return Err(block) } + + let has_heads = block.extrinsics.get(PARACHAINS_SET_POSITION as usize).map_or(false, |xt| { + !xt.is_signed() && match xt.extrinsic.function { + Call::Parachains(ParachainsCall::set_heads(_)) => true, + _ => false, + } + }); + + if !has_heads { return Err(block) } + Ok(CheckedBlock { + inner: block, + file_line: None, + }) + } + + // Creates a new checked block, asserting that it is valid. + #[doc(hidden)] + pub fn new_unchecked(block: Block, file: &'static str, line: u32) -> Self { + CheckedBlock { + inner: block, + file_line: Some((file, line)), + } + } + + /// Extract the timestamp from the block. + pub fn timestamp(&self) -> ::primitives::Timestamp { + let x = self.inner.extrinsics.get(TIMESTAMP_SET_POSITION as usize).and_then(|xt| match xt.extrinsic.function { + Call::Timestamp(TimestampCall::set(x)) => Some(x), + _ => None + }); + + match x { + Some(x) => x, + None => panic!("Invalid polkadot block asserted at {:?}", self.file_line), + } + } + + /// Extract the parachain heads from the block. + pub fn parachain_heads(&self) -> &[CandidateReceipt] { + let x = self.inner.extrinsics.get(PARACHAINS_SET_POSITION as usize).and_then(|xt| match xt.extrinsic.function { + Call::Parachains(ParachainsCall::set_heads(ref x)) => Some(&x[..]), + _ => None + }); + + match x { + Some(x) => x, + None => panic!("Invalid polkadot block asserted at {:?}", self.file_line), + } + } + + /// Convert into inner block. + pub fn into_inner(self) -> Block { self.inner } +} + +impl ::std::ops::Deref for CheckedBlock { + type Target = Block; + + fn deref(&self) -> &Block { &self.inner } +} + +/// Assert that a block is structurally valid. May lead to panic in the future +/// in case it isn't. +#[macro_export] +macro_rules! assert_polkadot_block { + ($block: expr) => { + $crate::CheckedBlock::new_unchecked($block, file!(), line!()) + } +} diff --git a/polkadot/runtime/src/lib.rs b/polkadot/runtime/src/lib.rs index 16d080cb9e..9d9b3f9a59 100644 --- a/polkadot/runtime/src/lib.rs +++ b/polkadot/runtime/src/lib.rs @@ -58,11 +58,17 @@ extern crate substrate_runtime_staking as staking; extern crate substrate_runtime_system as system; extern crate substrate_runtime_timestamp as timestamp; +#[cfg(feature = "std")] +mod checked_block; mod parachains; +mod utils; -use rstd::prelude::*; -use primitives::{AccountId, Balance, BlockNumber, Hash, Index, Log, SessionKey, Signature}; -use primitives::parachain::CandidateReceipt; +#[cfg(feature = "std")] +pub use checked_block::CheckedBlock; +pub use utils::{inherent_extrinsics, check_extrinsic}; +pub use staking::address::Address as RawAddress; + +use primitives::{AccountId, AccountIndex, Balance, BlockNumber, Hash, Index, Log, SessionKey, Signature}; use runtime_primitives::{generic, traits::{HasPublicAux, BlakeTwo256, Convert}}; #[cfg(feature = "std")] @@ -78,107 +84,23 @@ pub const TIMESTAMP_SET_POSITION: u32 = 0; /// The position of the parachains set extrinsic. pub const PARACHAINS_SET_POSITION: u32 = 1; +/// The address format for describing accounts. +pub type Address = staking::Address; /// Block Id type for this block. pub type BlockId = generic::BlockId; /// Unchecked extrinsic type as expected by this runtime. -pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; -/// Extrinsic type as expected by this runtime. -pub type Extrinsic = generic::Extrinsic; - +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +/// Extrinsic type as expected by this runtime. This is not the type that is signed. +pub type Extrinsic = generic::Extrinsic; +/// Extrinsic type that is signed. +pub type BareExtrinsic = generic::Extrinsic; /// Block type as expected by this runtime. pub type Block = generic::Block; -/// Provides a type-safe wrapper around a structurally valid block. -#[cfg(feature = "std")] -pub struct CheckedBlock { - inner: Block, - file_line: Option<(&'static str, u32)>, -} - -#[cfg(feature = "std")] -impl CheckedBlock { - /// Create a new checked block. Fails if the block is not structurally valid. - pub fn new(block: Block) -> Result { - let has_timestamp = block.extrinsics.get(TIMESTAMP_SET_POSITION as usize).map_or(false, |xt| { - !xt.is_signed() && match xt.extrinsic.function { - Call::Timestamp(TimestampCall::set(_)) => true, - _ => false, - } - }); - - if !has_timestamp { return Err(block) } - - let has_heads = block.extrinsics.get(PARACHAINS_SET_POSITION as usize).map_or(false, |xt| { - !xt.is_signed() && match xt.extrinsic.function { - Call::Parachains(ParachainsCall::set_heads(_)) => true, - _ => false, - } - }); - - if !has_heads { return Err(block) } - Ok(CheckedBlock { - inner: block, - file_line: None, - }) - } - - // Creates a new checked block, asserting that it is valid. - #[doc(hidden)] - pub fn new_unchecked(block: Block, file: &'static str, line: u32) -> Self { - CheckedBlock { - inner: block, - file_line: Some((file, line)), - } - } - - /// Extract the timestamp from the block. - pub fn timestamp(&self) -> ::primitives::Timestamp { - let x = self.inner.extrinsics.get(TIMESTAMP_SET_POSITION as usize).and_then(|xt| match xt.extrinsic.function { - Call::Timestamp(TimestampCall::set(x)) => Some(x), - _ => None - }); - - match x { - Some(x) => x, - None => panic!("Invalid polkadot block asserted at {:?}", self.file_line), - } - } - - /// Extract the parachain heads from the block. - pub fn parachain_heads(&self) -> &[CandidateReceipt] { - let x = self.inner.extrinsics.get(PARACHAINS_SET_POSITION as usize).and_then(|xt| match xt.extrinsic.function { - Call::Parachains(ParachainsCall::set_heads(ref x)) => Some(&x[..]), - _ => None - }); - - match x { - Some(x) => x, - None => panic!("Invalid polkadot block asserted at {:?}", self.file_line), - } - } - - /// Convert into inner block. - pub fn into_inner(self) -> Block { self.inner } -} - -#[cfg(feature = "std")] -impl ::std::ops::Deref for CheckedBlock { - type Target = Block; - - fn deref(&self) -> &Block { &self.inner } -} - -/// Assert that a block is structurally valid. May lead to panic in the future -/// in case it isn't. -#[cfg(feature = "std")] -#[macro_export] -macro_rules! assert_polkadot_block { - ($block: expr) => { - $crate::CheckedBlock::new_unchecked($block, file!(), line!()) - } -} - /// Concrete runtime type used to parameterize the various modules. +// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. +#[derive(Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] pub struct Concrete; impl HasPublicAux for Concrete { @@ -228,6 +150,7 @@ pub type Session = session::Module; impl staking::Trait for Concrete { type Balance = Balance; type DetermineContractAddress = BlakeTwo256; + type AccountIndex = AccountIndex; } /// Staking module for this concrete runtime. pub type Staking = staking::Module; @@ -280,7 +203,7 @@ impl_outer_dispatch! { } /// Executive: handles dispatch to the various modules. -pub type Executive = executive::Executive; impl_outer_config! { @@ -295,35 +218,6 @@ impl_outer_config! { } } -/// Produces the list of inherent extrinsics. -pub fn inherent_extrinsics(timestamp: ::primitives::Timestamp, parachain_heads: Vec) -> Vec { - vec![ - UncheckedExtrinsic { - extrinsic: Extrinsic { - signed: Default::default(), - function: Call::Timestamp(TimestampCall::set(timestamp)), - index: 0, - }, - signature: Default::default(), - }, - UncheckedExtrinsic { - extrinsic: Extrinsic { - signed: Default::default(), - function: Call::Parachains(ParachainsCall::set_heads(parachain_heads)), - index: 0, - }, - signature: Default::default(), - }, - ] -} - -/// Checks an unchecked extrinsic for validity. -pub fn check_extrinsic(xt: UncheckedExtrinsic) -> bool { - use runtime_primitives::traits::Checkable; - - xt.check().is_ok() -} - pub mod api { impl_stubs!( authorities => |()| super::Consensus::authorities(), @@ -378,14 +272,14 @@ mod tests { let mut block = Block { header: Header::new(1, Default::default(), Default::default(), Default::default(), Default::default()), extrinsics: vec![ - UncheckedExtrinsic { - extrinsic: Extrinsic { + UncheckedExtrinsic::new( + generic::Extrinsic { function: Call::Timestamp(timestamp::Call::set(100_000_000)), signed: Default::default(), index: Default::default(), }, - signature: Default::default(), - } + Default::default(), + ) ], }; @@ -394,14 +288,14 @@ mod tests { assert_eq!(block, decoded); - block.extrinsics.push(UncheckedExtrinsic { - extrinsic: Extrinsic { + block.extrinsics.push(UncheckedExtrinsic::new( + generic::Extrinsic { function: Call::Staking(staking::Call::stake()), signed: Default::default(), index: 10101, }, - signature: Default::default(), - }); + Default::default(), + )); let raw = block.encode(); let decoded = Block::decode(&mut &raw[..]).unwrap(); @@ -414,25 +308,25 @@ mod tests { let mut block = Block { header: Header::new(1, Default::default(), Default::default(), Default::default(), Default::default()), extrinsics: vec![ - UncheckedExtrinsic { - extrinsic: Extrinsic { + UncheckedExtrinsic::new( + generic::Extrinsic { function: Call::Timestamp(timestamp::Call::set(100_000_000)), signed: Default::default(), index: Default::default(), }, - signature: Default::default(), - } + Default::default(), + ) ], }; - block.extrinsics.push(UncheckedExtrinsic { - extrinsic: Extrinsic { + block.extrinsics.push(UncheckedExtrinsic::new( + generic::Extrinsic { function: Call::Staking(staking::Call::stake()), signed: Default::default(), index: 10101, }, - signature: Default::default(), - }); + Default::default() + )); let raw = block.encode(); let decoded_primitive = ::primitives::Block::decode(&mut &raw[..]).unwrap(); @@ -444,22 +338,24 @@ mod tests { #[test] fn serialize_unchecked() { - let tx = UncheckedExtrinsic { - extrinsic: Extrinsic { - signed: [1; 32].into(), + let tx = UncheckedExtrinsic::new( + Extrinsic { + signed: AccountId::from([1; 32]).into(), index: 999, function: Call::Timestamp(TimestampCall::set(135135)), }, - signature: runtime_primitives::Ed25519Signature(primitives::hash::H512([0; 64])).into(), - }; - // 71000000 - // 0101010101010101010101010101010101010101010101010101010101010101 - // e703000000000000 - // 00 + runtime_primitives::Ed25519Signature(primitives::hash::H512([0; 64])).into() + ); + + // 6f000000 + // ff0101010101010101010101010101010101010101010101010101010101010101 + // e7030000 + // 0300 // df0f0200 // 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 let v = Slicable::encode(&tx); + assert_eq!(&v[..], &hex!["6f000000ff0101010101010101010101010101010101010101010101010101010101010101e70300000300df0f02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"][..]); println!("{}", HexDisplay::from(&v)); assert_eq!(UncheckedExtrinsic::decode(&mut &v[..]).unwrap(), tx); } @@ -467,7 +363,7 @@ mod tests { #[test] fn serialize_checked() { let xt = Extrinsic { - signed: hex!["0d71d1a9cad6f2ab773435a7dec1bac019994d05d1dd5eb3108211dcf25c9d1e"].into(), + signed: AccountId::from(hex!["0d71d1a9cad6f2ab773435a7dec1bac019994d05d1dd5eb3108211dcf25c9d1e"]).into(), index: 0, function: Call::CouncilVoting(council::voting::Call::propose(Box::new( PrivCall::Consensus(consensus::PrivCall::set_code( @@ -476,11 +372,6 @@ mod tests { ))), }; let v = Slicable::encode(&xt); - - let data = hex!["e00000000d71d1a9cad6f2ab773435a7dec1bac019994d05d1dd5eb3108211dcf25c9d1e0000000007000000000000006369D39D892B7B87A6769F90E14C618C2B84EBB293E2CC46640136E112C078C75619AC2E0815F2511568736623C055156C8FC427CE2AEE4AE2838F86EFE80208"]; - let uxt: UncheckedExtrinsic = Slicable::decode(&mut &data[..]).unwrap(); - assert_eq!(uxt.extrinsic, xt); - assert_eq!(Extrinsic::decode(&mut &v[..]).unwrap(), xt); } } diff --git a/polkadot/runtime/src/parachains.rs b/polkadot/runtime/src/parachains.rs index 5bd62afcf2..9bf9a2d181 100644 --- a/polkadot/runtime/src/parachains.rs +++ b/polkadot/runtime/src/parachains.rs @@ -234,6 +234,7 @@ mod tests { use runtime_primitives::testing::{Digest, Header}; use consensus; + #[derive(Clone, Eq, PartialEq)] pub struct Test; impl HasPublicAux for Test { type PublicAux = u64; diff --git a/polkadot/runtime/src/utils.rs b/polkadot/runtime/src/utils.rs new file mode 100644 index 0000000000..4c16e215ba --- /dev/null +++ b/polkadot/runtime/src/utils.rs @@ -0,0 +1,51 @@ +// Copyright 2017 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 . + +//! Utils for block interaction. + +use rstd::prelude::*; +use super::{Call, UncheckedExtrinsic, Extrinsic, Staking}; +use runtime_primitives::traits::{Checkable, AuxLookup}; +use primitives::parachain::CandidateReceipt; +use timestamp::Call as TimestampCall; +use parachains::Call as ParachainsCall; + +/// Produces the list of inherent extrinsics. +pub fn inherent_extrinsics(timestamp: ::primitives::Timestamp, parachain_heads: Vec) -> Vec { + vec![ + UncheckedExtrinsic::new( + Extrinsic { + signed: Default::default(), + function: Call::Timestamp(TimestampCall::set(timestamp)), + index: 0, + }, + Default::default() + ), + UncheckedExtrinsic::new( + Extrinsic { + signed: Default::default(), + function: Call::Parachains(ParachainsCall::set_heads(parachain_heads)), + index: 0, + }, + Default::default() + ) + ] +} + +/// Checks an unchecked extrinsic for validity. +pub fn check_extrinsic(xt: UncheckedExtrinsic) -> bool { + xt.check(Staking::lookup).is_ok() +} diff --git a/polkadot/runtime/wasm/Cargo.lock b/polkadot/runtime/wasm/Cargo.lock index 40e3acb37a..aac3375c7a 100644 --- a/polkadot/runtime/wasm/Cargo.lock +++ b/polkadot/runtime/wasm/Cargo.lock @@ -543,6 +543,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "substrate-codec" version = "0.1.0" +dependencies = [ + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "substrate-keyring" @@ -647,6 +650,7 @@ version = "0.1.0" dependencies = [ "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-codec 0.1.0", "substrate-runtime-io 0.1.0", "substrate-runtime-primitives 0.1.0", @@ -767,6 +771,7 @@ dependencies = [ "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "safe-mix 0.1.0", "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-codec 0.1.0", "substrate-primitives 0.1.0", "substrate-runtime-io 0.1.0", diff --git a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm index 9ea43b2f9a..4e37cbbab1 100644 Binary files a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm and b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm differ diff --git a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm index dc22849a14..2d84f17d87 100755 Binary files a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm and b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm differ diff --git a/polkadot/service/src/lib.rs b/polkadot/service/src/lib.rs index 1e86d52112..a22e62d342 100644 --- a/polkadot/service/src/lib.rs +++ b/polkadot/service/src/lib.rs @@ -177,6 +177,11 @@ fn poc_2_testnet_config() -> ChainConfig { intentions: initial_authorities.iter().cloned().map(Into::into).collect(), transaction_base_fee: 100, transaction_byte_fee: 1, + existential_deposit: 500, + transfer_fee: 0, + creation_fee: 0, + contract_fee: 0, + reclaim_rebate: 0, balances: endowed_accounts.iter().map(|&k|(k, 1u128 << 60)).collect(), validator_count: 12, sessions_per_era: 24, // 24 hours per era. @@ -236,6 +241,11 @@ fn testnet_config(initial_authorities: Vec) -> ChainConfig { intentions: initial_authorities.iter().cloned().map(Into::into).collect(), transaction_base_fee: 1, transaction_byte_fee: 0, + existential_deposit: 500, + transfer_fee: 0, + creation_fee: 0, + contract_fee: 0, + reclaim_rebate: 0, balances: endowed_accounts.iter().map(|&k|(k, (1u128 << 60))).collect(), validator_count: 2, sessions_per_era: 5, diff --git a/polkadot/transaction-pool/Cargo.toml b/polkadot/transaction-pool/Cargo.toml index 140bea88e6..89fa3e88c0 100644 --- a/polkadot/transaction-pool/Cargo.toml +++ b/polkadot/transaction-pool/Cargo.toml @@ -6,11 +6,13 @@ authors = ["Parity Technologies "] [dependencies] log = "0.3.0" error-chain = "0.11" +parking_lot = "0.4" polkadot-api = { path = "../api" } polkadot-primitives = { path = "../primitives" } polkadot-runtime = { path = "../runtime" } substrate-client = { path = "../../substrate/client" } substrate-codec = { path = "../../substrate/codec" } +substrate-keyring = { path = "../../substrate/keyring" } substrate-extrinsic-pool = { path = "../../substrate/extrinsic-pool" } substrate-primitives = { path = "../../substrate/primitives" } substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" } diff --git a/polkadot/transaction-pool/src/error.rs b/polkadot/transaction-pool/src/error.rs index 18b79e59fe..78f9d3e771 100644 --- a/polkadot/transaction-pool/src/error.rs +++ b/polkadot/transaction-pool/src/error.rs @@ -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"), + } } } diff --git a/polkadot/transaction-pool/src/lib.rs b/polkadot/transaction-pool/src/lib.rs index 2c2224b5d3..843af20fb1 100644 --- a/polkadot/transaction-pool/src/lib.rs +++ b/polkadot/transaction-pool/src/lib.rs @@ -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 = ::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>, 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 { - if !xt.is_signed() { - bail!(ErrorKind::IsInherent(xt)) + fn create(original: UncheckedExtrinsic) -> Result { + 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(&self, lookup: F) -> Result<()> where + F: FnOnce(Address) -> result::Result + Send + Sync + { + let inner: result::Result = 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 { + 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 { + 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 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 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 for Scoring { pub struct Ready<'a, T: 'a + PolkadotApi> { at_block: T::CheckedBlockId, api: &'a T, - known_indices: HashMap, -} - -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, + known_indexes: HashMap, } 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 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 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> { - 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 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 { 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 { Ok(TestCheckedBlockId(id)) } + fn session_keys(&self, _at: &TestCheckedBlockId) -> Result> { unimplemented!() } + fn validators(&self, _at: &TestCheckedBlockId) -> Result> { unimplemented!() } + fn random_seed(&self, _at: &TestCheckedBlockId) -> Result { unimplemented!() } + fn duty_roster(&self, _at: &TestCheckedBlockId) -> Result { unimplemented!() } + fn timestamp(&self, _at: &TestCheckedBlockId) -> Result { unimplemented!() } + fn evaluate_block(&self, _at: &TestCheckedBlockId, _block: Block) -> Result { unimplemented!() } + fn active_parachains(&self, _at: &TestCheckedBlockId) -> Result> { unimplemented!() } + fn parachain_code(&self, _at: &TestCheckedBlockId, _parachain: ParaId) -> Result>> { unimplemented!() } + fn parachain_head(&self, _at: &TestCheckedBlockId, _parachain: ParaId) -> Result>> { unimplemented!() } + fn build_block(&self, _at: &TestCheckedBlockId, _timestamp: Timestamp, _new_heads: Vec) -> Result { unimplemented!() } + fn inherent_extrinsics(&self, _at: &TestCheckedBlockId, _timestamp: Timestamp, _new_heads: Vec) -> Result>> { unimplemented!() } + + fn index(&self, _at: &TestCheckedBlockId, _account: AccountId) -> Result { + Ok((_account[0] as u32) + number_of(_at)) + } + fn lookup(&self, _at: &TestCheckedBlockId, _address: RawAddress) -> Result> { + 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![]); +*/ + } +}