Use BTreeMap rather than HashMap in demo runtime.

This commit is contained in:
Gav
2018-02-25 16:58:07 +01:00
18 changed files with 1166 additions and 208 deletions
+3
View File
@@ -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" }
+231 -11
View File
@@ -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());
}
}
+2 -2
View File
@@ -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)))
);
+3
View File
@@ -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" }
+51
View File
@@ -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()
}
}
+343 -30
View File
@@ -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)
}
+116 -88
View File
@@ -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 {
signed: Keyring::One.to_raw_public(),
nonce: 0,
function: Function::StakingTransfer(Keyring::Two.to_raw_public(), 69),
}]
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, || {
+194 -8
View File
@@ -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());
}
}
+6 -3
View File
@@ -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;
+7
View File
@@ -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
+78 -6
View File
@@ -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]),
};
+3 -2
View File
@@ -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()
);
+4 -3
View File
@@ -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.
+15 -5
View File
@@ -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))
+105 -48
View File
@@ -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,54 +139,61 @@ pub mod internal {
header
}
}
/// Dispatch a function.
pub fn dispatch_function(function: &Function, transactor: &AccountId) {
match *function {
Function::StakingStake => {
::runtime::staking::public::stake(transactor);
}
Function::StakingUnstake => {
::runtime::staking::public::unstake(transactor);
}
Function::StakingTransfer(dest, value) => {
::runtime::staking::public::transfer(transactor, &dest, value);
}
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);
}
Function::GovernanceApprove(era_index) => {
::runtime::governance::public::approve(transactor, era_index);
}
/// 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);
}
Function::StakingUnstake => {
::runtime::staking::public::unstake(transactor);
}
Function::StakingTransfer(dest, value) => {
::runtime::staking::public::transfer(transactor, &dest, value);
}
Function::SessionSetKey(session) => {
::runtime::session::public::set_key(transactor, &session);
}
Function::GovernancePropose(ref proposal) => {
::runtime::governance::public::propose(transactor, proposal);
}
Function::GovernanceApprove(era_index) => {
::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
storage::put(&nonce_key, &(expected_nonce + 1));
// 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,
transactions: vec![],
body: Body {
timestamp: 100_000,
transactions: vec![],
}
};
with_externalities(&mut t, || {
@@ -360,7 +414,10 @@ mod tests {
let b = Block {
header: h,
transactions: vec![],
body: Body {
timestamp: 100_000,
transactions: vec![],
}
};
with_externalities(&mut t, || {
+5 -2
View File
@@ -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);
}
}