mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-23 23:35:44 +00:00
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:
@@ -11,6 +11,7 @@ polkadot-primitives = { path = "../primitives" }
|
|||||||
substrate-codec = { path = "../../substrate/codec" }
|
substrate-codec = { path = "../../substrate/codec" }
|
||||||
substrate-runtime-io = { path = "../../substrate/runtime-io" }
|
substrate-runtime-io = { path = "../../substrate/runtime-io" }
|
||||||
substrate-runtime-executive = { path = "../../substrate/runtime/executive" }
|
substrate-runtime-executive = { path = "../../substrate/runtime/executive" }
|
||||||
|
substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" }
|
||||||
substrate-client = { path = "../../substrate/client" }
|
substrate-client = { path = "../../substrate/client" }
|
||||||
substrate-primitives = { path = "../../substrate/primitives" }
|
substrate-primitives = { path = "../../substrate/primitives" }
|
||||||
substrate-executor = { path = "../../substrate/executor" }
|
substrate-executor = { path = "../../substrate/executor" }
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ use polkadot_executor::Executor as LocalDispatch;
|
|||||||
use substrate_executor::{NativeExecutionDispatch, NativeExecutor};
|
use substrate_executor::{NativeExecutionDispatch, NativeExecutor};
|
||||||
use state_machine;
|
use state_machine;
|
||||||
|
|
||||||
|
use runtime::Address;
|
||||||
|
use runtime_primitives::traits::AuxLookup;
|
||||||
use primitives::{AccountId, Block, Header, BlockId, Hash, Index, SessionKey, Timestamp, UncheckedExtrinsic};
|
use primitives::{AccountId, Block, Header, BlockId, Hash, Index, SessionKey, Timestamp, UncheckedExtrinsic};
|
||||||
use primitives::parachain::{CandidateReceipt, DutyRoster, Id as ParaId};
|
use primitives::parachain::{CandidateReceipt, DutyRoster, Id as ParaId};
|
||||||
|
|
||||||
@@ -135,7 +137,11 @@ impl<B: LocalBackend<Block>> PolkadotApi for Client<B, LocalCallExecutor<B, Nati
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn index(&self, at: &CheckedId, account: AccountId) -> Result<Index> {
|
fn index(&self, at: &CheckedId, account: AccountId) -> Result<Index> {
|
||||||
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<Option<AccountId>> {
|
||||||
|
with_runtime!(self, at, || <::runtime::Staking as AuxLookup>::lookup(address).ok())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn active_parachains(&self, at: &CheckedId) -> Result<Vec<ParaId>> {
|
fn active_parachains(&self, at: &CheckedId) -> Result<Vec<ParaId>> {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ extern crate substrate_client as client;
|
|||||||
extern crate substrate_executor as substrate_executor;
|
extern crate substrate_executor as substrate_executor;
|
||||||
extern crate substrate_runtime_executive;
|
extern crate substrate_runtime_executive;
|
||||||
extern crate substrate_primitives;
|
extern crate substrate_primitives;
|
||||||
|
extern crate substrate_runtime_primitives as runtime_primitives;
|
||||||
extern crate substrate_state_machine as state_machine;
|
extern crate substrate_state_machine as state_machine;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
@@ -37,7 +38,9 @@ extern crate substrate_keyring as keyring;
|
|||||||
pub mod full;
|
pub mod full;
|
||||||
pub mod light;
|
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};
|
use primitives::parachain::{CandidateReceipt, DutyRoster, Id as ParaId};
|
||||||
|
|
||||||
error_chain! {
|
error_chain! {
|
||||||
@@ -119,9 +122,12 @@ pub trait PolkadotApi {
|
|||||||
/// Get the timestamp registered at a block.
|
/// Get the timestamp registered at a block.
|
||||||
fn timestamp(&self, at: &Self::CheckedBlockId) -> Result<Timestamp>;
|
fn timestamp(&self, at: &Self::CheckedBlockId) -> Result<Timestamp>;
|
||||||
|
|
||||||
/// 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<Index>;
|
fn index(&self, at: &Self::CheckedBlockId, account: AccountId) -> Result<Index>;
|
||||||
|
|
||||||
|
/// Get the account id of an address at a block.
|
||||||
|
fn lookup(&self, at: &Self::CheckedBlockId, address: Address) -> Result<Option<AccountId>>;
|
||||||
|
|
||||||
/// Get the active parachains at a block.
|
/// Get the active parachains at a block.
|
||||||
fn active_parachains(&self, at: &Self::CheckedBlockId) -> Result<Vec<ParaId>>;
|
fn active_parachains(&self, at: &Self::CheckedBlockId) -> Result<Vec<ParaId>>;
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ use client::{Client, CallExecutor};
|
|||||||
use codec::Slicable;
|
use codec::Slicable;
|
||||||
use state_machine;
|
use state_machine;
|
||||||
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};
|
use primitives::parachain::{CandidateReceipt, DutyRoster, Id as ParaId};
|
||||||
use full::CheckedId;
|
use full::CheckedId;
|
||||||
use {PolkadotApi, BlockBuilder, RemotePolkadotApi, CheckedBlockId, Result, ErrorKind};
|
use {PolkadotApi, BlockBuilder, RemotePolkadotApi, CheckedBlockId, Result, ErrorKind};
|
||||||
@@ -84,6 +85,10 @@ impl<B: Backend<Block>, E: CallExecutor<Block>> PolkadotApi for RemotePolkadotAp
|
|||||||
Err(ErrorKind::UnknownRuntime.into())
|
Err(ErrorKind::UnknownRuntime.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lookup(&self, _at: &CheckedId, _address: Address) -> Result<Option<AccountId>> {
|
||||||
|
Err(ErrorKind::UnknownRuntime.into())
|
||||||
|
}
|
||||||
|
|
||||||
fn active_parachains(&self, _at: &Self::CheckedBlockId) -> Result<Vec<ParaId>> {
|
fn active_parachains(&self, _at: &Self::CheckedBlockId) -> Result<Vec<ParaId>> {
|
||||||
Err(ErrorKind::UnknownRuntime.into())
|
Err(ErrorKind::UnknownRuntime.into())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ use runtime_support::Hashable;
|
|||||||
use polkadot_api::PolkadotApi;
|
use polkadot_api::PolkadotApi;
|
||||||
use polkadot_primitives::{Hash, Block, BlockId, BlockNumber, Header, Timestamp};
|
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_primitives::parachain::{Id as ParaId, Chain, DutyRoster, BlockData, Extrinsic as ParachainExtrinsic, CandidateReceipt};
|
||||||
|
use polkadot_runtime::BareExtrinsic;
|
||||||
use primitives::AuthorityId;
|
use primitives::AuthorityId;
|
||||||
use transaction_pool::{Ready, TransactionPool};
|
use transaction_pool::{Ready, TransactionPool};
|
||||||
use tokio_core::reactor::{Handle, Timeout, Interval};
|
use tokio_core::reactor::{Handle, Timeout, Interval};
|
||||||
@@ -502,9 +503,9 @@ impl<C, R, P> bft::Proposer<Block> for Proposer<C, R, P>
|
|||||||
let mut next_index = {
|
let mut next_index = {
|
||||||
let readiness_evaluator = Ready::create(self.parent_id.clone(), &*self.client);
|
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
|
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()
|
.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))
|
.unwrap_or_else(|| self.client.index(&self.parent_id, local_id))
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -531,7 +532,7 @@ impl<C, R, P> bft::Proposer<Block> for Proposer<C, R, P>
|
|||||||
=> MisbehaviorKind::BftDoubleCommit(round as u32, (h1, s1.signature), (h2, s2.signature)),
|
=> MisbehaviorKind::BftDoubleCommit(round as u32, (h1, s1.signature), (h2, s2.signature)),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let extrinsic = Extrinsic {
|
let extrinsic = BareExtrinsic {
|
||||||
signed: local_id,
|
signed: local_id,
|
||||||
index: next_index,
|
index: next_index,
|
||||||
function: Call::Consensus(ConsensusCall::report_misbehavior(report)),
|
function: Call::Consensus(ConsensusCall::report_misbehavior(report)),
|
||||||
@@ -540,7 +541,13 @@ impl<C, R, P> bft::Proposer<Block> for Proposer<C, R, P>
|
|||||||
next_index += 1;
|
next_index += 1;
|
||||||
|
|
||||||
let signature = MaybeUnsigned(self.local_key.sign(&extrinsic.encode()).into());
|
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)
|
self.transaction_pool.import_unchecked_extrinsic(uxt)
|
||||||
.expect("locally signed extrinsic is valid; qed");
|
.expect("locally signed extrinsic is valid; qed");
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
//! Basic parachain that adds a number as part of its state.
|
//! Basic parachain that adds a number as part of its state.
|
||||||
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![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"))]
|
#[cfg(not(feature = "std"))]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|||||||
@@ -16,15 +16,14 @@
|
|||||||
|
|
||||||
//! Defines WASM module logic.
|
//! Defines WASM module logic.
|
||||||
|
|
||||||
use core::intrinsics;
|
use core::{intrinsics, panic};
|
||||||
|
|
||||||
use parachain::{self, ValidationResult};
|
use parachain::{self, ValidationResult};
|
||||||
use parachain::codec::Slicable;
|
use parachain::codec::Slicable;
|
||||||
use super::{HeadData, BlockData};
|
use super::{HeadData, BlockData};
|
||||||
|
|
||||||
#[panic_implementation]
|
#[panic_implementation]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn rust_begin_panic(_info: &::core::panic::PanicInfo) -> ! {
|
pub fn panic(_info: &panic::PanicInfo) -> ! {
|
||||||
unsafe {
|
unsafe {
|
||||||
intrinsics::abort()
|
intrinsics::abort()
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -67,6 +67,10 @@ pub type BlockNumber = u64;
|
|||||||
/// Alias to Ed25519 pubkey that identifies an account on the relay chain.
|
/// Alias to Ed25519 pubkey that identifies an account on the relay chain.
|
||||||
pub type AccountId = primitives::hash::H256;
|
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
|
/// 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".
|
/// exactly equivalent to what the substrate calls an "authority".
|
||||||
pub type SessionKey = primitives::AuthorityId;
|
pub type SessionKey = primitives::AuthorityId;
|
||||||
|
|||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! 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<Self, Block> {
|
||||||
|
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!())
|
||||||
|
}
|
||||||
|
}
|
||||||
+49
-158
@@ -58,11 +58,17 @@ extern crate substrate_runtime_staking as staking;
|
|||||||
extern crate substrate_runtime_system as system;
|
extern crate substrate_runtime_system as system;
|
||||||
extern crate substrate_runtime_timestamp as timestamp;
|
extern crate substrate_runtime_timestamp as timestamp;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
mod checked_block;
|
||||||
mod parachains;
|
mod parachains;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
use rstd::prelude::*;
|
#[cfg(feature = "std")]
|
||||||
use primitives::{AccountId, Balance, BlockNumber, Hash, Index, Log, SessionKey, Signature};
|
pub use checked_block::CheckedBlock;
|
||||||
use primitives::parachain::CandidateReceipt;
|
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}};
|
use runtime_primitives::{generic, traits::{HasPublicAux, BlakeTwo256, Convert}};
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@@ -78,107 +84,23 @@ pub const TIMESTAMP_SET_POSITION: u32 = 0;
|
|||||||
/// The position of the parachains set extrinsic.
|
/// The position of the parachains set extrinsic.
|
||||||
pub const PARACHAINS_SET_POSITION: u32 = 1;
|
pub const PARACHAINS_SET_POSITION: u32 = 1;
|
||||||
|
|
||||||
|
/// The address format for describing accounts.
|
||||||
|
pub type Address = staking::Address<Concrete>;
|
||||||
/// Block Id type for this block.
|
/// Block Id type for this block.
|
||||||
pub type BlockId = generic::BlockId<Block>;
|
pub type BlockId = generic::BlockId<Block>;
|
||||||
/// Unchecked extrinsic type as expected by this runtime.
|
/// Unchecked extrinsic type as expected by this runtime.
|
||||||
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<AccountId, Index, Call, Signature>;
|
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Index, Call, Signature>;
|
||||||
/// Extrinsic type as expected by this runtime.
|
/// Extrinsic type as expected by this runtime. This is not the type that is signed.
|
||||||
pub type Extrinsic = generic::Extrinsic<AccountId, Index, Call>;
|
pub type Extrinsic = generic::Extrinsic<Address, Index, Call>;
|
||||||
|
/// Extrinsic type that is signed.
|
||||||
|
pub type BareExtrinsic = generic::Extrinsic<AccountId, Index, Call>;
|
||||||
/// Block type as expected by this runtime.
|
/// Block type as expected by this runtime.
|
||||||
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
||||||
|
|
||||||
/// 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<Self, Block> {
|
|
||||||
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.
|
/// 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;
|
pub struct Concrete;
|
||||||
|
|
||||||
impl HasPublicAux for Concrete {
|
impl HasPublicAux for Concrete {
|
||||||
@@ -228,6 +150,7 @@ pub type Session = session::Module<Concrete>;
|
|||||||
impl staking::Trait for Concrete {
|
impl staking::Trait for Concrete {
|
||||||
type Balance = Balance;
|
type Balance = Balance;
|
||||||
type DetermineContractAddress = BlakeTwo256;
|
type DetermineContractAddress = BlakeTwo256;
|
||||||
|
type AccountIndex = AccountIndex;
|
||||||
}
|
}
|
||||||
/// Staking module for this concrete runtime.
|
/// Staking module for this concrete runtime.
|
||||||
pub type Staking = staking::Module<Concrete>;
|
pub type Staking = staking::Module<Concrete>;
|
||||||
@@ -280,7 +203,7 @@ impl_outer_dispatch! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Executive: handles dispatch to the various modules.
|
/// Executive: handles dispatch to the various modules.
|
||||||
pub type Executive = executive::Executive<Concrete, Block, Staking,
|
pub type Executive = executive::Executive<Concrete, Block, Staking, Staking,
|
||||||
(((((((), Parachains), Council), Democracy), Staking), Session), Timestamp)>;
|
(((((((), Parachains), Council), Democracy), Staking), Session), Timestamp)>;
|
||||||
|
|
||||||
impl_outer_config! {
|
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<CandidateReceipt>) -> Vec<UncheckedExtrinsic> {
|
|
||||||
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 {
|
pub mod api {
|
||||||
impl_stubs!(
|
impl_stubs!(
|
||||||
authorities => |()| super::Consensus::authorities(),
|
authorities => |()| super::Consensus::authorities(),
|
||||||
@@ -378,14 +272,14 @@ mod tests {
|
|||||||
let mut block = Block {
|
let mut block = Block {
|
||||||
header: Header::new(1, Default::default(), Default::default(), Default::default(), Default::default()),
|
header: Header::new(1, Default::default(), Default::default(), Default::default(), Default::default()),
|
||||||
extrinsics: vec![
|
extrinsics: vec![
|
||||||
UncheckedExtrinsic {
|
UncheckedExtrinsic::new(
|
||||||
extrinsic: Extrinsic {
|
generic::Extrinsic {
|
||||||
function: Call::Timestamp(timestamp::Call::set(100_000_000)),
|
function: Call::Timestamp(timestamp::Call::set(100_000_000)),
|
||||||
signed: Default::default(),
|
signed: Default::default(),
|
||||||
index: Default::default(),
|
index: Default::default(),
|
||||||
},
|
},
|
||||||
signature: Default::default(),
|
Default::default(),
|
||||||
}
|
)
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -394,14 +288,14 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(block, decoded);
|
assert_eq!(block, decoded);
|
||||||
|
|
||||||
block.extrinsics.push(UncheckedExtrinsic {
|
block.extrinsics.push(UncheckedExtrinsic::new(
|
||||||
extrinsic: Extrinsic {
|
generic::Extrinsic {
|
||||||
function: Call::Staking(staking::Call::stake()),
|
function: Call::Staking(staking::Call::stake()),
|
||||||
signed: Default::default(),
|
signed: Default::default(),
|
||||||
index: 10101,
|
index: 10101,
|
||||||
},
|
},
|
||||||
signature: Default::default(),
|
Default::default(),
|
||||||
});
|
));
|
||||||
|
|
||||||
let raw = block.encode();
|
let raw = block.encode();
|
||||||
let decoded = Block::decode(&mut &raw[..]).unwrap();
|
let decoded = Block::decode(&mut &raw[..]).unwrap();
|
||||||
@@ -414,25 +308,25 @@ mod tests {
|
|||||||
let mut block = Block {
|
let mut block = Block {
|
||||||
header: Header::new(1, Default::default(), Default::default(), Default::default(), Default::default()),
|
header: Header::new(1, Default::default(), Default::default(), Default::default(), Default::default()),
|
||||||
extrinsics: vec![
|
extrinsics: vec![
|
||||||
UncheckedExtrinsic {
|
UncheckedExtrinsic::new(
|
||||||
extrinsic: Extrinsic {
|
generic::Extrinsic {
|
||||||
function: Call::Timestamp(timestamp::Call::set(100_000_000)),
|
function: Call::Timestamp(timestamp::Call::set(100_000_000)),
|
||||||
signed: Default::default(),
|
signed: Default::default(),
|
||||||
index: Default::default(),
|
index: Default::default(),
|
||||||
},
|
},
|
||||||
signature: Default::default(),
|
Default::default(),
|
||||||
}
|
)
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
block.extrinsics.push(UncheckedExtrinsic {
|
block.extrinsics.push(UncheckedExtrinsic::new(
|
||||||
extrinsic: Extrinsic {
|
generic::Extrinsic {
|
||||||
function: Call::Staking(staking::Call::stake()),
|
function: Call::Staking(staking::Call::stake()),
|
||||||
signed: Default::default(),
|
signed: Default::default(),
|
||||||
index: 10101,
|
index: 10101,
|
||||||
},
|
},
|
||||||
signature: Default::default(),
|
Default::default()
|
||||||
});
|
));
|
||||||
|
|
||||||
let raw = block.encode();
|
let raw = block.encode();
|
||||||
let decoded_primitive = ::primitives::Block::decode(&mut &raw[..]).unwrap();
|
let decoded_primitive = ::primitives::Block::decode(&mut &raw[..]).unwrap();
|
||||||
@@ -444,22 +338,24 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_unchecked() {
|
fn serialize_unchecked() {
|
||||||
let tx = UncheckedExtrinsic {
|
let tx = UncheckedExtrinsic::new(
|
||||||
extrinsic: Extrinsic {
|
Extrinsic {
|
||||||
signed: [1; 32].into(),
|
signed: AccountId::from([1; 32]).into(),
|
||||||
index: 999,
|
index: 999,
|
||||||
function: Call::Timestamp(TimestampCall::set(135135)),
|
function: Call::Timestamp(TimestampCall::set(135135)),
|
||||||
},
|
},
|
||||||
signature: runtime_primitives::Ed25519Signature(primitives::hash::H512([0; 64])).into(),
|
runtime_primitives::Ed25519Signature(primitives::hash::H512([0; 64])).into()
|
||||||
};
|
);
|
||||||
// 71000000
|
|
||||||
// 0101010101010101010101010101010101010101010101010101010101010101
|
// 6f000000
|
||||||
// e703000000000000
|
// ff0101010101010101010101010101010101010101010101010101010101010101
|
||||||
// 00
|
// e7030000
|
||||||
|
// 0300
|
||||||
// df0f0200
|
// df0f0200
|
||||||
// 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
// 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
|
||||||
let v = Slicable::encode(&tx);
|
let v = Slicable::encode(&tx);
|
||||||
|
assert_eq!(&v[..], &hex!["6f000000ff0101010101010101010101010101010101010101010101010101010101010101e70300000300df0f02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"][..]);
|
||||||
println!("{}", HexDisplay::from(&v));
|
println!("{}", HexDisplay::from(&v));
|
||||||
assert_eq!(UncheckedExtrinsic::decode(&mut &v[..]).unwrap(), tx);
|
assert_eq!(UncheckedExtrinsic::decode(&mut &v[..]).unwrap(), tx);
|
||||||
}
|
}
|
||||||
@@ -467,7 +363,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn serialize_checked() {
|
fn serialize_checked() {
|
||||||
let xt = Extrinsic {
|
let xt = Extrinsic {
|
||||||
signed: hex!["0d71d1a9cad6f2ab773435a7dec1bac019994d05d1dd5eb3108211dcf25c9d1e"].into(),
|
signed: AccountId::from(hex!["0d71d1a9cad6f2ab773435a7dec1bac019994d05d1dd5eb3108211dcf25c9d1e"]).into(),
|
||||||
index: 0,
|
index: 0,
|
||||||
function: Call::CouncilVoting(council::voting::Call::propose(Box::new(
|
function: Call::CouncilVoting(council::voting::Call::propose(Box::new(
|
||||||
PrivCall::Consensus(consensus::PrivCall::set_code(
|
PrivCall::Consensus(consensus::PrivCall::set_code(
|
||||||
@@ -476,11 +372,6 @@ mod tests {
|
|||||||
))),
|
))),
|
||||||
};
|
};
|
||||||
let v = Slicable::encode(&xt);
|
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);
|
assert_eq!(Extrinsic::decode(&mut &v[..]).unwrap(), xt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -234,6 +234,7 @@ mod tests {
|
|||||||
use runtime_primitives::testing::{Digest, Header};
|
use runtime_primitives::testing::{Digest, Header};
|
||||||
use consensus;
|
use consensus;
|
||||||
|
|
||||||
|
#[derive(Clone, Eq, PartialEq)]
|
||||||
pub struct Test;
|
pub struct Test;
|
||||||
impl HasPublicAux for Test {
|
impl HasPublicAux for Test {
|
||||||
type PublicAux = u64;
|
type PublicAux = u64;
|
||||||
|
|||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! 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<CandidateReceipt>) -> Vec<UncheckedExtrinsic> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
Generated
+5
@@ -543,6 +543,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "substrate-codec"
|
name = "substrate-codec"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "substrate-keyring"
|
name = "substrate-keyring"
|
||||||
@@ -647,6 +650,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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 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-codec 0.1.0",
|
||||||
"substrate-runtime-io 0.1.0",
|
"substrate-runtime-io 0.1.0",
|
||||||
"substrate-runtime-primitives 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)",
|
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"safe-mix 0.1.0",
|
"safe-mix 0.1.0",
|
||||||
"serde 1.0.64 (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-codec 0.1.0",
|
||||||
"substrate-primitives 0.1.0",
|
"substrate-primitives 0.1.0",
|
||||||
"substrate-runtime-io 0.1.0",
|
"substrate-runtime-io 0.1.0",
|
||||||
|
|||||||
BIN
Binary file not shown.
Binary file not shown.
@@ -177,6 +177,11 @@ fn poc_2_testnet_config() -> ChainConfig {
|
|||||||
intentions: initial_authorities.iter().cloned().map(Into::into).collect(),
|
intentions: initial_authorities.iter().cloned().map(Into::into).collect(),
|
||||||
transaction_base_fee: 100,
|
transaction_base_fee: 100,
|
||||||
transaction_byte_fee: 1,
|
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(),
|
balances: endowed_accounts.iter().map(|&k|(k, 1u128 << 60)).collect(),
|
||||||
validator_count: 12,
|
validator_count: 12,
|
||||||
sessions_per_era: 24, // 24 hours per era.
|
sessions_per_era: 24, // 24 hours per era.
|
||||||
@@ -236,6 +241,11 @@ fn testnet_config(initial_authorities: Vec<AuthorityId>) -> ChainConfig {
|
|||||||
intentions: initial_authorities.iter().cloned().map(Into::into).collect(),
|
intentions: initial_authorities.iter().cloned().map(Into::into).collect(),
|
||||||
transaction_base_fee: 1,
|
transaction_base_fee: 1,
|
||||||
transaction_byte_fee: 0,
|
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(),
|
balances: endowed_accounts.iter().map(|&k|(k, (1u128 << 60))).collect(),
|
||||||
validator_count: 2,
|
validator_count: 2,
|
||||||
sessions_per_era: 5,
|
sessions_per_era: 5,
|
||||||
|
|||||||
@@ -6,11 +6,13 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.3.0"
|
log = "0.3.0"
|
||||||
error-chain = "0.11"
|
error-chain = "0.11"
|
||||||
|
parking_lot = "0.4"
|
||||||
polkadot-api = { path = "../api" }
|
polkadot-api = { path = "../api" }
|
||||||
polkadot-primitives = { path = "../primitives" }
|
polkadot-primitives = { path = "../primitives" }
|
||||||
polkadot-runtime = { path = "../runtime" }
|
polkadot-runtime = { path = "../runtime" }
|
||||||
substrate-client = { path = "../../substrate/client" }
|
substrate-client = { path = "../../substrate/client" }
|
||||||
substrate-codec = { path = "../../substrate/codec" }
|
substrate-codec = { path = "../../substrate/codec" }
|
||||||
|
substrate-keyring = { path = "../../substrate/keyring" }
|
||||||
substrate-extrinsic-pool = { path = "../../substrate/extrinsic-pool" }
|
substrate-extrinsic-pool = { path = "../../substrate/extrinsic-pool" }
|
||||||
substrate-primitives = { path = "../../substrate/primitives" }
|
substrate-primitives = { path = "../../substrate/primitives" }
|
||||||
substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" }
|
substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" }
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
use extrinsic_pool::{self, txpool};
|
use extrinsic_pool::{self, txpool};
|
||||||
use primitives::Hash;
|
use primitives::Hash;
|
||||||
use runtime::UncheckedExtrinsic;
|
use runtime::{Address, UncheckedExtrinsic};
|
||||||
|
|
||||||
error_chain! {
|
error_chain! {
|
||||||
links {
|
links {
|
||||||
@@ -34,9 +34,9 @@ error_chain! {
|
|||||||
display("Inehrent transactions cannot be queued."),
|
display("Inehrent transactions cannot be queued."),
|
||||||
}
|
}
|
||||||
/// Attempted to queue a transaction with bad signature.
|
/// Attempted to queue a transaction with bad signature.
|
||||||
BadSignature(xt: UncheckedExtrinsic) {
|
BadSignature(e: &'static str) {
|
||||||
description("Transaction had bad signature."),
|
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.
|
/// Attempted to queue a transaction that is already in the pool.
|
||||||
AlreadyImported(hash: Hash) {
|
AlreadyImported(hash: Hash) {
|
||||||
@@ -48,6 +48,16 @@ error_chain! {
|
|||||||
description("Error importing transaction"),
|
description("Error importing transaction"),
|
||||||
display("Error importing transaction: {}", err.description()),
|
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"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ extern crate substrate_runtime_primitives;
|
|||||||
extern crate polkadot_runtime as runtime;
|
extern crate polkadot_runtime as runtime;
|
||||||
extern crate polkadot_primitives as primitives;
|
extern crate polkadot_primitives as primitives;
|
||||||
extern crate polkadot_api;
|
extern crate polkadot_api;
|
||||||
|
extern crate parking_lot;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
extern crate substrate_keyring;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate error_chain;
|
extern crate error_chain;
|
||||||
@@ -33,18 +37,20 @@ mod error;
|
|||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
collections::HashMap,
|
collections::{hash_map::Entry, HashMap},
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
result
|
||||||
};
|
};
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
use codec::Slicable;
|
use codec::Slicable;
|
||||||
use extrinsic_pool::{Pool, txpool::{self, Readiness, scoring::{Change, Choice}}};
|
use extrinsic_pool::{Pool, txpool::{self, Readiness, scoring::{Change, Choice}}};
|
||||||
use extrinsic_pool::api::ExtrinsicPool;
|
use extrinsic_pool::api::ExtrinsicPool;
|
||||||
use polkadot_api::PolkadotApi;
|
use polkadot_api::PolkadotApi;
|
||||||
use primitives::{AccountId, Hash, UncheckedExtrinsic as FutureProofUncheckedExtrinsic};
|
use primitives::{AccountId, AccountIndex, Hash, Index, UncheckedExtrinsic as FutureProofUncheckedExtrinsic};
|
||||||
use runtime::UncheckedExtrinsic;
|
use runtime::{Address, RawAddress, UncheckedExtrinsic};
|
||||||
use substrate_runtime_primitives::traits::{Bounded, Checkable, BlakeTwo256, Hashing};
|
use substrate_runtime_primitives::traits::{Bounded, Checkable, Hashing, BlakeTwo256};
|
||||||
|
|
||||||
pub use extrinsic_pool::txpool::{Options, Status, LightStatus, VerifiedTransaction as VerifiedTransactionOps};
|
pub use extrinsic_pool::txpool::{Options, Status, LightStatus, VerifiedTransaction as VerifiedTransactionOps};
|
||||||
pub use error::{Error, ErrorKind, Result};
|
pub use error::{Error, ErrorKind, Result};
|
||||||
@@ -53,37 +59,68 @@ pub use error::{Error, ErrorKind, Result};
|
|||||||
pub type CheckedExtrinsic = <UncheckedExtrinsic as Checkable>::Checked;
|
pub type CheckedExtrinsic = <UncheckedExtrinsic as Checkable>::Checked;
|
||||||
|
|
||||||
/// A verified transaction which should be includable and non-inherent.
|
/// A verified transaction which should be includable and non-inherent.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub struct VerifiedTransaction {
|
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,
|
hash: Hash,
|
||||||
encoded_size: usize,
|
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 {
|
impl VerifiedTransaction {
|
||||||
/// Attempt to verify a transaction.
|
/// Attempt to verify a transaction.
|
||||||
fn create(xt: UncheckedExtrinsic) -> Result<Self> {
|
fn create(original: UncheckedExtrinsic) -> Result<Self> {
|
||||||
if !xt.is_signed() {
|
if !original.is_signed() {
|
||||||
bail!(ErrorKind::IsInherent(xt))
|
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);
|
/// If this transaction isn't really verified, verify it and morph it into a really verified
|
||||||
match xt.check() {
|
/// transaction.
|
||||||
Ok(xt) => {
|
pub fn polish<F>(&self, lookup: F) -> Result<()> where
|
||||||
let hash = BlakeTwo256::hash(&message);
|
F: FnOnce(Address) -> result::Result<AccountId, &'static str> + Send + Sync
|
||||||
Ok(VerifiedTransaction {
|
{
|
||||||
inner: xt,
|
let inner: result::Result<CheckedExtrinsic, Error> = self.original
|
||||||
hash: hash.into(),
|
.clone()
|
||||||
encoded_size: message.len(),
|
.check(lookup)
|
||||||
})
|
.map_err(|e| ErrorKind::BadSignature(e).into());
|
||||||
}
|
*self.inner.lock() = Some(inner?);
|
||||||
Err(xt) => Err(ErrorKind::BadSignature(xt).into()),
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is this transaction *really* verified?
|
||||||
|
pub fn is_really_verified(&self) -> bool {
|
||||||
|
self.inner.lock().is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the underlying transaction.
|
/// Access the underlying transaction.
|
||||||
pub fn as_transaction(&self) -> &UncheckedExtrinsic {
|
pub fn as_transaction(&self) -> &UncheckedExtrinsic {
|
||||||
self.as_ref().as_unchecked()
|
&self.original
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert to primitive unchecked extrinsic.
|
/// Convert to primitive unchecked extrinsic.
|
||||||
@@ -93,8 +130,8 @@ impl VerifiedTransaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Consume the verified transaciton, yielding the unchecked counterpart.
|
/// Consume the verified transaciton, yielding the unchecked counterpart.
|
||||||
pub fn into_inner(self) -> CheckedExtrinsic {
|
pub fn into_inner(self) -> Result<CheckedExtrinsic> {
|
||||||
self.inner
|
self.inner.lock().clone().ok_or_else(|| ErrorKind::NotReady.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the 256-bit hash of this transaction.
|
/// Get the 256-bit hash of this transaction.
|
||||||
@@ -103,8 +140,13 @@ impl VerifiedTransaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the account ID of the sender of this transaction.
|
/// Get the account ID of the sender of this transaction.
|
||||||
pub fn sender(&self) -> &AccountId {
|
pub fn sender(&self) -> Result<AccountId> {
|
||||||
&self.inner.signed
|
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.
|
/// 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 {
|
impl txpool::VerifiedTransaction for VerifiedTransaction {
|
||||||
type Hash = Hash;
|
type Hash = Hash;
|
||||||
type Sender = AccountId;
|
type Sender = Address;
|
||||||
|
|
||||||
fn hash(&self) -> &Self::Hash {
|
fn hash(&self) -> &Self::Hash {
|
||||||
&self.hash
|
&self.hash
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sender(&self) -> &Self::Sender {
|
fn sender(&self) -> &Self::Sender {
|
||||||
&self.inner.signed
|
self.original.sender()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mem_usage(&self) -> usize {
|
fn mem_usage(&self) -> usize {
|
||||||
@@ -145,7 +181,7 @@ impl txpool::Scoring<VerifiedTransaction> for Scoring {
|
|||||||
type Event = ();
|
type Event = ();
|
||||||
|
|
||||||
fn compare(&self, old: &VerifiedTransaction, other: &VerifiedTransaction) -> Ordering {
|
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 {
|
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> {
|
pub struct Ready<'a, T: 'a + PolkadotApi> {
|
||||||
at_block: T::CheckedBlockId,
|
at_block: T::CheckedBlockId,
|
||||||
api: &'a T,
|
api: &'a T,
|
||||||
known_indices: HashMap<AccountId, ::primitives::Index>,
|
known_nonces: HashMap<AccountId, (::primitives::Index, bool)>,
|
||||||
}
|
known_indexes: HashMap<AccountIndex, AccountId>,
|
||||||
|
|
||||||
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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: 'a + PolkadotApi> Ready<'a, T> {
|
impl<'a, T: 'a + PolkadotApi> Ready<'a, T> {
|
||||||
@@ -193,34 +220,85 @@ impl<'a, T: 'a + PolkadotApi> Ready<'a, T> {
|
|||||||
Ready {
|
Ready {
|
||||||
at_block: at,
|
at_block: at,
|
||||||
api,
|
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 {
|
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));
|
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
|
// TODO: find a way to handle index error properly -- will need changes to
|
||||||
// transaction-pool trait.
|
// transaction-pool trait.
|
||||||
let (api, at_block) = (&self.api, &self.at_block);
|
let (api, at_block) = (&self.api, &self.at_block);
|
||||||
let next_index = self.known_indices.entry(sender)
|
let get_nonce = || api.index(at_block, sender).ok().unwrap_or_else(Bounded::max_value);
|
||||||
.or_insert_with(|| 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) {
|
if *was_index_sender == is_index_sender || get_nonce() == *next_nonce {
|
||||||
Ordering::Greater => Readiness::Future,
|
match xt.original.extrinsic.index.cmp(&next_nonce) {
|
||||||
Ordering::Equal => Readiness::Ready,
|
Ordering::Greater => Readiness::Future,
|
||||||
Ordering::Less => Readiness::Stale,
|
Ordering::Less => Readiness::Stale,
|
||||||
};
|
Ordering::Equal => {
|
||||||
|
// remember to increment `next_nonce`
|
||||||
// remember to increment `next_index`
|
// TODO: this won't work perfectly since accounts can now be killed, returning the nonce
|
||||||
*next_index = next_index.saturating_add(1);
|
// to zero.
|
||||||
|
*next_nonce = next_nonce.saturating_add(1);
|
||||||
result
|
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>> {
|
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()
|
.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![]);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user