mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 19:11:02 +00:00
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:
committed by
Gav Wood
parent
ec9060460c
commit
1f2d01566e
@@ -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]),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user