Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Proposal creation and evaluation to plug into BFT #77

Merged
merged 38 commits into from
Feb 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
482a074
reshuffle consensus libraries
rphmeier Feb 8, 2018
917b092
polkadot-useful type definitions for statement table
rphmeier Feb 8, 2018
8e2fd3c
begin BftService
rphmeier Feb 10, 2018
776cf13
Merge branch 'master' into rh-split-bft-table
rphmeier Feb 10, 2018
6abfed4
primary selection logic
rphmeier Feb 12, 2018
fc18524
bft service implementation without I/O
rphmeier Feb 12, 2018
017fd51
extract out `BlockImport` trait
rphmeier Feb 12, 2018
25990ee
Merge branch 'master' into rh-split-bft-table
rphmeier Feb 12, 2018
c33c3ff
allow bft primitives to compile on wasm
rphmeier Feb 12, 2018
acab9a3
Block builder (substrate)
gavofyork Feb 12, 2018
1830fa7
take polkadot-consensus down to the core.
rphmeier Feb 12, 2018
767a9d9
test for preemption
rphmeier Feb 12, 2018
7fc4b4d
fix test build
rphmeier Feb 12, 2018
9acd3f9
Fix wasm build
gavofyork Feb 12, 2018
ca5900f
Bulid on any block
gavofyork Feb 13, 2018
d11cfe1
Test for block builder.
gavofyork Feb 13, 2018
b973ccc
Block import tests for client.
gavofyork Feb 13, 2018
ec61865
Tidy ups
gavofyork Feb 13, 2018
23638cd
clean up block builder instantiation
rphmeier Feb 15, 2018
dda6d24
Merge branch 'rh-split-bft-table' into rh-justification-verification
rphmeier Feb 15, 2018
340ce39
justification verification logic
rphmeier Feb 15, 2018
170b0d1
JustifiedHeader and import
rphmeier Feb 15, 2018
6a1a851
Propert block generation for tests
arkpar Feb 15, 2018
1352765
network and tablerouter trait
rphmeier Feb 15, 2018
2758503
use statement import to drive creation of further statements
rphmeier Feb 15, 2018
a1247bd
Fixed rpc tests
arkpar Feb 15, 2018
a1a19b6
custom error type for consensus
rphmeier Feb 15, 2018
40a9496
create proposer
rphmeier Feb 15, 2018
9e4f273
asynchronous proposal evaluation
rphmeier Feb 15, 2018
673fc2c
Merge branch 'master' into rh-justification-verification
rphmeier Feb 15, 2018
8636b77
Merge branch 'rh-justification-verification' into rh-polkadot-propose
rphmeier Feb 15, 2018
a5c09c8
inherent transactions in polkadot runtime
rphmeier Feb 16, 2018
7b1a563
fix tests to match real polkadot block constraints
rphmeier Feb 16, 2018
8d08573
implicitly generate inherent functions
rphmeier Feb 16, 2018
2abbe6c
add inherent transaction functionality to block body
rphmeier Feb 20, 2018
5bace3a
Merge branch 'master' into rh-polkadot-propose
rphmeier Feb 20, 2018
a87afa7
block builder logic for polkadot
rphmeier Feb 20, 2018
e891649
some tests for the polkadot API
rphmeier Feb 20, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions polkadot/api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
242 changes: 231 additions & 11 deletions polkadot/api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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! {
Expand All @@ -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>) {
Expand All @@ -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>>;

Expand All @@ -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,
Expand All @@ -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)
}
Expand All @@ -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());
}
}
4 changes: 2 additions & 2 deletions polkadot/collator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)))
);

Expand Down
3 changes: 3 additions & 0 deletions polkadot/consensus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
Loading