Proposal creation and evaluation to plug into BFT (#77)

* reshuffle consensus libraries

* polkadot-useful type definitions for statement table

* begin BftService

* primary selection logic

* bft service implementation without I/O

* extract out `BlockImport` trait

* allow bft primitives to compile on wasm

* Block builder (substrate)

* take polkadot-consensus down to the core.

* test for preemption

* fix test build

* Fix wasm build

* Bulid on any block

* Test for block builder.

* Block import tests for client.

* Tidy ups

* clean up block builder instantiation

* justification verification logic

* JustifiedHeader and import

* Propert block generation for tests

* network and tablerouter trait

* use statement import to drive creation of further statements

* Fixed rpc tests

* custom error type for consensus

* create proposer

* asynchronous proposal evaluation

* inherent transactions in polkadot runtime

* fix tests to match real polkadot block constraints

* implicitly generate inherent functions

* add inherent transaction functionality to block body

* block builder logic for polkadot

* some tests for the polkadot API
This commit is contained in:
Robert Habermeier
2018-02-25 10:58:17 +01:00
committed by Gav Wood
parent ec9060460c
commit 1f2d01566e
30 changed files with 1300 additions and 300 deletions
+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()
);
@@ -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, || {
@@ -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);
}
}