mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 18:01:03 +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
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use runtime::{system, parachains, consensus, session};
|
||||
use runtime::{system, parachains, consensus, session, timestamp};
|
||||
|
||||
impl_stubs!(
|
||||
execute_block => |block| system::internal::execute_block(block),
|
||||
@@ -23,5 +23,6 @@ impl_stubs!(
|
||||
validator_count => |()| session::validator_count(),
|
||||
validators => |()| session::validators(),
|
||||
authorities => |()| consensus::authorities(),
|
||||
duty_roster => |()| parachains::calculate_duty_roster()
|
||||
duty_roster => |()| parachains::calculate_duty_roster(),
|
||||
get_timestamp => |()| timestamp::get()
|
||||
);
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
//! Environment API: Allows certain information to be accessed throughout the runtime.
|
||||
|
||||
use rstd::boxed::Box;
|
||||
use rstd::mem;
|
||||
use rstd::cell::RefCell;
|
||||
use rstd::rc::Rc;
|
||||
|
||||
@@ -32,6 +31,8 @@ pub struct Environment {
|
||||
pub parent_hash: Hash,
|
||||
/// The current block digest.
|
||||
pub digest: Digest,
|
||||
/// The current transaction index
|
||||
pub transaction_index: u64,
|
||||
}
|
||||
|
||||
/// Do something with the environment and return its value. Keep the function short.
|
||||
@@ -52,7 +53,7 @@ fn env() -> Rc<RefCell<Environment>> {
|
||||
let singleton: Rc<RefCell<Environment>> = Rc::new(RefCell::new(Default::default()));
|
||||
|
||||
// Put it in the heap so it can outlive this call
|
||||
SINGLETON = mem::transmute(Box::new(singleton));
|
||||
SINGLETON = Box::into_raw(Box::new(singleton)) as *const _;
|
||||
}
|
||||
|
||||
// Now we give out a copy of the data that is safe to use concurrently.
|
||||
@@ -73,7 +74,7 @@ fn env() -> Rc<RefCell<Environment>> {
|
||||
let singleton: Rc<RefCell<Environment>> = Rc::new(RefCell::new(Default::default()));
|
||||
|
||||
// Put it in the heap so it can outlive this call
|
||||
*s.borrow_mut() = mem::transmute(Box::new(singleton));
|
||||
*s.borrow_mut() = Box::into_raw(Box::new(singleton)) as *const _;
|
||||
}
|
||||
|
||||
// Now we give out a copy of the data that is safe to use concurrently.
|
||||
|
||||
@@ -41,7 +41,7 @@ pub mod api;
|
||||
pub mod transaction {
|
||||
use rstd::ops;
|
||||
use polkadot_primitives::Signature;
|
||||
pub use polkadot_primitives::{Transaction, UncheckedTransaction};
|
||||
pub use polkadot_primitives::{Transaction, Function, UncheckedTransaction};
|
||||
|
||||
/// A type-safe indicator that a transaction has been checked.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
@@ -63,10 +63,20 @@ pub mod transaction {
|
||||
}
|
||||
}
|
||||
|
||||
/// Check the signature on a transaction.
|
||||
///
|
||||
/// On failure, return the transaction back.
|
||||
pub fn check(tx: UncheckedTransaction) -> Result<CheckedTransaction, UncheckedTransaction> {
|
||||
/// Check the validity of a transaction: whether it can appear at the given index
|
||||
/// and whether it is correctly authenticated.
|
||||
pub fn check(tx: UncheckedTransaction, index: u64) -> Result<CheckedTransaction, UncheckedTransaction> {
|
||||
match tx.transaction.function.inherent_index() {
|
||||
Some(correct_index) => {
|
||||
if index != correct_index || !tx.is_well_formed() { return Err(tx) }
|
||||
return Ok(CheckedTransaction(tx));
|
||||
}
|
||||
None => {
|
||||
// non-inherent functions must appear after inherent.
|
||||
if index < Function::inherent_functions() { return Err(tx) }
|
||||
}
|
||||
}
|
||||
|
||||
let msg = ::codec::Slicable::encode(&tx.transaction);
|
||||
if ::runtime_io::ed25519_verify(&tx.signature.0, &msg, &tx.transaction.signed) {
|
||||
Ok(CheckedTransaction(tx))
|
||||
|
||||
@@ -24,12 +24,12 @@ use codec::{KeyedVec, Slicable};
|
||||
use runtime_support::{Hashable, storage};
|
||||
use environment::with_env;
|
||||
use polkadot_primitives::{AccountId, Hash, TxOrder, BlockNumber, Block, Header,
|
||||
UncheckedTransaction, Function, Log};
|
||||
UncheckedTransaction, Function, InherentFunction, Log};
|
||||
use runtime::{staking, session};
|
||||
|
||||
const NONCE_OF: &[u8] = b"sys:non:";
|
||||
const BLOCK_HASH_AT: &[u8] = b"sys:old:";
|
||||
const CODE: &[u8] = b"sys:cod";
|
||||
const TEMP_TRANSACTION_NUMBER: &[u8] = b"temp:txcount:";
|
||||
|
||||
/// The current block number being processed. Set by `execute_block`.
|
||||
pub fn block_number() -> BlockNumber {
|
||||
@@ -71,8 +71,10 @@ pub mod internal {
|
||||
// any initial checks
|
||||
initial_checks(&block);
|
||||
|
||||
// execute transactions
|
||||
block.transactions.iter().cloned().for_each(super::execute_transaction);
|
||||
// execute all transactions, inherent or otherwise.
|
||||
for (tx_num, tx) in block.all_transactions().enumerate() {
|
||||
super::execute_transaction(tx, tx_num as u64);
|
||||
}
|
||||
|
||||
// post-transactional book-keeping.
|
||||
staking::internal::check_new_era();
|
||||
@@ -87,6 +89,8 @@ pub mod internal {
|
||||
|
||||
/// Execute a transaction outside of the block execution function.
|
||||
/// This doesn't attempt to validate anything regarding the block.
|
||||
/// Note that when building a block transaction by transaction, the
|
||||
/// inherent methods must be called manually.
|
||||
pub fn execute_transaction(utx: UncheckedTransaction, mut header: Header) -> Header {
|
||||
// populate environment from header.
|
||||
with_env(|e| {
|
||||
@@ -95,11 +99,16 @@ pub mod internal {
|
||||
mem::swap(&mut header.digest, &mut e.digest);
|
||||
});
|
||||
|
||||
super::execute_transaction(utx);
|
||||
let tx_num: u64 = storage::get_or_default(TEMP_TRANSACTION_NUMBER);
|
||||
|
||||
super::execute_transaction(utx, tx_num);
|
||||
|
||||
with_env(|e| {
|
||||
mem::swap(&mut header.digest, &mut e.digest);
|
||||
});
|
||||
|
||||
storage::put(TEMP_TRANSACTION_NUMBER, &(tx_num + 1));
|
||||
|
||||
header
|
||||
}
|
||||
|
||||
@@ -113,6 +122,11 @@ pub mod internal {
|
||||
mem::swap(&mut header.digest, &mut e.digest);
|
||||
});
|
||||
|
||||
let tx_count: u64 = storage::take_or_default(TEMP_TRANSACTION_NUMBER);
|
||||
if tx_count < Function::inherent_functions() {
|
||||
panic!("Not enough transactions provided to fulfill all inherent functions.");
|
||||
}
|
||||
|
||||
staking::internal::check_new_era();
|
||||
session::internal::check_rotate_session();
|
||||
|
||||
@@ -125,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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user