mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 12:11:02 +00:00
Use BTreeMap rather than HashMap in demo runtime.
This commit is contained in:
Generated
+4
@@ -1037,6 +1037,7 @@ dependencies = [
|
||||
"polkadot-runtime 0.1.0",
|
||||
"substrate-client 0.1.0",
|
||||
"substrate-executor 0.1.0",
|
||||
"substrate-keyring 0.1.0",
|
||||
"substrate-state-machine 0.1.0",
|
||||
]
|
||||
|
||||
@@ -1077,8 +1078,11 @@ name = "polkadot-consensus"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ed25519 0.1.0",
|
||||
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"polkadot-api 0.1.0",
|
||||
"polkadot-collator 0.1.0",
|
||||
"polkadot-primitives 0.1.0",
|
||||
"polkadot-statement-table 0.1.0",
|
||||
"substrate-bft 0.1.0",
|
||||
|
||||
@@ -3,3 +3,4 @@
|
||||
cd substrate/executor/wasm && ./build.sh && cd ../../..
|
||||
cd substrate/test-runtime/wasm && ./build.sh && cd ../../..
|
||||
cd polkadot/runtime/wasm && ./build.sh && cd ../../..
|
||||
cd demo/runtime/wasm && ./build.sh && cd ../../..
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::cell::RefCell;
|
||||
use rstd::collections::hash_map::{HashMap, Entry};
|
||||
use rstd::collections::btree_map::{BTreeMap, Entry};
|
||||
use runtime_io::{print, blake2_256};
|
||||
use codec::KeyedVec;
|
||||
use runtime_support::{storage, StorageVec};
|
||||
@@ -98,7 +98,7 @@ pub fn bondage(who: &AccountId) -> Bondage {
|
||||
pub mod public {
|
||||
use super::*;
|
||||
|
||||
type State = HashMap<AccountId, (Option<Balance>, Option<Vec<u8>>, HashMap<Vec<u8>, Option<Vec<u8>>>)>;
|
||||
type State = BTreeMap<AccountId, (Option<Balance>, Option<Vec<u8>>, BTreeMap<Vec<u8>, Option<Vec<u8>>>)>;
|
||||
|
||||
trait Externalities {
|
||||
fn get_storage(&self, account: &AccountId, location: &[u8]) -> Option<Vec<u8>>;
|
||||
@@ -215,7 +215,7 @@ pub mod public {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut local = HashMap::new();
|
||||
let mut local = BTreeMap::new();
|
||||
|
||||
// two inserts are safe
|
||||
assert!(&dest != transactor);
|
||||
@@ -251,7 +251,7 @@ pub mod public {
|
||||
// TODO: consider storing upper-bound for contract's gas limit in fixed-length runtime
|
||||
// code in contract itself and use that.
|
||||
|
||||
let local: RefCell<State> = RefCell::new(HashMap::new());
|
||||
let local: RefCell<State> = RefCell::new(BTreeMap::new());
|
||||
|
||||
if transactor != dest {
|
||||
let mut local = local.borrow_mut();
|
||||
|
||||
BIN
Binary file not shown.
Binary file not shown.
@@ -11,3 +11,6 @@ polkadot-primitives = { path = "../primitives" }
|
||||
substrate-client = { path = "../../substrate/client" }
|
||||
substrate-executor = { path = "../../substrate/executor" }
|
||||
substrate-state-machine = { path = "../../substrate/state-machine" }
|
||||
|
||||
[dev-dependencies]
|
||||
substrate-keyring = { path = "../../substrate/keyring" }
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
//! runtime.
|
||||
|
||||
extern crate polkadot_executor as polkadot_executor;
|
||||
extern crate polkadot_runtime ;
|
||||
extern crate polkadot_runtime;
|
||||
extern crate polkadot_primitives as primitives;
|
||||
extern crate substrate_client as client;
|
||||
extern crate substrate_executor as substrate_executor;
|
||||
@@ -27,13 +27,18 @@ extern crate substrate_state_machine as state_machine;
|
||||
#[macro_use]
|
||||
extern crate error_chain;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate substrate_keyring as keyring;
|
||||
|
||||
use client::backend::Backend;
|
||||
use client::Client;
|
||||
use polkadot_runtime::runtime;
|
||||
use polkadot_executor::Executor as LocalDispatch;
|
||||
use substrate_executor::{NativeExecutionDispatch, NativeExecutor};
|
||||
use primitives::{AccountId, SessionKey};
|
||||
use primitives::block::Id as BlockId;
|
||||
use state_machine::OverlayedChanges;
|
||||
use primitives::{AccountId, SessionKey, Timestamp};
|
||||
use primitives::block::{Id as BlockId, Block, Header, Body};
|
||||
use primitives::transaction::UncheckedTransaction;
|
||||
use primitives::parachain::DutyRoster;
|
||||
|
||||
error_chain! {
|
||||
@@ -43,10 +48,21 @@ error_chain! {
|
||||
description("Unknown runtime code")
|
||||
display("Unknown runtime code")
|
||||
}
|
||||
/// Unknown block ID.
|
||||
UnknownBlock(b: BlockId) {
|
||||
description("Unknown block")
|
||||
display("Unknown block")
|
||||
}
|
||||
/// Attempted to push an inherent transaction manually.
|
||||
PushedInherentTransaction(tx: UncheckedTransaction) {
|
||||
description("Attempted to push an inherent transaction to a block."),
|
||||
display("Pushed inherent transaction to a block: {:?}", tx),
|
||||
}
|
||||
/// Badly-formed transaction.
|
||||
BadlyFormedTransaction(tx: UncheckedTransaction) {
|
||||
description("Attempted to push a badly-formed transaction to a block."),
|
||||
display("Pushed badly-formed transaction to a block: {:?}", tx),
|
||||
}
|
||||
/// Some other error.
|
||||
// TODO: allow to be specified as associated type of PolkadotApi
|
||||
Other(e: Box<::std::error::Error + Send>) {
|
||||
@@ -60,10 +76,29 @@ error_chain! {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<client::error::Error> for Error {
|
||||
fn from(e: client::error::Error) -> Error {
|
||||
match e {
|
||||
client::error::Error(client::error::ErrorKind::UnknownBlock(b), _) => Error::from_kind(ErrorKind::UnknownBlock(b)),
|
||||
other => Error::from_kind(ErrorKind::Other(Box::new(other) as Box<_>)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BlockBuilder: Sized {
|
||||
/// Push a non-inherent transaction.
|
||||
fn push_transaction(&mut self, transaction: UncheckedTransaction) -> Result<()>;
|
||||
|
||||
/// Finalise the block.
|
||||
fn bake(self) -> Block;
|
||||
}
|
||||
|
||||
/// Trait encapsulating the Polkadot API.
|
||||
///
|
||||
/// All calls should fail when the exact runtime is unknown.
|
||||
pub trait PolkadotApi {
|
||||
type BlockBuilder: BlockBuilder;
|
||||
|
||||
/// Get session keys at a given block.
|
||||
fn session_keys(&self, at: &BlockId) -> Result<Vec<SessionKey>>;
|
||||
|
||||
@@ -72,24 +107,26 @@ pub trait PolkadotApi {
|
||||
|
||||
/// Get the authority duty roster at a block.
|
||||
fn duty_roster(&self, at: &BlockId) -> Result<DutyRoster>;
|
||||
}
|
||||
|
||||
fn convert_client_error(e: client::error::Error) -> Error {
|
||||
match e {
|
||||
client::error::Error(client::error::ErrorKind::UnknownBlock(b), _) => Error::from_kind(ErrorKind::UnknownBlock(b)),
|
||||
other => Error::from_kind(ErrorKind::Other(Box::new(other) as Box<_>)),
|
||||
}
|
||||
/// Get the timestamp registered at a block.
|
||||
fn timestamp(&self, at: &BlockId) -> Result<Timestamp>;
|
||||
|
||||
/// Evaluate a block and see if it gives an error.
|
||||
fn evaluate_block(&self, at: &BlockId, block: Block) -> Result<()>;
|
||||
|
||||
/// Create a block builder on top of the parent block.
|
||||
fn build_block(&self, parent: &BlockId, timestamp: u64) -> Result<Self::BlockBuilder>;
|
||||
}
|
||||
|
||||
// set up the necessary scaffolding to execute the runtime.
|
||||
macro_rules! with_runtime {
|
||||
($client: ident, $at: expr, $exec: expr) => {{
|
||||
// bail if the code is not the same as the natively linked.
|
||||
if $client.code_at($at).map_err(convert_client_error)? != LocalDispatch::native_equivalent() {
|
||||
if $client.code_at($at)? != LocalDispatch::native_equivalent() {
|
||||
bail!(ErrorKind::UnknownRuntime);
|
||||
}
|
||||
|
||||
$client.state_at($at).map_err(convert_client_error).and_then(|state| {
|
||||
$client.state_at($at).map_err(Error::from).and_then(|state| {
|
||||
let mut changes = Default::default();
|
||||
let mut ext = state_machine::Ext {
|
||||
overlay: &mut changes,
|
||||
@@ -104,6 +141,8 @@ macro_rules! with_runtime {
|
||||
impl<B: Backend> PolkadotApi for Client<B, NativeExecutor<LocalDispatch>>
|
||||
where ::client::error::Error: From<<<B as Backend>::State as state_machine::backend::Backend>::Error>
|
||||
{
|
||||
type BlockBuilder = ClientBlockBuilder<B::State>;
|
||||
|
||||
fn session_keys(&self, at: &BlockId) -> Result<Vec<SessionKey>> {
|
||||
with_runtime!(self, at, ::runtime::consensus::authorities)
|
||||
}
|
||||
@@ -115,4 +154,185 @@ impl<B: Backend> PolkadotApi for Client<B, NativeExecutor<LocalDispatch>>
|
||||
fn duty_roster(&self, at: &BlockId) -> Result<DutyRoster> {
|
||||
with_runtime!(self, at, ::runtime::parachains::calculate_duty_roster)
|
||||
}
|
||||
|
||||
fn timestamp(&self, at: &BlockId) -> Result<Timestamp> {
|
||||
with_runtime!(self, at, ::runtime::timestamp::get)
|
||||
}
|
||||
|
||||
fn evaluate_block(&self, at: &BlockId, block: Block) -> Result<()> {
|
||||
with_runtime!(self, at, || ::runtime::system::internal::execute_block(block))
|
||||
}
|
||||
|
||||
fn build_block(&self, parent: &BlockId, timestamp: Timestamp) -> Result<Self::BlockBuilder> {
|
||||
if self.code_at(parent)? != LocalDispatch::native_equivalent() {
|
||||
bail!(ErrorKind::UnknownRuntime);
|
||||
}
|
||||
|
||||
let header = Header {
|
||||
parent_hash: self.block_hash_from_id(parent)?.ok_or(ErrorKind::UnknownBlock(*parent))?,
|
||||
number: self.block_number_from_id(parent)?.ok_or(ErrorKind::UnknownBlock(*parent))? + 1,
|
||||
state_root: Default::default(),
|
||||
transaction_root: Default::default(),
|
||||
digest: Default::default(),
|
||||
};
|
||||
|
||||
let body = Body {
|
||||
timestamp: timestamp,
|
||||
transactions: Vec::new(),
|
||||
};
|
||||
|
||||
let mut builder = ClientBlockBuilder {
|
||||
parent: *parent,
|
||||
changes: OverlayedChanges::default(),
|
||||
state: self.state_at(parent)?,
|
||||
header,
|
||||
timestamp,
|
||||
transactions: Vec::new(),
|
||||
};
|
||||
|
||||
for inherent in body.inherent_transactions() {
|
||||
builder.execute_transaction(inherent)?;
|
||||
}
|
||||
|
||||
Ok(builder)
|
||||
}
|
||||
}
|
||||
|
||||
/// A polkadot block builder.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ClientBlockBuilder<S> {
|
||||
parent: BlockId,
|
||||
changes: OverlayedChanges,
|
||||
state: S,
|
||||
header: Header,
|
||||
timestamp: Timestamp,
|
||||
transactions: Vec<UncheckedTransaction>,
|
||||
}
|
||||
|
||||
impl<S: state_machine::Backend> ClientBlockBuilder<S>
|
||||
where S::Error: Into<client::error::Error>
|
||||
{
|
||||
// executes a transaction, inherent or otherwise, without appending to the list
|
||||
fn execute_transaction(&mut self, transaction: UncheckedTransaction) -> Result<()> {
|
||||
if !transaction.is_well_formed() {
|
||||
bail!(ErrorKind::BadlyFormedTransaction(transaction));
|
||||
}
|
||||
|
||||
let mut ext = state_machine::Ext {
|
||||
overlay: &mut self.changes,
|
||||
backend: &self.state,
|
||||
};
|
||||
|
||||
// TODO: avoid clone
|
||||
let header = self.header.clone();
|
||||
let result = ::substrate_executor::with_native_environment(
|
||||
&mut ext,
|
||||
move || runtime::system::internal::execute_transaction(transaction, header),
|
||||
).map_err(Into::into);
|
||||
|
||||
match result {
|
||||
Ok(header) => {
|
||||
ext.overlay.commit_prospective();
|
||||
self.header = header;
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
ext.overlay.discard_prospective();
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: state_machine::Backend> BlockBuilder for ClientBlockBuilder<S>
|
||||
where S::Error: Into<client::error::Error>
|
||||
{
|
||||
fn push_transaction(&mut self, transaction: UncheckedTransaction) -> Result<()> {
|
||||
if transaction.transaction.function.is_inherent() {
|
||||
bail!(ErrorKind::PushedInherentTransaction(transaction));
|
||||
} else {
|
||||
self.execute_transaction(transaction.clone())?;
|
||||
self.transactions.push(transaction);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn bake(mut self) -> Block {
|
||||
let mut ext = state_machine::Ext {
|
||||
overlay: &mut self.changes,
|
||||
backend: &self.state,
|
||||
};
|
||||
|
||||
let old_header = self.header;
|
||||
let final_header = ::substrate_executor::with_native_environment(
|
||||
&mut ext,
|
||||
move || runtime::system::internal::finalise_block(old_header)
|
||||
).expect("all inherent transactions pushed; all other transactions executed correctly; qed");
|
||||
|
||||
Block {
|
||||
header: final_header,
|
||||
body: Body {
|
||||
timestamp: self.timestamp,
|
||||
transactions: self.transactions,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use client::in_mem::Backend as InMemory;
|
||||
use polkadot_runtime::genesismap::{additional_storage_with_genesis, GenesisConfig};
|
||||
use substrate_executor::NativeExecutionDispatch;
|
||||
use keyring::Keyring;
|
||||
|
||||
fn validators() -> Vec<AccountId> {
|
||||
vec![
|
||||
Keyring::One.to_raw_public(),
|
||||
Keyring::Two.to_raw_public(),
|
||||
]
|
||||
}
|
||||
|
||||
fn client() -> Client<InMemory, NativeExecutor<LocalDispatch>> {
|
||||
::client::new_in_mem(
|
||||
LocalDispatch::new(),
|
||||
|| {
|
||||
let config = GenesisConfig::new_simple(validators(), 100);
|
||||
|
||||
// override code entry.
|
||||
let mut storage = config.genesis_map();
|
||||
storage.insert(b":code".to_vec(), LocalDispatch::native_equivalent().to_vec());
|
||||
|
||||
let block = ::client::genesis::construct_genesis_block(
|
||||
&config.genesis_map()
|
||||
);
|
||||
storage.extend(additional_storage_with_genesis(&block));
|
||||
(block.header, storage.into_iter().collect())
|
||||
}
|
||||
).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gets_session_and_validator_keys() {
|
||||
let client = client();
|
||||
assert_eq!(client.session_keys(&BlockId::Number(0)).unwrap(), validators());
|
||||
assert_eq!(client.validators(&BlockId::Number(0)).unwrap(), validators());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_block() {
|
||||
let client = client();
|
||||
|
||||
let block_builder = client.build_block(&BlockId::Number(0), 1_000_000).unwrap();
|
||||
let block = block_builder.bake();
|
||||
|
||||
assert_eq!(block.header.number, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cannot_build_block_on_unknown_parent() {
|
||||
let client = client();
|
||||
assert!(client.build_block(&BlockId::Number(100), 1_000_000).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ pub trait ParachainContext {
|
||||
fn produce_candidate<I: IntoIterator<Item=(ParaId, Message)>>(
|
||||
&self,
|
||||
ingress: I,
|
||||
) -> (parachain::BlockData, polkadot_primitives::Signature);
|
||||
) -> (parachain::BlockData, polkadot_primitives::AccountId, polkadot_primitives::Signature);
|
||||
}
|
||||
|
||||
/// Relay chain context needed to collate.
|
||||
@@ -131,7 +131,7 @@ pub fn collate<'a, R, P>(local_id: ParaId, relay_context: R, para_context: P)
|
||||
P: ParachainContext + 'a,
|
||||
{
|
||||
Box::new(collate_ingress(relay_context).map(move |ingress| {
|
||||
let (block_data, signature) = para_context.produce_candidate(
|
||||
let (block_data, _, signature) = para_context.produce_candidate(
|
||||
ingress.0.iter().flat_map(|&(id, ref msgs)| msgs.iter().cloned().map(move |msg| (id, msg)))
|
||||
);
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@ futures = "0.1.17"
|
||||
parking_lot = "0.4"
|
||||
tokio-timer = "0.1.2"
|
||||
ed25519 = { path = "../../substrate/ed25519" }
|
||||
error-chain = "0.11"
|
||||
polkadot-api = { path = "../api" }
|
||||
polkadot-collator = { path = "../collator" }
|
||||
polkadot-primitives = { path = "../primitives" }
|
||||
polkadot-statement-table = { path = "../statement-table" }
|
||||
substrate-bft = { path = "../../substrate/bft" }
|
||||
|
||||
@@ -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/>.
|
||||
|
||||
//! Errors that can occur during the consensus process.
|
||||
|
||||
use primitives::block::HeaderHash;
|
||||
|
||||
error_chain! {
|
||||
links {
|
||||
PolkadotApi(::polkadot_api::Error, ::polkadot_api::ErrorKind);
|
||||
Bft(::bft::Error, ::bft::ErrorKind);
|
||||
}
|
||||
|
||||
errors {
|
||||
InvalidDutyRosterLength(expected: usize, got: usize) {
|
||||
description("Duty Roster had invalid length"),
|
||||
display("Invalid duty roster length: expected {}, got {}", expected, got),
|
||||
}
|
||||
ProposalNotForPolkadot {
|
||||
description("Proposal provided not a Polkadot block."),
|
||||
display("Proposal provided not a Polkadot block."),
|
||||
}
|
||||
TimestampInFuture {
|
||||
description("Proposal had timestamp too far in the future."),
|
||||
display("Proposal had timestamp too far in the future."),
|
||||
}
|
||||
WrongParentHash(expected: HeaderHash, got: HeaderHash) {
|
||||
description("Proposal had wrong parent hash."),
|
||||
display("Proposal had wrong parent hash. Expected {:?}, got {:?}", expected, got),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<::bft::InputStreamConcluded> for Error {
|
||||
fn from(err: ::bft::InputStreamConcluded) -> Self {
|
||||
::bft::Error::from(err).into()
|
||||
}
|
||||
}
|
||||
@@ -29,31 +29,73 @@
|
||||
//!
|
||||
//! Groups themselves may be compromised by malicious authorities.
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
|
||||
use codec::Slicable;
|
||||
use table::Table;
|
||||
use table::generic::Statement as GenericStatement;
|
||||
use polkadot_primitives::Hash;
|
||||
use polkadot_primitives::parachain::{Id as ParaId, CandidateReceipt};
|
||||
use primitives::block::Block as SubstrateBlock;
|
||||
use primitives::AuthorityId;
|
||||
|
||||
use parking_lot::Mutex;
|
||||
|
||||
extern crate futures;
|
||||
extern crate ed25519;
|
||||
extern crate parking_lot;
|
||||
extern crate tokio_timer;
|
||||
extern crate polkadot_api;
|
||||
extern crate polkadot_collator as collator;
|
||||
extern crate polkadot_statement_table as table;
|
||||
extern crate polkadot_primitives;
|
||||
extern crate substrate_bft as bft;
|
||||
extern crate substrate_codec as codec;
|
||||
extern crate substrate_primitives as primitives;
|
||||
|
||||
#[macro_use]
|
||||
extern crate error_chain;
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
|
||||
use codec::Slicable;
|
||||
use table::{Table, Context as TableContextTrait};
|
||||
use table::generic::Statement as GenericStatement;
|
||||
use polkadot_api::{PolkadotApi, BlockBuilder};
|
||||
use polkadot_primitives::{Hash, Timestamp};
|
||||
use polkadot_primitives::block::Block as PolkadotBlock;
|
||||
use polkadot_primitives::parachain::{Id as ParaId, DutyRoster, BlockData, Extrinsic, CandidateReceipt};
|
||||
use primitives::block::{Block as SubstrateBlock, Header as SubstrateHeader, HeaderHash, Id as BlockId};
|
||||
use primitives::AuthorityId;
|
||||
|
||||
use futures::prelude::*;
|
||||
use futures::future;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
pub use self::error::{ErrorKind, Error};
|
||||
|
||||
mod error;
|
||||
|
||||
/// A handle to a statement table router.
|
||||
pub trait TableRouter {
|
||||
/// Errors when fetching data from the network.
|
||||
type Error;
|
||||
/// Future that resolves when candidate data is fetched.
|
||||
type FetchCandidate: IntoFuture<Item=BlockData,Error=Self::Error>;
|
||||
/// Future that resolves when extrinsic candidate data is fetched.
|
||||
type FetchExtrinsic: IntoFuture<Item=Extrinsic,Error=Self::Error>;
|
||||
|
||||
/// Note local candidate data.
|
||||
fn local_candidate_data(&self, block_data: BlockData, extrinsic: Extrinsic);
|
||||
|
||||
/// Fetch block data for a specific candidate.
|
||||
fn fetch_block_data(&self, candidate: &CandidateReceipt) -> Self::FetchCandidate;
|
||||
|
||||
/// Fetch extrinsic data for a specific candidate.
|
||||
fn fetch_extrinsic_data(&self, candidate: &CandidateReceipt) -> Self::FetchExtrinsic;
|
||||
}
|
||||
|
||||
/// A long-lived network which can create statement table routing instances.
|
||||
pub trait Network {
|
||||
/// The table router type. This should handle importing of any statements,
|
||||
/// routing statements to peers, and driving completion of any `StatementProducers`.
|
||||
type TableRouter: TableRouter;
|
||||
|
||||
/// Instantiate a table router using the given shared table.
|
||||
fn table_router(&self, table: Arc<SharedTable>) -> Self::TableRouter;
|
||||
}
|
||||
|
||||
/// Information about a specific group.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct GroupInfo {
|
||||
/// Authorities meant to check validity of candidates.
|
||||
pub validity_guarantors: HashSet<AuthorityId>,
|
||||
@@ -89,6 +131,10 @@ impl table::Context for TableContext {
|
||||
}
|
||||
|
||||
impl TableContext {
|
||||
fn local_id(&self) -> AuthorityId {
|
||||
self.key.public().0
|
||||
}
|
||||
|
||||
fn sign_statement(&self, statement: table::Statement) -> table::SignedStatement {
|
||||
let signature = sign_table_statement(&statement, &self.key, &self.parent_hash);
|
||||
let local_id = self.key.public().0;
|
||||
@@ -124,16 +170,120 @@ pub fn sign_table_statement(statement: &table::Statement, key: &ed25519::Pair, p
|
||||
struct SharedTableInner {
|
||||
table: Table<TableContext>,
|
||||
proposed_digest: Option<Hash>,
|
||||
checked_validity: HashSet<Hash>,
|
||||
checked_availability: HashSet<Hash>,
|
||||
}
|
||||
|
||||
impl SharedTableInner {
|
||||
fn import_statement(
|
||||
// Import a single statement. Provide a handle to a table router.
|
||||
fn import_statement<R: TableRouter>(
|
||||
&mut self,
|
||||
context: &TableContext,
|
||||
statement: ::table::SignedStatement,
|
||||
router: &R,
|
||||
statement: table::SignedStatement,
|
||||
received_from: Option<AuthorityId>,
|
||||
) -> Option<table::Summary> {
|
||||
self.table.import_statement(context, statement, received_from)
|
||||
) -> StatementProducer<<R::FetchCandidate as IntoFuture>::Future, <R::FetchExtrinsic as IntoFuture>::Future> {
|
||||
let mut producer = StatementProducer {
|
||||
fetch_block_data: None,
|
||||
fetch_extrinsic: None,
|
||||
produced_statements: Default::default(),
|
||||
_key: context.key.clone(),
|
||||
};
|
||||
|
||||
let summary = match self.table.import_statement(context, statement, received_from) {
|
||||
Some(summary) => summary,
|
||||
None => return producer,
|
||||
};
|
||||
|
||||
let local_id = context.local_id();
|
||||
let is_validity_member = context.is_member_of(&local_id, &summary.group_id);
|
||||
let is_availability_member =
|
||||
context.is_availability_guarantor_of(&local_id, &summary.group_id);
|
||||
|
||||
let digest = &summary.candidate;
|
||||
|
||||
// TODO: consider a strategy based on the number of candidate votes as well.
|
||||
// only check validity if this wasn't locally proposed.
|
||||
let checking_validity = is_validity_member
|
||||
&& self.proposed_digest.as_ref().map_or(true, |d| d != digest)
|
||||
&& self.checked_validity.insert(digest.clone());
|
||||
|
||||
let checking_availability = is_availability_member && self.checked_availability.insert(digest.clone());
|
||||
|
||||
if checking_validity || checking_availability {
|
||||
match self.table.get_candidate(&digest) {
|
||||
None => {} // TODO: handle table inconsistency somehow?
|
||||
Some(candidate) => {
|
||||
if checking_validity {
|
||||
producer.fetch_block_data = Some(router.fetch_block_data(candidate).into_future().fuse());
|
||||
}
|
||||
|
||||
if checking_availability {
|
||||
producer.fetch_extrinsic = Some(router.fetch_extrinsic_data(candidate).into_future().fuse());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
producer
|
||||
}
|
||||
}
|
||||
|
||||
/// Produced statements about a specific candidate.
|
||||
/// Both may be `None`.
|
||||
#[derive(Default)]
|
||||
pub struct ProducedStatements {
|
||||
/// A statement about the validity of the candidate.
|
||||
pub validity: Option<table::Statement>,
|
||||
/// A statement about the availability of the candidate.
|
||||
pub availability: Option<table::Statement>,
|
||||
}
|
||||
|
||||
/// Future that produces statements about a specific candidate.
|
||||
pub struct StatementProducer<D: Future, E: Future> {
|
||||
fetch_block_data: Option<future::Fuse<D>>,
|
||||
fetch_extrinsic: Option<future::Fuse<E>>,
|
||||
produced_statements: ProducedStatements,
|
||||
_key: Arc<ed25519::Pair>,
|
||||
}
|
||||
|
||||
impl<D, E, Err> Future for StatementProducer<D, E>
|
||||
where
|
||||
D: Future<Item=BlockData,Error=Err>,
|
||||
E: Future<Item=Extrinsic,Error=Err>,
|
||||
{
|
||||
type Item = ProducedStatements;
|
||||
type Error = Err;
|
||||
|
||||
fn poll(&mut self) -> Poll<ProducedStatements, Err> {
|
||||
let mut done = true;
|
||||
if let Some(ref mut fetch_block_data) = self.fetch_block_data {
|
||||
match fetch_block_data.poll()? {
|
||||
Async::Ready(_block_data) => {
|
||||
// TODO [PoC-2] : validate block data here and make statement.
|
||||
},
|
||||
Async::NotReady => {
|
||||
done = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref mut fetch_extrinsic) = self.fetch_extrinsic {
|
||||
match fetch_extrinsic.poll()? {
|
||||
Async::Ready(_extrinsic) => {
|
||||
// TODO [PoC-2]: guarantee availability of data and make statment.
|
||||
}
|
||||
Async::NotReady => {
|
||||
done = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if done {
|
||||
Ok(Async::Ready(::std::mem::replace(&mut self.produced_statements, Default::default())))
|
||||
} else {
|
||||
Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +297,7 @@ impl Clone for SharedTable {
|
||||
fn clone(&self) -> Self {
|
||||
SharedTable {
|
||||
context: self.context.clone(),
|
||||
inner: self.inner.clone()
|
||||
inner: self.inner.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,24 +313,34 @@ impl SharedTable {
|
||||
inner: Arc::new(Mutex::new(SharedTableInner {
|
||||
table: Table::default(),
|
||||
proposed_digest: None,
|
||||
checked_validity: HashSet::new(),
|
||||
checked_availability: HashSet::new(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Import a single statement.
|
||||
pub fn import_statement(
|
||||
/// Get group info.
|
||||
pub fn group_info(&self) -> &HashMap<ParaId, GroupInfo> {
|
||||
&self.context.groups
|
||||
}
|
||||
|
||||
/// Import a single statement. Provide a handle to a table router
|
||||
/// for dispatching any other requests which come up.
|
||||
pub fn import_statement<R: TableRouter>(
|
||||
&self,
|
||||
router: &R,
|
||||
statement: table::SignedStatement,
|
||||
received_from: Option<AuthorityId>,
|
||||
) -> Option<table::Summary> {
|
||||
self.inner.lock().import_statement(&*self.context, statement, received_from)
|
||||
) -> StatementProducer<<R::FetchCandidate as IntoFuture>::Future, <R::FetchExtrinsic as IntoFuture>::Future> {
|
||||
self.inner.lock().import_statement(&*self.context, router, statement, received_from)
|
||||
}
|
||||
|
||||
/// Sign and import a local statement.
|
||||
pub fn sign_and_import(
|
||||
pub fn sign_and_import<R: TableRouter>(
|
||||
&self,
|
||||
router: &R,
|
||||
statement: table::Statement,
|
||||
) -> Option<table::Summary> {
|
||||
) -> StatementProducer<<R::FetchCandidate as IntoFuture>::Future, <R::FetchExtrinsic as IntoFuture>::Future> {
|
||||
let proposed_digest = match statement {
|
||||
GenericStatement::Candidate(ref c) => Some(c.hash()),
|
||||
_ => None,
|
||||
@@ -193,21 +353,25 @@ impl SharedTable {
|
||||
inner.proposed_digest = proposed_digest;
|
||||
}
|
||||
|
||||
inner.import_statement(&*self.context, signed_statement, None)
|
||||
inner.import_statement(&*self.context, router, signed_statement, None)
|
||||
}
|
||||
|
||||
/// Import many statements at once.
|
||||
///
|
||||
/// Provide an iterator yielding pairs of (statement, received_from).
|
||||
pub fn import_statements<I, U>(&self, iterable: I) -> U
|
||||
pub fn import_statements<R, I, U>(&self, router: &R, iterable: I) -> U
|
||||
where
|
||||
R: TableRouter,
|
||||
I: IntoIterator<Item=(table::SignedStatement, Option<AuthorityId>)>,
|
||||
U: ::std::iter::FromIterator<table::Summary>,
|
||||
U: ::std::iter::FromIterator<StatementProducer<
|
||||
<R::FetchCandidate as IntoFuture>::Future,
|
||||
<R::FetchExtrinsic as IntoFuture>::Future>
|
||||
>,
|
||||
{
|
||||
let mut inner = self.inner.lock();
|
||||
|
||||
iterable.into_iter().filter_map(move |(statement, received_from)| {
|
||||
inner.import_statement(&*self.context, statement, received_from)
|
||||
iterable.into_iter().map(move |(statement, received_from)| {
|
||||
inner.import_statement(&*self.context, router, statement, received_from)
|
||||
}).collect()
|
||||
}
|
||||
|
||||
@@ -241,3 +405,152 @@ impl SharedTable {
|
||||
self.inner.lock().proposed_digest.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn make_group_info(roster: DutyRoster, authorities: &[AuthorityId]) -> Result<HashMap<ParaId, GroupInfo>, Error> {
|
||||
if roster.validator_duty.len() != authorities.len() {
|
||||
bail!(ErrorKind::InvalidDutyRosterLength(authorities.len(), roster.validator_duty.len()))
|
||||
}
|
||||
|
||||
if roster.guarantor_duty.len() != authorities.len() {
|
||||
bail!(ErrorKind::InvalidDutyRosterLength(authorities.len(), roster.guarantor_duty.len()))
|
||||
}
|
||||
|
||||
let mut map = HashMap::new();
|
||||
|
||||
let duty_iter = authorities.iter().zip(&roster.validator_duty).zip(&roster.guarantor_duty);
|
||||
for ((authority, v_duty), a_duty) in duty_iter {
|
||||
use polkadot_primitives::parachain::Chain;
|
||||
|
||||
match *v_duty {
|
||||
Chain::Relay => {}, // does nothing for now.
|
||||
Chain::Parachain(ref id) => {
|
||||
map.entry(id.clone()).or_insert_with(GroupInfo::default)
|
||||
.validity_guarantors
|
||||
.insert(authority.clone());
|
||||
}
|
||||
}
|
||||
|
||||
match *a_duty {
|
||||
Chain::Relay => {}, // does nothing for now.
|
||||
Chain::Parachain(ref id) => {
|
||||
map.entry(id.clone()).or_insert_with(GroupInfo::default)
|
||||
.availability_guarantors
|
||||
.insert(authority.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for live_group in map.values_mut() {
|
||||
let validity_len = live_group.validity_guarantors.len();
|
||||
let availability_len = live_group.availability_guarantors.len();
|
||||
|
||||
live_group.needed_validity = validity_len / 2 + validity_len % 2;
|
||||
live_group.needed_availability = availability_len / 2 + availability_len % 2;
|
||||
}
|
||||
|
||||
Ok(map)
|
||||
}
|
||||
|
||||
/// Polkadot proposer factory.
|
||||
pub struct ProposerFactory<C, N> {
|
||||
/// The client instance.
|
||||
pub client: Arc<C>,
|
||||
/// The backing network handle.
|
||||
pub network: N,
|
||||
}
|
||||
|
||||
impl<C: PolkadotApi, N: Network> bft::ProposerFactory for ProposerFactory<C, N> {
|
||||
type Proposer = Proposer<C, N::TableRouter>;
|
||||
type Error = Error;
|
||||
|
||||
fn init(&self, parent_header: &SubstrateHeader, authorities: &[AuthorityId], sign_with: Arc<ed25519::Pair>) -> Result<Self::Proposer, Error> {
|
||||
let parent_hash = parent_header.hash();
|
||||
let duty_roster = self.client.duty_roster(&BlockId::Hash(parent_hash))?;
|
||||
|
||||
let group_info = make_group_info(duty_roster, authorities)?;
|
||||
let table = Arc::new(SharedTable::new(group_info, sign_with, parent_hash));
|
||||
let router = self.network.table_router(table.clone());
|
||||
|
||||
// TODO [PoC-2]: kick off collation process.
|
||||
Ok(Proposer {
|
||||
parent_hash,
|
||||
_table: table,
|
||||
_router: router,
|
||||
client: self.client.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn current_timestamp() -> Timestamp {
|
||||
use std::time;
|
||||
|
||||
time::SystemTime::now().duration_since(time::UNIX_EPOCH)
|
||||
.expect("now always later than unix epoch; qed")
|
||||
.as_secs()
|
||||
}
|
||||
|
||||
/// The Polkadot proposer logic.
|
||||
pub struct Proposer<C, R> {
|
||||
parent_hash: HeaderHash,
|
||||
client: Arc<C>,
|
||||
_table: Arc<SharedTable>,
|
||||
_router: R,
|
||||
}
|
||||
|
||||
impl<C: PolkadotApi, R: TableRouter> bft::Proposer for Proposer<C, R> {
|
||||
type Error = Error;
|
||||
type Create = Result<SubstrateBlock, Error>;
|
||||
type Evaluate = Result<bool, Error>;
|
||||
|
||||
fn propose(&self) -> Result<SubstrateBlock, Error> {
|
||||
// TODO: handle case when current timestamp behind that in state.
|
||||
let polkadot_block = self.client.build_block(
|
||||
&BlockId::Hash(self.parent_hash),
|
||||
current_timestamp()
|
||||
)?.bake();
|
||||
|
||||
// TODO: integrate transaction queue and `push_transaction`s.
|
||||
|
||||
let substrate_block = Slicable::decode(&mut polkadot_block.encode().as_slice())
|
||||
.expect("polkadot blocks defined to serialize to substrate blocks correctly; qed");
|
||||
|
||||
Ok(substrate_block)
|
||||
}
|
||||
|
||||
// TODO: certain kinds of errors here should lead to a misbehavior report.
|
||||
fn evaluate(&self, proposal: &SubstrateBlock) -> Result<bool, Error> {
|
||||
evaluate_proposal(proposal, &*self.client, current_timestamp(), &self.parent_hash)
|
||||
}
|
||||
}
|
||||
|
||||
fn evaluate_proposal<C: PolkadotApi>(
|
||||
proposal: &SubstrateBlock,
|
||||
client: &C,
|
||||
now: Timestamp,
|
||||
parent_hash: &HeaderHash,
|
||||
) -> Result<bool, Error> {
|
||||
const MAX_TIMESTAMP_DRIFT: Timestamp = 4;
|
||||
|
||||
let encoded = Slicable::encode(proposal);
|
||||
let proposal = PolkadotBlock::decode(&mut &encoded[..])
|
||||
.ok_or_else(|| ErrorKind::ProposalNotForPolkadot)?;
|
||||
|
||||
if proposal.header.parent_hash != *parent_hash {
|
||||
bail!(ErrorKind::WrongParentHash(*parent_hash, proposal.header.parent_hash));
|
||||
}
|
||||
|
||||
// no need to check number because
|
||||
// a) we assume the parent is valid.
|
||||
// b) the runtime checks that `proposal.parent_hash` == `block_hash(proposal.number - 1)`
|
||||
|
||||
let block_timestamp = proposal.body.timestamp;
|
||||
|
||||
// TODO: just defer using `tokio_timer` to delay prepare vote.
|
||||
if block_timestamp > now + MAX_TIMESTAMP_DRIFT {
|
||||
bail!(ErrorKind::TimestampInFuture)
|
||||
}
|
||||
|
||||
// execute the block.
|
||||
client.evaluate_block(&BlockId::Hash(*parent_hash), proposal)?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
@@ -44,8 +44,10 @@ mod tests {
|
||||
use polkadot_runtime::runtime::staking::balance;
|
||||
use state_machine::{CodeExecutor, TestExternalities};
|
||||
use primitives::twox_128;
|
||||
use polkadot_primitives::{Hash, Header, BlockNumber, Block, Digest, Transaction,
|
||||
UncheckedTransaction, Function};
|
||||
use polkadot_primitives::{
|
||||
Hash, Header, Body, BlockNumber, Block, Digest, Transaction,
|
||||
UncheckedTransaction, Function, InherentFunction,
|
||||
};
|
||||
use ed25519::{Public, Pair};
|
||||
|
||||
const BLOATY_CODE: &[u8] = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm");
|
||||
@@ -58,76 +60,6 @@ mod tests {
|
||||
)
|
||||
}
|
||||
|
||||
fn tx() -> UncheckedTransaction {
|
||||
let transaction = Transaction {
|
||||
signed: Keyring::One.to_raw_public(),
|
||||
nonce: 0,
|
||||
function: Function::StakingTransfer(Keyring::Two.to_raw_public(), 69),
|
||||
};
|
||||
let signature = Keyring::from_raw_public(transaction.signed).unwrap()
|
||||
.sign(&transaction.encode());
|
||||
|
||||
UncheckedTransaction { transaction, signature }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panic_execution_with_foreign_code_gives_error() {
|
||||
let one = Keyring::One.to_raw_public();
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
];
|
||||
|
||||
let r = Executor::new().call(&mut t, BLOATY_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx()));
|
||||
assert!(r.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panic_execution_with_native_equivalent_code_gives_error() {
|
||||
let one = Keyring::One.to_raw_public();
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
];
|
||||
|
||||
let r = Executor::new().call(&mut t, COMPACT_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx()));
|
||||
assert!(r.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn successful_execution_with_native_equivalent_code_gives_ok() {
|
||||
let one = Keyring::One.to_raw_public();
|
||||
let two = Keyring::Two.to_raw_public();
|
||||
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
];
|
||||
|
||||
let r = Executor::new().call(&mut t, COMPACT_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx()));
|
||||
assert!(r.is_ok());
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(balance(&one), 42);
|
||||
assert_eq!(balance(&two), 69);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn successful_execution_with_foreign_code_gives_ok() {
|
||||
let one = Keyring::One.to_raw_public();
|
||||
let two = Keyring::Two.to_raw_public();
|
||||
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
];
|
||||
|
||||
let r = Executor::new().call(&mut t, BLOATY_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx()));
|
||||
assert!(r.is_ok());
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(balance(&one), 42);
|
||||
assert_eq!(balance(&two), 69);
|
||||
});
|
||||
}
|
||||
|
||||
fn new_test_ext() -> TestExternalities {
|
||||
let one = Keyring::One.to_raw_public();
|
||||
let two = Keyring::Two.to_raw_public();
|
||||
@@ -152,40 +84,77 @@ mod tests {
|
||||
]
|
||||
}
|
||||
|
||||
fn construct_block(number: BlockNumber, parent_hash: Hash, state_root: Hash, txs: Vec<Transaction>) -> (Vec<u8>, Hash) {
|
||||
fn set_timestamp(timestamp: u64) -> UncheckedTransaction {
|
||||
UncheckedTransaction::inherent(InherentFunction::TimestampSet(timestamp))
|
||||
}
|
||||
|
||||
fn tx() -> UncheckedTransaction {
|
||||
let transaction = Transaction {
|
||||
signed: Keyring::One.to_raw_public(),
|
||||
nonce: 0,
|
||||
function: Function::StakingTransfer(Keyring::Two.to_raw_public(), 69),
|
||||
};
|
||||
let signature = Keyring::from_raw_public(transaction.signed).unwrap()
|
||||
.sign(&transaction.encode());
|
||||
|
||||
UncheckedTransaction { transaction, signature }
|
||||
}
|
||||
|
||||
fn execute_tx_on<C>(executor: C, ext: &mut TestExternalities, code: &[u8], tx: UncheckedTransaction, header: Header)
|
||||
-> Result<Vec<u8>, C::Error>
|
||||
where C: CodeExecutor
|
||||
{
|
||||
let next_header = executor.call(ext, code, "execute_transaction", &vec![].and(&header).and(&set_timestamp(100_000))).unwrap();
|
||||
let next_input = next_header.and(&tx);
|
||||
|
||||
executor.call(ext, code, "execute_transaction", &next_input[..])
|
||||
}
|
||||
|
||||
fn construct_block(number: BlockNumber, parent_hash: Hash, state_root: Hash, timestamp: u64, txs: Vec<Transaction>) -> (Vec<u8>, Hash) {
|
||||
use triehash::ordered_trie_root;
|
||||
|
||||
|
||||
let transactions = txs.into_iter().map(|transaction| {
|
||||
let signature = Pair::from(Keyring::from_public(Public::from_raw(transaction.signed)).unwrap())
|
||||
.sign(&transaction.encode());
|
||||
|
||||
UncheckedTransaction { transaction, signature }
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
let transaction_root = ordered_trie_root(transactions.iter().map(Slicable::encode)).0.into();
|
||||
}).collect();
|
||||
|
||||
let header = Header {
|
||||
parent_hash,
|
||||
number,
|
||||
state_root,
|
||||
transaction_root,
|
||||
transaction_root: Default::default(),
|
||||
digest: Digest { logs: vec![], },
|
||||
};
|
||||
let hash = header.blake2_256();
|
||||
|
||||
(Block { header, transactions }.encode(), hash.into())
|
||||
let mut block = Block {
|
||||
header,
|
||||
body: Body { timestamp, transactions },
|
||||
};
|
||||
|
||||
let transaction_root = ordered_trie_root(block.all_transactions().map(|tx| Slicable::encode(&tx))).0.into();
|
||||
block.header.transaction_root = transaction_root;
|
||||
|
||||
let hash = block.header.blake2_256();
|
||||
|
||||
(block.encode(), hash.into())
|
||||
}
|
||||
|
||||
fn block1() -> (Vec<u8>, Hash) {
|
||||
construct_block(
|
||||
1,
|
||||
[69u8; 32].into(),
|
||||
hex!("2481853da20b9f4322f34650fea5f240dcbfb266d02db94bfa0153c31f4a29db").into(),
|
||||
vec![Transaction {
|
||||
hex!("3df569d47a0d7f4a448486f04fba4eea3e9dfca001319c609f88b3a67b0dd1ea").into(),
|
||||
100_000,
|
||||
vec![
|
||||
Transaction {
|
||||
signed: Keyring::One.to_raw_public(),
|
||||
nonce: 0,
|
||||
function: Function::StakingTransfer(Keyring::Two.to_raw_public(), 69),
|
||||
}]
|
||||
}
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
@@ -193,7 +162,8 @@ mod tests {
|
||||
construct_block(
|
||||
2,
|
||||
block1().1,
|
||||
hex!("1feb4d3a2e587079e6ce1685fa79994efd995e33cb289d39cded67aac1bb46a9").into(),
|
||||
hex!("c8776c92e8012bf6b3f206448eda3f00bca26d77f220f4714c81cbc92a30e1e2").into(),
|
||||
200_000,
|
||||
vec![
|
||||
Transaction {
|
||||
signed: Keyring::Two.to_raw_public(),
|
||||
@@ -209,6 +179,64 @@ mod tests {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panic_execution_with_foreign_code_gives_error() {
|
||||
let one = Keyring::One.to_raw_public();
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
];
|
||||
|
||||
let r = execute_tx_on(Executor::new(), &mut t, BLOATY_CODE, tx(), Header::from_block_number(1));
|
||||
assert!(r.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panic_execution_with_native_equivalent_code_gives_error() {
|
||||
let one = Keyring::One.to_raw_public();
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
];
|
||||
|
||||
let r = execute_tx_on(Executor::new(), &mut t, COMPACT_CODE, tx(), Header::from_block_number(1));
|
||||
assert!(r.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn successful_execution_with_native_equivalent_code_gives_ok() {
|
||||
let one = Keyring::One.to_raw_public();
|
||||
let two = Keyring::Two.to_raw_public();
|
||||
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
];
|
||||
|
||||
let r = execute_tx_on(Executor::new(), &mut t, COMPACT_CODE, tx(), Header::from_block_number(1));
|
||||
assert!(r.is_ok());
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(balance(&one), 42);
|
||||
assert_eq!(balance(&two), 69);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn successful_execution_with_foreign_code_gives_ok() {
|
||||
let one = Keyring::One.to_raw_public();
|
||||
let two = Keyring::Two.to_raw_public();
|
||||
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
];
|
||||
|
||||
let r = execute_tx_on(Executor::new(), &mut t, BLOATY_CODE, tx(), Header::from_block_number(1));
|
||||
assert!(r.is_ok());
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(balance(&one), 42);
|
||||
assert_eq!(balance(&two), 69);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_native_block_import_works() {
|
||||
let mut t = new_test_ext();
|
||||
@@ -255,7 +283,7 @@ mod tests {
|
||||
];
|
||||
|
||||
let foreign_code = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm");
|
||||
let r = WasmExecutor.call(&mut t, &foreign_code[..], "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx()));
|
||||
let r = execute_tx_on(WasmExecutor, &mut t, &foreign_code[..], tx(), Header::from_block_number(1));
|
||||
assert!(r.is_err());
|
||||
}
|
||||
|
||||
@@ -269,7 +297,7 @@ mod tests {
|
||||
];
|
||||
|
||||
let foreign_code = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm");
|
||||
let r = WasmExecutor.call(&mut t, &foreign_code[..], "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx()));
|
||||
let r = execute_tx_on(WasmExecutor, &mut t, &foreign_code[..], tx(), Header::from_block_number(1));
|
||||
assert!(r.is_ok());
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
|
||||
@@ -21,7 +21,7 @@ use primitives::bytes;
|
||||
use primitives::H256;
|
||||
use rstd::vec::Vec;
|
||||
use codec::{Input, Slicable};
|
||||
use transaction::UncheckedTransaction;
|
||||
use transaction::{UncheckedTransaction, Function, InherentFunction};
|
||||
|
||||
pub use primitives::block::Id;
|
||||
|
||||
@@ -69,8 +69,65 @@ impl Slicable for Digest {
|
||||
}
|
||||
}
|
||||
|
||||
/// The block "body": A bunch of transactions.
|
||||
pub type Body = Vec<UncheckedTransaction>;
|
||||
/// Iterator over all inherent transactions.
|
||||
pub struct InherentTransactions<'a> {
|
||||
number: u64,
|
||||
body: &'a Body,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for InherentTransactions<'a> {
|
||||
type Item = UncheckedTransaction;
|
||||
|
||||
fn next(&mut self) -> Option<UncheckedTransaction> {
|
||||
if self.number == InherentFunction::count() {
|
||||
return None
|
||||
}
|
||||
|
||||
self.number += 1;
|
||||
|
||||
let function = match self.number {
|
||||
1 => Some(InherentFunction::TimestampSet(self.body.timestamp)),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
function.map(UncheckedTransaction::inherent)
|
||||
}
|
||||
}
|
||||
|
||||
/// Type alias for an iterator over all transactions in a block.
|
||||
pub type AllTransactions<'a> = ::rstd::iter::Chain<
|
||||
InherentTransactions<'a>,
|
||||
::rstd::iter::Cloned<::rstd::slice::Iter<'a, UncheckedTransaction>>,
|
||||
>;
|
||||
|
||||
/// The block body. Contains timestamp and transactions.
|
||||
// TODO: add candidates update as well.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
#[cfg_attr(feature = "std", serde(deny_unknown_fields))]
|
||||
pub struct Body {
|
||||
/// The timestamp of the block.
|
||||
pub timestamp: u64,
|
||||
/// The transactions in the block.
|
||||
pub transactions: Vec<UncheckedTransaction>,
|
||||
}
|
||||
|
||||
impl Body {
|
||||
/// Get an iterator over all inherent transactions of the body.
|
||||
pub fn inherent_transactions(&self) -> InherentTransactions {
|
||||
InherentTransactions {
|
||||
number: 0,
|
||||
body: self,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get an iterator over all transactions in a block.
|
||||
pub fn all_transactions(&self) -> AllTransactions {
|
||||
self.inherent_transactions().chain(self.transactions.iter().cloned())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// A Polkadot relay chain block.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
@@ -78,21 +135,65 @@ pub type Body = Vec<UncheckedTransaction>;
|
||||
pub struct Block {
|
||||
/// The block header.
|
||||
pub header: Header,
|
||||
/// All relay-chain transactions.
|
||||
pub transactions: Body,
|
||||
/// The block body.
|
||||
pub body: Body,
|
||||
}
|
||||
|
||||
impl Block {
|
||||
/// Get an iterator over all inherent transactions of the body.
|
||||
pub fn inherent_transactions(&self) -> InherentTransactions {
|
||||
self.body.inherent_transactions()
|
||||
}
|
||||
|
||||
/// Get an iterator over all transactions in a block.
|
||||
pub fn all_transactions(&self) -> AllTransactions {
|
||||
self.body.all_transactions()
|
||||
}
|
||||
}
|
||||
|
||||
impl Slicable for Block {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
let (header, transactions) = try_opt!(Slicable::decode(input));
|
||||
Some(Block { header, transactions })
|
||||
let header = try_opt!(Slicable::decode(input));
|
||||
|
||||
let transactions_len: u32 = try_opt!(Slicable::decode(input));
|
||||
let regular_transactions_len = try_opt!(transactions_len.checked_sub(InherentFunction::count() as u32));
|
||||
|
||||
let timestamp_tx = try_opt!(UncheckedTransaction::decode(input));
|
||||
let timestamp = match timestamp_tx.transaction.function {
|
||||
Function::Inherent(InherentFunction::TimestampSet(ref t)) if timestamp_tx.is_well_formed() => { t.clone() }
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let transactions: Option<Vec<_>> = (0..regular_transactions_len)
|
||||
.map(|_| UncheckedTransaction::decode(input))
|
||||
.filter(|tx| tx.as_ref().map_or(true, |tx| tx.is_well_formed()))
|
||||
.collect();
|
||||
|
||||
let body = Body {
|
||||
timestamp,
|
||||
transactions: try_opt!(transactions),
|
||||
};
|
||||
|
||||
Some(Block { header, body })
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
|
||||
v.extend(self.header.encode());
|
||||
v.extend(self.transactions.encode());
|
||||
|
||||
// encode inherent transactions before non-inherent.
|
||||
let transactions_len = self.body.transactions.len() as u64 + InherentFunction::count();
|
||||
(transactions_len as u32).using_encoded(|s| v.extend(s));
|
||||
|
||||
let timestamp_set_tx = UncheckedTransaction::inherent(
|
||||
InherentFunction::TimestampSet(self.body.timestamp)
|
||||
);
|
||||
|
||||
v.extend(timestamp_set_tx.encode());
|
||||
for non_inherent_transaction in &self.body.transactions {
|
||||
v.extend(non_inherent_transaction.encode());
|
||||
}
|
||||
|
||||
v
|
||||
}
|
||||
@@ -186,4 +287,89 @@ mod tests {
|
||||
let v = header.encode();
|
||||
assert_eq!(Header::decode(&mut &v[..]).unwrap(), header);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_encoding_round_trip() {
|
||||
let mut block = Block {
|
||||
header: Header::from_block_number(1),
|
||||
body: Body {
|
||||
timestamp: 100_000_000,
|
||||
transactions: Vec::new(),
|
||||
}
|
||||
};
|
||||
|
||||
let raw = block.encode();
|
||||
let decoded = Block::decode(&mut &raw[..]).unwrap();
|
||||
|
||||
assert_eq!(block, decoded);
|
||||
|
||||
block.body.transactions.push(UncheckedTransaction {
|
||||
transaction: ::transaction::Transaction {
|
||||
function: Function::StakingStake,
|
||||
signed: Default::default(),
|
||||
nonce: 10101,
|
||||
},
|
||||
signature: Default::default(),
|
||||
});
|
||||
|
||||
let raw = block.encode();
|
||||
let decoded = Block::decode(&mut &raw[..]).unwrap();
|
||||
|
||||
assert_eq!(block, decoded);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_encoding_substrate_round_trip() {
|
||||
let mut block = Block {
|
||||
header: Header::from_block_number(1),
|
||||
body: Body {
|
||||
timestamp: 100_000_000,
|
||||
transactions: Vec::new(),
|
||||
}
|
||||
};
|
||||
|
||||
block.body.transactions.push(UncheckedTransaction {
|
||||
transaction: ::transaction::Transaction {
|
||||
function: Function::StakingStake,
|
||||
signed: Default::default(),
|
||||
nonce: 10101,
|
||||
},
|
||||
signature: Default::default(),
|
||||
});
|
||||
|
||||
let raw = block.encode();
|
||||
let decoded_substrate = ::primitives::block::Block::decode(&mut &raw[..]).unwrap();
|
||||
let encoded_substrate = decoded_substrate.encode();
|
||||
let decoded = Block::decode(&mut &encoded_substrate[..]).unwrap();
|
||||
|
||||
assert_eq!(block, decoded);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_body_without_inherents_fails() {
|
||||
let substrate_blank = ::primitives::block::Block {
|
||||
header: ::primitives::block::Header::from_block_number(1),
|
||||
transactions: Vec::new(),
|
||||
};
|
||||
|
||||
let encoded_substrate = substrate_blank.encode();
|
||||
assert!(Block::decode(&mut &encoded_substrate[..]).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inherent_transactions_iter_contains_all_inherent() {
|
||||
let block = Block {
|
||||
header: Header::from_block_number(1),
|
||||
body: Body {
|
||||
timestamp: 10101,
|
||||
transactions: Vec::new(),
|
||||
}
|
||||
};
|
||||
|
||||
let mut iter = block.inherent_transactions();
|
||||
|
||||
assert_eq!(InherentFunction::count(), 1); // following depends on this assertion.
|
||||
assert_eq!(iter.next().unwrap(), UncheckedTransaction::inherent(InherentFunction::TimestampSet(10101)));
|
||||
assert!(iter.next().is_none());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,9 +49,9 @@ pub mod validator;
|
||||
pub mod block;
|
||||
pub mod transaction;
|
||||
|
||||
pub use self::block::{Header, Block, Log, Digest};
|
||||
pub use self::block::{Header, Body, Block, Log, Digest};
|
||||
pub use self::block::Number as BlockNumber;
|
||||
pub use self::transaction::{Transaction, UncheckedTransaction, Function, Proposal};
|
||||
pub use self::transaction::{Transaction, UncheckedTransaction, Function, InherentFunction, Proposal};
|
||||
|
||||
/// Virtual account ID that represents the idea of a dispatch/statement being signed by everybody
|
||||
/// (who matters). Essentially this means that a majority of validators have decided it is
|
||||
@@ -75,5 +75,8 @@ pub type TxOrder = u64;
|
||||
/// A hash of some data used by the relay chain.
|
||||
pub type Hash = primitives::H256;
|
||||
|
||||
/// Alias to 520-bit hash when used in the context of a signature on the relay chain.
|
||||
/// Alias to 512-bit hash when used in the context of a signature on the relay chain.
|
||||
pub type Signature = primitives::hash::H512;
|
||||
|
||||
/// A timestamp: seconds since the unix epoch.
|
||||
pub type Timestamp = u64;
|
||||
|
||||
@@ -121,6 +121,13 @@ impl Slicable for DutyRoster {
|
||||
}
|
||||
}
|
||||
|
||||
/// Extrinsic data for a parachain.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
#[cfg_attr(feature = "std", serde(deny_unknown_fields))]
|
||||
pub struct Extrinsic;
|
||||
|
||||
/// Candidate parachain block.
|
||||
///
|
||||
/// https://github.com/w3f/polkadot-spec/blob/master/spec.md#candidate-para-chain-block
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
use rstd::vec::Vec;
|
||||
use codec::{Input, Slicable};
|
||||
use ::Signature;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::fmt;
|
||||
@@ -184,12 +185,35 @@ impl FunctionId {
|
||||
}
|
||||
}
|
||||
|
||||
/// Inherent functions on the runtime.
|
||||
/// These must be called each block by the `EVERYBODY` account.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub enum InherentFunction {
|
||||
/// Set the timestamp.
|
||||
TimestampSet(u64),
|
||||
}
|
||||
|
||||
impl InherentFunction {
|
||||
/// Get the number of inherent functions.
|
||||
pub fn count() -> u64 {
|
||||
1
|
||||
}
|
||||
|
||||
/// Get the index.
|
||||
pub fn index(&self) -> u64 {
|
||||
match *self {
|
||||
InherentFunction::TimestampSet(_) => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Functions on the runtime.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub enum Function {
|
||||
/// Set the timestamp.
|
||||
TimestampSet(u64),
|
||||
/// An inherent function.
|
||||
Inherent(InherentFunction),
|
||||
/// Set temporary session key as a validator.
|
||||
SessionSetKey(::SessionKey),
|
||||
/// Staking subsystem: begin staking.
|
||||
@@ -204,12 +228,34 @@ pub enum Function {
|
||||
GovernanceApprove(BlockNumber),
|
||||
}
|
||||
|
||||
impl Function {
|
||||
/// The number of inherent functions.
|
||||
pub fn inherent_functions() -> u64 { InherentFunction::count() }
|
||||
|
||||
/// Whether this function is "inherent": that it must be part of every
|
||||
/// block at the given index and no other.
|
||||
///
|
||||
/// Transactions containing inherent functions should not be signed.
|
||||
pub fn is_inherent(&self) -> bool {
|
||||
self.inherent_index().is_some()
|
||||
}
|
||||
|
||||
/// If this function is inherent, returns the index it should occupy
|
||||
/// in the block. Otherwise returns `None`.
|
||||
pub fn inherent_index(&self) -> Option<u64> {
|
||||
match *self {
|
||||
Function::Inherent(ref inner) => Some(inner.index()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Slicable for Function {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
let id = try_opt!(u8::decode(input).and_then(FunctionId::from_u8));
|
||||
Some(match id {
|
||||
FunctionId::TimestampSet =>
|
||||
Function::TimestampSet(try_opt!(Slicable::decode(input))),
|
||||
Function::Inherent(InherentFunction::TimestampSet(try_opt!(Slicable::decode(input)))),
|
||||
FunctionId::SessionSetKey =>
|
||||
Function::SessionSetKey(try_opt!(Slicable::decode(input))),
|
||||
FunctionId::StakingStake => Function::StakingStake,
|
||||
@@ -230,7 +276,7 @@ impl Slicable for Function {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
match *self {
|
||||
Function::TimestampSet(ref data) => {
|
||||
Function::Inherent(InherentFunction::TimestampSet(ref data)) => {
|
||||
(FunctionId::TimestampSet as u8).using_encoded(|s| v.extend(s));
|
||||
data.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
@@ -308,7 +354,33 @@ pub struct UncheckedTransaction {
|
||||
/// The actual transaction information.
|
||||
pub transaction: Transaction,
|
||||
/// The signature; should be an Ed25519 signature applied to the serialised `transaction` field.
|
||||
pub signature: super::Signature,
|
||||
pub signature: Signature,
|
||||
}
|
||||
|
||||
impl UncheckedTransaction {
|
||||
/// Whether the transaction is well-formed. In particular checks that
|
||||
/// inherent transactions have the correct signed and signature fields.
|
||||
///
|
||||
/// Does not check signatures on other transactions.
|
||||
pub fn is_well_formed(&self) -> bool {
|
||||
if self.transaction.function.is_inherent() {
|
||||
self.transaction.signed == ::EVERYBODY && self.signature == Signature::zero()
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new inherent-style transaction from the given function.
|
||||
pub fn inherent(function: InherentFunction) -> Self {
|
||||
UncheckedTransaction {
|
||||
transaction: Transaction {
|
||||
function: Function::Inherent(function),
|
||||
nonce: 0,
|
||||
signed: ::EVERYBODY
|
||||
},
|
||||
signature: Signature::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Slicable for UncheckedTransaction {
|
||||
@@ -372,7 +444,7 @@ mod tests {
|
||||
transaction: Transaction {
|
||||
signed: [1; 32],
|
||||
nonce: 999u64,
|
||||
function: Function::TimestampSet(135135),
|
||||
function: Function::Inherent(InherentFunction::TimestampSet(135135)),
|
||||
},
|
||||
signature: primitives::hash::H512([0; 64]),
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use runtime::{system, parachains, consensus, session};
|
||||
use runtime::{system, parachains, consensus, session, timestamp};
|
||||
|
||||
impl_stubs!(
|
||||
execute_block => |block| system::internal::execute_block(block),
|
||||
@@ -23,5 +23,6 @@ impl_stubs!(
|
||||
validator_count => |()| session::validator_count(),
|
||||
validators => |()| session::validators(),
|
||||
authorities => |()| consensus::authorities(),
|
||||
duty_roster => |()| parachains::calculate_duty_roster()
|
||||
duty_roster => |()| parachains::calculate_duty_roster(),
|
||||
get_timestamp => |()| timestamp::get()
|
||||
);
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
//! Environment API: Allows certain information to be accessed throughout the runtime.
|
||||
|
||||
use rstd::boxed::Box;
|
||||
use rstd::mem;
|
||||
use rstd::cell::RefCell;
|
||||
use rstd::rc::Rc;
|
||||
|
||||
@@ -32,6 +31,8 @@ pub struct Environment {
|
||||
pub parent_hash: Hash,
|
||||
/// The current block digest.
|
||||
pub digest: Digest,
|
||||
/// The current transaction index
|
||||
pub transaction_index: u64,
|
||||
}
|
||||
|
||||
/// Do something with the environment and return its value. Keep the function short.
|
||||
@@ -52,7 +53,7 @@ fn env() -> Rc<RefCell<Environment>> {
|
||||
let singleton: Rc<RefCell<Environment>> = Rc::new(RefCell::new(Default::default()));
|
||||
|
||||
// Put it in the heap so it can outlive this call
|
||||
SINGLETON = mem::transmute(Box::new(singleton));
|
||||
SINGLETON = Box::into_raw(Box::new(singleton)) as *const _;
|
||||
}
|
||||
|
||||
// Now we give out a copy of the data that is safe to use concurrently.
|
||||
@@ -73,7 +74,7 @@ fn env() -> Rc<RefCell<Environment>> {
|
||||
let singleton: Rc<RefCell<Environment>> = Rc::new(RefCell::new(Default::default()));
|
||||
|
||||
// Put it in the heap so it can outlive this call
|
||||
*s.borrow_mut() = mem::transmute(Box::new(singleton));
|
||||
*s.borrow_mut() = Box::into_raw(Box::new(singleton)) as *const _;
|
||||
}
|
||||
|
||||
// Now we give out a copy of the data that is safe to use concurrently.
|
||||
|
||||
@@ -41,7 +41,7 @@ pub mod api;
|
||||
pub mod transaction {
|
||||
use rstd::ops;
|
||||
use polkadot_primitives::Signature;
|
||||
pub use polkadot_primitives::{Transaction, UncheckedTransaction};
|
||||
pub use polkadot_primitives::{Transaction, Function, UncheckedTransaction};
|
||||
|
||||
/// A type-safe indicator that a transaction has been checked.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
@@ -63,10 +63,20 @@ pub mod transaction {
|
||||
}
|
||||
}
|
||||
|
||||
/// Check the signature on a transaction.
|
||||
///
|
||||
/// On failure, return the transaction back.
|
||||
pub fn check(tx: UncheckedTransaction) -> Result<CheckedTransaction, UncheckedTransaction> {
|
||||
/// Check the validity of a transaction: whether it can appear at the given index
|
||||
/// and whether it is correctly authenticated.
|
||||
pub fn check(tx: UncheckedTransaction, index: u64) -> Result<CheckedTransaction, UncheckedTransaction> {
|
||||
match tx.transaction.function.inherent_index() {
|
||||
Some(correct_index) => {
|
||||
if index != correct_index || !tx.is_well_formed() { return Err(tx) }
|
||||
return Ok(CheckedTransaction(tx));
|
||||
}
|
||||
None => {
|
||||
// non-inherent functions must appear after inherent.
|
||||
if index < Function::inherent_functions() { return Err(tx) }
|
||||
}
|
||||
}
|
||||
|
||||
let msg = ::codec::Slicable::encode(&tx.transaction);
|
||||
if ::runtime_io::ed25519_verify(&tx.signature.0, &msg, &tx.transaction.signed) {
|
||||
Ok(CheckedTransaction(tx))
|
||||
|
||||
@@ -24,12 +24,12 @@ use codec::{KeyedVec, Slicable};
|
||||
use runtime_support::{Hashable, storage};
|
||||
use environment::with_env;
|
||||
use polkadot_primitives::{AccountId, Hash, TxOrder, BlockNumber, Block, Header,
|
||||
UncheckedTransaction, Function, Log};
|
||||
UncheckedTransaction, Function, InherentFunction, Log};
|
||||
use runtime::{staking, session};
|
||||
|
||||
const NONCE_OF: &[u8] = b"sys:non:";
|
||||
const BLOCK_HASH_AT: &[u8] = b"sys:old:";
|
||||
const CODE: &[u8] = b"sys:cod";
|
||||
const TEMP_TRANSACTION_NUMBER: &[u8] = b"temp:txcount:";
|
||||
|
||||
/// The current block number being processed. Set by `execute_block`.
|
||||
pub fn block_number() -> BlockNumber {
|
||||
@@ -71,8 +71,10 @@ pub mod internal {
|
||||
// any initial checks
|
||||
initial_checks(&block);
|
||||
|
||||
// execute transactions
|
||||
block.transactions.iter().cloned().for_each(super::execute_transaction);
|
||||
// execute all transactions, inherent or otherwise.
|
||||
for (tx_num, tx) in block.all_transactions().enumerate() {
|
||||
super::execute_transaction(tx, tx_num as u64);
|
||||
}
|
||||
|
||||
// post-transactional book-keeping.
|
||||
staking::internal::check_new_era();
|
||||
@@ -87,6 +89,8 @@ pub mod internal {
|
||||
|
||||
/// Execute a transaction outside of the block execution function.
|
||||
/// This doesn't attempt to validate anything regarding the block.
|
||||
/// Note that when building a block transaction by transaction, the
|
||||
/// inherent methods must be called manually.
|
||||
pub fn execute_transaction(utx: UncheckedTransaction, mut header: Header) -> Header {
|
||||
// populate environment from header.
|
||||
with_env(|e| {
|
||||
@@ -95,11 +99,16 @@ pub mod internal {
|
||||
mem::swap(&mut header.digest, &mut e.digest);
|
||||
});
|
||||
|
||||
super::execute_transaction(utx);
|
||||
let tx_num: u64 = storage::get_or_default(TEMP_TRANSACTION_NUMBER);
|
||||
|
||||
super::execute_transaction(utx, tx_num);
|
||||
|
||||
with_env(|e| {
|
||||
mem::swap(&mut header.digest, &mut e.digest);
|
||||
});
|
||||
|
||||
storage::put(TEMP_TRANSACTION_NUMBER, &(tx_num + 1));
|
||||
|
||||
header
|
||||
}
|
||||
|
||||
@@ -113,6 +122,11 @@ pub mod internal {
|
||||
mem::swap(&mut header.digest, &mut e.digest);
|
||||
});
|
||||
|
||||
let tx_count: u64 = storage::take_or_default(TEMP_TRANSACTION_NUMBER);
|
||||
if tx_count < Function::inherent_functions() {
|
||||
panic!("Not enough transactions provided to fulfill all inherent functions.");
|
||||
}
|
||||
|
||||
staking::internal::check_new_era();
|
||||
session::internal::check_rotate_session();
|
||||
|
||||
@@ -125,10 +139,14 @@ pub mod internal {
|
||||
|
||||
header
|
||||
}
|
||||
}
|
||||
|
||||
/// Dispatch a function.
|
||||
pub fn dispatch_function(function: &Function, transactor: &AccountId) {
|
||||
/// Dispatch a function.
|
||||
fn dispatch_function(function: &Function, transactor: &AccountId) {
|
||||
match *function {
|
||||
Function::Inherent(InherentFunction::TimestampSet(t)) => {
|
||||
::runtime::timestamp::public::set(t);
|
||||
}
|
||||
Function::StakingStake => {
|
||||
::runtime::staking::public::stake(transactor);
|
||||
}
|
||||
@@ -141,9 +159,6 @@ pub mod internal {
|
||||
Function::SessionSetKey(session) => {
|
||||
::runtime::session::public::set_key(transactor, &session);
|
||||
}
|
||||
Function::TimestampSet(t) => {
|
||||
::runtime::timestamp::public::set(t);
|
||||
}
|
||||
Function::GovernancePropose(ref proposal) => {
|
||||
::runtime::governance::public::propose(transactor, proposal);
|
||||
}
|
||||
@@ -151,28 +166,34 @@ pub mod internal {
|
||||
::runtime::governance::public::approve(transactor, era_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_transaction(utx: UncheckedTransaction) {
|
||||
fn execute_transaction(utx: UncheckedTransaction, tx_num: u64) {
|
||||
use ::transaction;
|
||||
|
||||
// Verify the signature is good.
|
||||
let tx = match transaction::check(utx) {
|
||||
// Verify the transaction is authenticated at its position.
|
||||
let tx = match transaction::check(utx, tx_num) {
|
||||
Ok(tx) => tx,
|
||||
Err(_) => panic!("All transactions should be properly signed"),
|
||||
Err(_) => panic!("Transaction at index {} not properly authenticated", tx_num),
|
||||
};
|
||||
|
||||
// check nonce
|
||||
let nonce_key = tx.signed.to_keyed_vec(NONCE_OF);
|
||||
let expected_nonce: TxOrder = storage::get_or(&nonce_key, 0);
|
||||
let (expected_nonce, increment_nonce) = if !tx.function.is_inherent() {
|
||||
(storage::get_or(&nonce_key, 0), true)
|
||||
} else {
|
||||
(0, false)
|
||||
};
|
||||
|
||||
assert!(tx.nonce == expected_nonce, "All transactions should have the correct nonce");
|
||||
|
||||
// increment nonce in storage
|
||||
// increment nonce in storage, unless it's the EVERYBODY account.
|
||||
if increment_nonce {
|
||||
storage::put(&nonce_key, &(expected_nonce + 1));
|
||||
}
|
||||
|
||||
// decode parameters and dispatch
|
||||
internal::dispatch_function(&tx.function, &tx.signed);
|
||||
dispatch_function(&tx.function, &tx.signed);
|
||||
}
|
||||
|
||||
fn initial_checks(block: &Block) {
|
||||
@@ -185,7 +206,7 @@ fn initial_checks(block: &Block) {
|
||||
);
|
||||
|
||||
// check transaction trie root represents the transactions.
|
||||
let txs = block.transactions.iter().map(Slicable::encode).collect::<Vec<_>>();
|
||||
let txs = block.all_transactions().map(|tx| Slicable::encode(&tx)).collect::<Vec<_>>();
|
||||
let txs = txs.iter().map(Vec::as_slice).collect::<Vec<_>>();
|
||||
let txs_root = enumerated_trie_root(&txs).into();
|
||||
info_expect_equal_hash(&header.transaction_root, &txs_root);
|
||||
@@ -239,9 +260,37 @@ mod tests {
|
||||
use keyring::Keyring;
|
||||
use environment::with_env;
|
||||
use primitives::hexdisplay::HexDisplay;
|
||||
use polkadot_primitives::{Header, Digest, UncheckedTransaction, Transaction, Function};
|
||||
use polkadot_primitives::{Header, Body, Digest, UncheckedTransaction, Transaction, Function, InherentFunction};
|
||||
use runtime::staking;
|
||||
|
||||
fn set_timestamp() -> UncheckedTransaction {
|
||||
UncheckedTransaction::inherent(InherentFunction::TimestampSet(100_000))
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn fails_if_first_not_timestamp_set() {
|
||||
let one = Keyring::One.to_raw_public();
|
||||
let two = Keyring::Two.to_raw_public();
|
||||
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
];
|
||||
|
||||
let tx = UncheckedTransaction {
|
||||
transaction: Transaction {
|
||||
signed: one.clone(),
|
||||
nonce: 0,
|
||||
function: Function::StakingTransfer(two, 69),
|
||||
},
|
||||
signature: hex!("5f9832c5a4a39e2dd4a3a0c5b400e9836beb362cb8f7d845a8291a2ae6fe366612e080e4acd0b5a75c3d0b6ee69614a68fb63698c1e76bf1f2dcd8fa617ddf05").into(),
|
||||
};
|
||||
|
||||
with_externalities(&mut t, || {
|
||||
internal::execute_transaction(tx, Header::from_block_number(1));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn staking_balance_transfer_dispatch_works() {
|
||||
let one = Keyring::One.to_raw_public();
|
||||
@@ -261,7 +310,9 @@ mod tests {
|
||||
};
|
||||
|
||||
with_externalities(&mut t, || {
|
||||
internal::execute_transaction(tx, Header::from_block_number(1));
|
||||
let header = Header::from_block_number(1);
|
||||
let header = internal::execute_transaction(set_timestamp(), header);
|
||||
internal::execute_transaction(tx, header);
|
||||
assert_eq!(staking::balance(&one), 42);
|
||||
assert_eq!(staking::balance(&two), 69);
|
||||
});
|
||||
@@ -293,22 +344,22 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn block_import_works() {
|
||||
let one = Keyring::One.to_raw_public();
|
||||
let two = Keyring::Two.to_raw_public();
|
||||
|
||||
let mut t = new_test_ext();
|
||||
|
||||
let h = Header {
|
||||
parent_hash: [69u8; 32].into(),
|
||||
number: 1,
|
||||
state_root: hex!("1ab2dbb7d4868a670b181327b0b6a58dc64b10cfb9876f737a5aa014b8da31e0").into(),
|
||||
transaction_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(),
|
||||
state_root: hex!("aa4fbcdc09b21e4366aebccd9b9ec0831a8a2765c712d3397f121ff8e60e21e2").into(),
|
||||
transaction_root: hex!("328ae80be3adf358d2a2e188cbe1bfd3f8cd5b15a2e7666e2b4eccf7450efc32").into(),
|
||||
digest: Digest { logs: vec![], },
|
||||
};
|
||||
|
||||
let b = Block {
|
||||
header: h,
|
||||
transactions: vec![],
|
||||
body: Body {
|
||||
timestamp: 100_000,
|
||||
transactions: vec![]
|
||||
}
|
||||
};
|
||||
|
||||
with_externalities(&mut t, || {
|
||||
@@ -334,7 +385,10 @@ mod tests {
|
||||
|
||||
let b = Block {
|
||||
header: h,
|
||||
body: Body {
|
||||
timestamp: 100_000,
|
||||
transactions: vec![],
|
||||
}
|
||||
};
|
||||
|
||||
with_externalities(&mut t, || {
|
||||
@@ -360,7 +414,10 @@ mod tests {
|
||||
|
||||
let b = Block {
|
||||
header: h,
|
||||
body: Body {
|
||||
timestamp: 100_000,
|
||||
transactions: vec![],
|
||||
}
|
||||
};
|
||||
|
||||
with_externalities(&mut t, || {
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
//! Timestamp manager: just handles the current timestamp.
|
||||
|
||||
use runtime_support::storage;
|
||||
|
||||
pub type Timestamp = u64;
|
||||
use polkadot_primitives::Timestamp;
|
||||
|
||||
const CURRENT_TIMESTAMP: &[u8] = b"tim:val";
|
||||
|
||||
@@ -32,6 +31,10 @@ pub mod public {
|
||||
|
||||
/// Set the current time.
|
||||
pub fn set(now: Timestamp) {
|
||||
if super::get() > now {
|
||||
panic!("last timestamp less than now");
|
||||
}
|
||||
|
||||
storage::put(CURRENT_TIMESTAMP, &now);
|
||||
}
|
||||
}
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -47,6 +47,12 @@ error_chain! {
|
||||
description("Unable to dispatch agreement future"),
|
||||
display("Unable to dispatch agreement future: {:?}", e),
|
||||
}
|
||||
|
||||
/// Some other error.
|
||||
Other(e: Box<::std::error::Error + Send>) {
|
||||
description("Other error")
|
||||
display("Other error: {}", e.description())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,8 @@ pub struct LocalizedMessage<C, D, V, S> {
|
||||
/// Provides necessary types for protocol messages, and functions necessary for a
|
||||
/// participant to evaluate and create those messages.
|
||||
pub trait Context {
|
||||
/// Errors which can occur from the futures in this context.
|
||||
type Error: From<InputStreamConcluded>;
|
||||
/// Candidate proposed.
|
||||
type Candidate: Debug + Eq + Clone;
|
||||
/// Candidate digest.
|
||||
@@ -83,9 +85,11 @@ pub trait Context {
|
||||
/// Signature.
|
||||
type Signature: Debug + Eq + Clone;
|
||||
/// A future that resolves when a round timeout is concluded.
|
||||
type RoundTimeout: Future<Item=()>;
|
||||
type RoundTimeout: Future<Item=(), Error=Self::Error>;
|
||||
/// A future that resolves when a proposal is ready.
|
||||
type CreateProposal: Future<Item=Self::Candidate>;
|
||||
type CreateProposal: Future<Item=Self::Candidate, Error=Self::Error>;
|
||||
/// A future that resolves when a proposal has been evaluated.
|
||||
type EvaluateProposal: Future<Item=bool, Error=Self::Error>;
|
||||
|
||||
/// Get the local authority ID.
|
||||
fn local_id(&self) -> Self::AuthorityId;
|
||||
@@ -103,8 +107,8 @@ pub trait Context {
|
||||
/// Get the proposer for a given round of consensus.
|
||||
fn round_proposer(&self, round: usize) -> Self::AuthorityId;
|
||||
|
||||
/// Whether the candidate is valid.
|
||||
fn candidate_valid(&self, candidate: &Self::Candidate) -> bool;
|
||||
/// Whether the proposal is valid.
|
||||
fn proposal_valid(&self, proposal: &Self::Candidate) -> Self::EvaluateProposal;
|
||||
|
||||
/// Create a round timeout. The context will determine the correct timeout
|
||||
/// length, and create a future that will resolve when the timeout is
|
||||
@@ -246,6 +250,7 @@ struct Strategy<C: Context> {
|
||||
nodes: usize,
|
||||
max_faulty: usize,
|
||||
fetching_proposal: Option<C::CreateProposal>,
|
||||
evaluating_proposal: Option<C::EvaluateProposal>,
|
||||
round_timeout: future::Fuse<C::RoundTimeout>,
|
||||
local_state: LocalState,
|
||||
locked: Option<Locked<C::Digest, C::Signature>>,
|
||||
@@ -278,6 +283,7 @@ impl<C: Context> Strategy<C> {
|
||||
current_accumulator,
|
||||
future_accumulator,
|
||||
fetching_proposal: None,
|
||||
evaluating_proposal: None,
|
||||
local_state: LocalState::Start,
|
||||
locked: None,
|
||||
notable_candidates: HashMap::new(),
|
||||
@@ -324,15 +330,12 @@ impl<C: Context> Strategy<C> {
|
||||
// rounds if necessary.
|
||||
//
|
||||
// only call within the context of a `Task`.
|
||||
fn poll<E>(
|
||||
fn poll(
|
||||
&mut self,
|
||||
context: &C,
|
||||
sending: &mut Sending<<C as TypeResolve>::Communication>
|
||||
)
|
||||
-> Poll<Committed<C::Candidate, C::Digest, C::Signature>, E>
|
||||
where
|
||||
C::RoundTimeout: Future<Error=E>,
|
||||
C::CreateProposal: Future<Error=E>,
|
||||
-> Poll<Committed<C::Candidate, C::Digest, C::Signature>, C::Error>
|
||||
{
|
||||
let mut last_watermark = (
|
||||
self.current_accumulator.round_number(),
|
||||
@@ -361,18 +364,15 @@ impl<C: Context> Strategy<C> {
|
||||
|
||||
// perform one round of polling: attempt to broadcast messages and change the state.
|
||||
// if the round or internal round-state changes, this should be called again.
|
||||
fn poll_once<E>(
|
||||
fn poll_once(
|
||||
&mut self,
|
||||
context: &C,
|
||||
sending: &mut Sending<<C as TypeResolve>::Communication>
|
||||
)
|
||||
-> Poll<Committed<C::Candidate, C::Digest, C::Signature>, E>
|
||||
where
|
||||
C::RoundTimeout: Future<Error=E>,
|
||||
C::CreateProposal: Future<Error=E>,
|
||||
-> Poll<Committed<C::Candidate, C::Digest, C::Signature>, C::Error>
|
||||
{
|
||||
self.propose(context, sending)?;
|
||||
self.prepare(context, sending);
|
||||
self.prepare(context, sending)?;
|
||||
self.commit(context, sending);
|
||||
self.vote_advance(context, sending)?;
|
||||
|
||||
@@ -423,7 +423,7 @@ impl<C: Context> Strategy<C> {
|
||||
context: &C,
|
||||
sending: &mut Sending<<C as TypeResolve>::Communication>
|
||||
)
|
||||
-> Result<(), <C::CreateProposal as Future>::Error>
|
||||
-> Result<(), C::Error>
|
||||
{
|
||||
if let LocalState::Start = self.local_state {
|
||||
let mut propose = false;
|
||||
@@ -486,11 +486,13 @@ impl<C: Context> Strategy<C> {
|
||||
&mut self,
|
||||
context: &C,
|
||||
sending: &mut Sending<<C as TypeResolve>::Communication>
|
||||
) {
|
||||
)
|
||||
-> Result<(), C::Error>
|
||||
{
|
||||
// prepare only upon start or having proposed.
|
||||
match self.local_state {
|
||||
LocalState::Start | LocalState::Proposed => {},
|
||||
_ => return
|
||||
_ => return Ok(())
|
||||
};
|
||||
|
||||
let mut prepare_for = None;
|
||||
@@ -508,11 +510,20 @@ impl<C: Context> Strategy<C> {
|
||||
// this is necessary to preserve the liveness property.
|
||||
prepare_for = Some(digest);
|
||||
}
|
||||
None => if context.candidate_valid(candidate) {
|
||||
None => {
|
||||
let res = self.evaluating_proposal
|
||||
.get_or_insert_with(|| context.proposal_valid(candidate))
|
||||
.poll()?;
|
||||
|
||||
if let Async::Ready(valid) = res {
|
||||
self.evaluating_proposal = None;
|
||||
if valid {
|
||||
prepare_for = Some(digest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(digest) = prepare_for {
|
||||
let message = Message::Prepare(
|
||||
@@ -523,6 +534,8 @@ impl<C: Context> Strategy<C> {
|
||||
self.import_and_send_message(message, context, sending);
|
||||
self.local_state = LocalState::Prepared;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn commit(
|
||||
@@ -561,7 +574,7 @@ impl<C: Context> Strategy<C> {
|
||||
context: &C,
|
||||
sending: &mut Sending<<C as TypeResolve>::Communication>
|
||||
)
|
||||
-> Result<(), <C::RoundTimeout as Future>::Error>
|
||||
-> Result<(), C::Error>
|
||||
{
|
||||
// we can vote for advancement under all circumstances unless we have already.
|
||||
if let LocalState::VoteAdvance = self.local_state { return Ok(()) }
|
||||
@@ -592,6 +605,7 @@ impl<C: Context> Strategy<C> {
|
||||
let threshold = self.nodes - self.max_faulty;
|
||||
|
||||
self.fetching_proposal = None;
|
||||
self.evaluating_proposal = None;
|
||||
self.round_timeout = context.begin_round_timeout(round).fuse();
|
||||
self.local_state = LocalState::Start;
|
||||
|
||||
@@ -647,17 +661,14 @@ pub struct Agreement<C: Context, I, O> {
|
||||
strategy: Strategy<C>,
|
||||
}
|
||||
|
||||
impl<C, I, O, E> Future for Agreement<C, I, O>
|
||||
impl<C, I, O> Future for Agreement<C, I, O>
|
||||
where
|
||||
C: Context,
|
||||
C::RoundTimeout: Future<Error=E>,
|
||||
C::CreateProposal: Future<Error=E>,
|
||||
I: Stream<Item=<C as TypeResolve>::Communication,Error=E>,
|
||||
O: Sink<SinkItem=<C as TypeResolve>::Communication,SinkError=E>,
|
||||
E: From<InputStreamConcluded>,
|
||||
I: Stream<Item=<C as TypeResolve>::Communication,Error=C::Error>,
|
||||
O: Sink<SinkItem=<C as TypeResolve>::Communication,SinkError=C::Error>,
|
||||
{
|
||||
type Item = Committed<C::Candidate, C::Digest, C::Signature>;
|
||||
type Error = E;
|
||||
type Error = C::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
// even if we've observed the conclusion, wait until all
|
||||
@@ -721,15 +732,12 @@ impl<C, I, O, E> Future for Agreement<C, I, O>
|
||||
/// conclude without having witnessed the conclusion.
|
||||
/// In general, this future should be pre-empted by the import of a justification
|
||||
/// set for this block height.
|
||||
pub fn agree<C: Context, I, O, E>(context: C, nodes: usize, max_faulty: usize, input: I, output: O)
|
||||
pub fn agree<C: Context, I, O>(context: C, nodes: usize, max_faulty: usize, input: I, output: O)
|
||||
-> Agreement<C, I, O>
|
||||
where
|
||||
C: Context,
|
||||
C::RoundTimeout: Future<Error=E>,
|
||||
C::CreateProposal: Future<Error=E>,
|
||||
I: Stream<Item=<C as TypeResolve>::Communication,Error=E>,
|
||||
O: Sink<SinkItem=<C as TypeResolve>::Communication,SinkError=E>,
|
||||
E: From<InputStreamConcluded>,
|
||||
I: Stream<Item=<C as TypeResolve>::Communication,Error=C::Error>,
|
||||
O: Sink<SinkItem=<C as TypeResolve>::Communication,SinkError=C::Error>,
|
||||
{
|
||||
let strategy = Strategy::create(&context, nodes, max_faulty);
|
||||
Agreement {
|
||||
|
||||
@@ -159,12 +159,14 @@ struct TestContext {
|
||||
}
|
||||
|
||||
impl Context for TestContext {
|
||||
type Error = Error;
|
||||
type Candidate = Candidate;
|
||||
type Digest = Digest;
|
||||
type AuthorityId = AuthorityId;
|
||||
type Signature = Signature;
|
||||
type RoundTimeout = Box<Future<Item=(), Error=Error>>;
|
||||
type CreateProposal = FutureResult<Candidate, Error>;
|
||||
type EvaluateProposal = FutureResult<bool, Error>;
|
||||
|
||||
fn local_id(&self) -> AuthorityId {
|
||||
self.local_id.clone()
|
||||
@@ -200,8 +202,8 @@ impl Context for TestContext {
|
||||
self.shared.lock().unwrap().round_proposer(round)
|
||||
}
|
||||
|
||||
fn candidate_valid(&self, candidate: &Candidate) -> bool {
|
||||
candidate.0 % 3 != 0
|
||||
fn proposal_valid(&self, proposal: &Candidate) -> FutureResult<bool, Error> {
|
||||
Ok(proposal.0 % 3 != 0).into_future()
|
||||
}
|
||||
|
||||
fn begin_round_timeout(&self, round: usize) -> Self::RoundTimeout {
|
||||
|
||||
@@ -100,22 +100,35 @@ pub type Committed = generic::Committed<Block, HeaderHash, LocalizedSignature>;
|
||||
/// Communication between BFT participants.
|
||||
pub type Communication = generic::Communication<Block, HeaderHash, AuthorityId, LocalizedSignature>;
|
||||
|
||||
/// Proposer factory. Can be used to create a proposer instance.
|
||||
pub trait ProposerFactory {
|
||||
/// The proposer type this creates.
|
||||
type Proposer: Proposer;
|
||||
/// Error which can occur upon creation.
|
||||
type Error: From<Error>;
|
||||
|
||||
/// Initialize the proposal logic on top of a specific header.
|
||||
// TODO: provide state context explicitly?
|
||||
fn init(&self, parent_header: &Header, authorities: &[AuthorityId], sign_with: Arc<ed25519::Pair>) -> Result<Self::Proposer, Self::Error>;
|
||||
}
|
||||
|
||||
/// Logic for a proposer.
|
||||
///
|
||||
/// This will encapsulate creation and evaluation of proposals at a specific
|
||||
/// block.
|
||||
pub trait Proposer: Sized {
|
||||
type CreateProposal: IntoFuture<Item=Block,Error=Error>;
|
||||
|
||||
/// Initialize the proposal logic on top of a specific header.
|
||||
// TODO: provide state context explicitly?
|
||||
fn init(parent_header: &Header, sign_with: Arc<ed25519::Pair>) -> Self;
|
||||
pub trait Proposer {
|
||||
/// Error type which can occur when proposing or evaluating.
|
||||
type Error: From<Error> + From<InputStreamConcluded> + 'static;
|
||||
/// Future that resolves to a created proposal.
|
||||
type Create: IntoFuture<Item=Block,Error=Self::Error>;
|
||||
/// Future that resolves when a proposal is evaluated.
|
||||
type Evaluate: IntoFuture<Item=bool,Error=Self::Error>;
|
||||
|
||||
/// Create a proposal.
|
||||
fn propose(&self) -> Self::CreateProposal;
|
||||
fn propose(&self) -> Self::Create;
|
||||
/// Evaluate proposal. True means valid.
|
||||
// TODO: change this to a future.
|
||||
fn evaluate(&self, proposal: &Block) -> bool;
|
||||
fn evaluate(&self, proposal: &Block) -> Self::Evaluate;
|
||||
}
|
||||
|
||||
/// Block import trait.
|
||||
@@ -141,12 +154,14 @@ struct BftInstance<P> {
|
||||
}
|
||||
|
||||
impl<P: Proposer> generic::Context for BftInstance<P> {
|
||||
type Error = P::Error;
|
||||
type AuthorityId = AuthorityId;
|
||||
type Digest = HeaderHash;
|
||||
type Signature = LocalizedSignature;
|
||||
type Candidate = Block;
|
||||
type RoundTimeout = Box<Future<Item=(),Error=Error> + Send>;
|
||||
type CreateProposal = <P::CreateProposal as IntoFuture>::Future;
|
||||
type RoundTimeout = Box<Future<Item=(),Error=Self::Error> + Send>;
|
||||
type CreateProposal = <P::Create as IntoFuture>::Future;
|
||||
type EvaluateProposal = <P::Evaluate as IntoFuture>::Future;
|
||||
|
||||
fn local_id(&self) -> AuthorityId {
|
||||
self.key.public().0
|
||||
@@ -181,8 +196,8 @@ impl<P: Proposer> generic::Context for BftInstance<P> {
|
||||
self.authorities[(index as usize) % self.authorities.len()]
|
||||
}
|
||||
|
||||
fn candidate_valid(&self, proposal: &Block) -> bool {
|
||||
self.proposer.evaluate(proposal)
|
||||
fn proposal_valid(&self, proposal: &Block) -> Self::EvaluateProposal {
|
||||
self.proposer.evaluate(proposal).into_future()
|
||||
}
|
||||
|
||||
fn begin_round_timeout(&self, round: usize) -> Self::RoundTimeout {
|
||||
@@ -194,24 +209,25 @@ impl<P: Proposer> generic::Context for BftInstance<P> {
|
||||
.saturating_mul(self.round_timeout_multiplier);
|
||||
|
||||
Box::new(self.timer.sleep(Duration::from_secs(timeout))
|
||||
.map_err(|_| ErrorKind::FaultyTimer.into()))
|
||||
.map_err(|_| Error::from(ErrorKind::FaultyTimer))
|
||||
.map_err(Into::into))
|
||||
}
|
||||
}
|
||||
|
||||
type Input = stream::Empty<Communication, Error>;
|
||||
type Input<E> = stream::Empty<Communication, E>;
|
||||
|
||||
// "black hole" output sink.
|
||||
struct Output;
|
||||
struct Output<E>(::std::marker::PhantomData<E>);
|
||||
|
||||
impl Sink for Output {
|
||||
impl<E> Sink for Output<E> {
|
||||
type SinkItem = Communication;
|
||||
type SinkError = Error;
|
||||
type SinkError = E;
|
||||
|
||||
fn start_send(&mut self, _item: Communication) -> ::futures::StartSend<Communication, Error> {
|
||||
fn start_send(&mut self, _item: Communication) -> ::futures::StartSend<Communication, E> {
|
||||
Ok(::futures::AsyncSink::Ready)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> ::futures::Poll<(), Error> {
|
||||
fn poll_complete(&mut self) -> ::futures::Poll<(), E> {
|
||||
Ok(Async::Ready(()))
|
||||
}
|
||||
}
|
||||
@@ -219,7 +235,7 @@ impl Sink for Output {
|
||||
/// A future that resolves either when canceled (witnessing a block from the network at same height)
|
||||
/// or when agreement completes.
|
||||
pub struct BftFuture<P: Proposer, I> {
|
||||
inner: generic::Agreement<BftInstance<P>, Input, Output>,
|
||||
inner: generic::Agreement<BftInstance<P>, Input<P::Error>, Output<P::Error>>,
|
||||
cancel: Arc<AtomicBool>,
|
||||
send_task: Option<oneshot::Sender<task::Task>>,
|
||||
import: Arc<I>,
|
||||
@@ -282,30 +298,38 @@ pub struct BftService<P, E, I> {
|
||||
timer: Timer,
|
||||
round_timeout_multiplier: u64,
|
||||
key: Arc<ed25519::Pair>, // TODO: key changing over time.
|
||||
_marker: ::std::marker::PhantomData<P>,
|
||||
factory: P,
|
||||
}
|
||||
|
||||
impl<P, E, I> BftService<P, E, I>
|
||||
where
|
||||
P: Proposer,
|
||||
E: Executor<BftFuture<P, I>>,
|
||||
P: ProposerFactory,
|
||||
E: Executor<BftFuture<P::Proposer, I>>,
|
||||
I: BlockImport + Authorities,
|
||||
{
|
||||
/// Signal that a valid block with the given header has been imported.
|
||||
///
|
||||
/// This will begin the consensus process to build a block on top of it.
|
||||
/// If the executor fails to run the future, an error will be returned.
|
||||
pub fn build_upon(&self, header: &Header) -> Result<(), Error> {
|
||||
/// If the local signing key is an authority, this will begin the consensus process to build a
|
||||
/// block on top of it. If the executor fails to run the future, an error will be returned.
|
||||
pub fn build_upon(&self, header: &Header) -> Result<(), P::Error> {
|
||||
let hash = header.hash();
|
||||
let mut _preempted_consensus = None;
|
||||
let mut _preempted_consensus = None; // defers drop of live to the end.
|
||||
|
||||
let proposer = P::init(header, self.key.clone());
|
||||
let authorities = self.client.authorities(&BlockId::Hash(hash))?;
|
||||
|
||||
// TODO: check key is one of the authorities.
|
||||
let authorities = self.client.authorities(&BlockId::Hash(hash))?;
|
||||
let n = authorities.len();
|
||||
let max_faulty = max_faulty_of(n);
|
||||
|
||||
let local_id = self.key.public().0;
|
||||
|
||||
if !authorities.contains(&local_id) {
|
||||
self.live_agreements.lock().remove(&header.parent_hash);
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
let proposer = self.factory.init(header, &authorities, self.key.clone())?;
|
||||
|
||||
let bft_instance = BftInstance {
|
||||
proposer,
|
||||
parent_hash: hash,
|
||||
@@ -320,7 +344,7 @@ impl<P, E, I> BftService<P, E, I>
|
||||
n,
|
||||
max_faulty,
|
||||
stream::empty(),
|
||||
Output,
|
||||
Output(Default::default()),
|
||||
);
|
||||
|
||||
let cancel = Arc::new(AtomicBool::new(false));
|
||||
@@ -331,7 +355,7 @@ impl<P, E, I> BftService<P, E, I>
|
||||
cancel: cancel.clone(),
|
||||
send_task: Some(tx),
|
||||
import: self.client.clone(),
|
||||
}).map_err(|e| e.kind()).map_err(ErrorKind::Executor)?;
|
||||
}).map_err(|e| e.kind()).map_err(ErrorKind::Executor).map_err(Error::from)?;
|
||||
|
||||
{
|
||||
let mut live = self.live_agreements.lock();
|
||||
@@ -455,14 +479,22 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
struct DummyFactory;
|
||||
struct DummyProposer(block::Number);
|
||||
|
||||
impl Proposer for DummyProposer {
|
||||
type CreateProposal = Result<Block, Error>;
|
||||
impl ProposerFactory for DummyFactory {
|
||||
type Proposer = DummyProposer;
|
||||
type Error = Error;
|
||||
|
||||
fn init(parent_header: &Header, _sign_with: Arc<ed25519::Pair>) -> Self {
|
||||
DummyProposer(parent_header.number + 1)
|
||||
fn init(&self, parent_header: &Header, _authorities: &[AuthorityId], _sign_with: Arc<ed25519::Pair>) -> Result<DummyProposer, Error> {
|
||||
Ok(DummyProposer(parent_header.number + 1))
|
||||
}
|
||||
}
|
||||
|
||||
impl Proposer for DummyProposer {
|
||||
type Error = Error;
|
||||
type Create = Result<Block, Error>;
|
||||
type Evaluate = Result<bool, Error>;
|
||||
|
||||
fn propose(&self) -> Result<Block, Error> {
|
||||
Ok(Block {
|
||||
@@ -471,13 +503,13 @@ mod tests {
|
||||
})
|
||||
}
|
||||
|
||||
fn evaluate(&self, proposal: &Block) -> bool {
|
||||
proposal.header.number == self.0
|
||||
fn evaluate(&self, proposal: &Block) -> Result<bool, Error> {
|
||||
Ok(proposal.header.number == self.0)
|
||||
}
|
||||
}
|
||||
|
||||
fn make_service(client: FakeClient, handle: Handle)
|
||||
-> BftService<DummyProposer, Handle, FakeClient>
|
||||
-> BftService<DummyFactory, Handle, FakeClient>
|
||||
{
|
||||
BftService {
|
||||
client: Arc::new(client),
|
||||
@@ -486,7 +518,7 @@ mod tests {
|
||||
timer: Timer::default(),
|
||||
round_timeout_multiplier: 4,
|
||||
key: Arc::new(Keyring::One.into()),
|
||||
_marker: Default::default(),
|
||||
factory: DummyFactory
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ impl Slicable for Header {
|
||||
}
|
||||
|
||||
/// Block indentification.
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
pub enum Id {
|
||||
/// Identify by block header hash.
|
||||
|
||||
@@ -26,5 +26,5 @@ pub use std::rc;
|
||||
pub use std::slice;
|
||||
pub use std::vec;
|
||||
pub mod collections {
|
||||
pub use std::collections::hash_map;
|
||||
pub use std::collections::btree_map;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ pub use alloc::boxed;
|
||||
pub use alloc::rc;
|
||||
pub use alloc::vec;
|
||||
pub mod collections {
|
||||
pub use alloc::collections::hash_map;
|
||||
pub use alloc::btree_map;
|
||||
}
|
||||
pub use core::cell;
|
||||
pub use core::cmp;
|
||||
|
||||
@@ -66,16 +66,11 @@ pub fn get_or_else<T: Slicable + Sized, F: FnOnce() -> T>(key: &[u8], default_va
|
||||
get(key).unwrap_or_else(default_value)
|
||||
}
|
||||
|
||||
/// Please `value` in storage under `key`.
|
||||
/// Put `value` in storage under `key`.
|
||||
pub fn put<T: Slicable>(key: &[u8], value: &T) {
|
||||
value.using_encoded(|slice| runtime_io::set_storage(&twox_128(key)[..], slice));
|
||||
}
|
||||
|
||||
/// Please `value` in storage under `key`.
|
||||
pub fn place<T: Slicable>(key: &[u8], value: T) {
|
||||
value.using_encoded(|slice| runtime_io::set_storage(&twox_128(key)[..], slice));
|
||||
}
|
||||
|
||||
/// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise.
|
||||
pub fn take<T: Slicable + Sized>(key: &[u8]) -> Option<T> {
|
||||
let r = get(key);
|
||||
@@ -199,16 +194,11 @@ pub mod unhashed {
|
||||
get(key).unwrap_or_else(default_value)
|
||||
}
|
||||
|
||||
/// Please `value` in storage under `key`.
|
||||
/// Put `value` in storage under `key`.
|
||||
pub fn put<T: Slicable>(key: &[u8], value: &T) {
|
||||
value.using_encoded(|slice| runtime_io::set_storage(key, slice));
|
||||
}
|
||||
|
||||
/// Please `value` in storage under `key`.
|
||||
pub fn place<T: Slicable>(key: &[u8], value: T) {
|
||||
value.using_encoded(|slice| runtime_io::set_storage(key, slice));
|
||||
}
|
||||
|
||||
/// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise.
|
||||
pub fn take<T: Slicable + Sized>(key: &[u8]) -> Option<T> {
|
||||
let r = get(key);
|
||||
|
||||
@@ -47,7 +47,7 @@ pub use backend::Backend;
|
||||
///
|
||||
/// A transaction shares all prospective changes within an inner overlay
|
||||
/// that can be cleared.
|
||||
#[derive(Default)]
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct OverlayedChanges {
|
||||
prospective: HashMap<Vec<u8>, Option<Vec<u8>>>,
|
||||
committed: HashMap<Vec<u8>, Option<Vec<u8>>>,
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
Reference in New Issue
Block a user