mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 09:51:02 +00:00
Modularised dispatch (#95)
* Completely rework dispatch mechanism into something modular. Not yet complete but 75% there. * Council vote tests. * Fix tests. * whitespace. * Fix demo runtime tests. * Fix up tests. * Remove dead code. * Use match for Id * Make PrivPass better protected. * Address other grumbles. * Give PrivPass a private member. * Testing PrivPass. * Add docs.
This commit is contained in:
Generated
+2
@@ -255,6 +255,8 @@ dependencies = [
|
||||
"integer-sqrt 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-keyring 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
|
||||
@@ -41,11 +41,13 @@ mod tests {
|
||||
use codec::{KeyedVec, Slicable, Joiner};
|
||||
use keyring::Keyring::{self, Alice, Bob};
|
||||
use runtime_support::Hashable;
|
||||
use demo_runtime::runtime::staking::{self, balance, BALANCE_OF};
|
||||
use state_machine::{CodeExecutor, TestExternalities};
|
||||
use primitives::twox_128;
|
||||
use demo_primitives::{Hash, Header, BlockNumber, Block, Digest, Transaction,
|
||||
UncheckedTransaction, Function};
|
||||
use demo_primitives::{Hash, Header, BlockNumber, Digest};
|
||||
use demo_runtime::transaction::{Transaction, UncheckedTransaction};
|
||||
use demo_runtime::block::Block;
|
||||
use demo_runtime::runtime::staking::{self, balance, BALANCE_OF};
|
||||
use demo_runtime::dispatch;
|
||||
use ed25519::{Public, Pair};
|
||||
|
||||
const BLOATY_CODE: &[u8] = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm");
|
||||
@@ -62,7 +64,7 @@ mod tests {
|
||||
let transaction = Transaction {
|
||||
signed: Alice.into(),
|
||||
nonce: 0,
|
||||
function: Function::StakingTransfer(Bob.into(), 69),
|
||||
function: dispatch::PubCall::Staking(staking::public::Call::transfer(Bob.into(), 69)),
|
||||
};
|
||||
let signature = Keyring::from_raw_public(transaction.signed).unwrap()
|
||||
.sign(&transaction.encode());
|
||||
@@ -73,7 +75,8 @@ mod tests {
|
||||
#[test]
|
||||
fn panic_execution_with_foreign_code_gives_error() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TRANSACTION_FEE).to_vec() => vec![0u8; 8]
|
||||
];
|
||||
|
||||
let r = Executor::new().call(&mut t, BLOATY_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx()));
|
||||
@@ -83,7 +86,8 @@ mod tests {
|
||||
#[test]
|
||||
fn panic_execution_with_native_equivalent_code_gives_error() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TRANSACTION_FEE).to_vec() => vec![0u8; 8]
|
||||
];
|
||||
|
||||
let r = Executor::new().call(&mut t, COMPACT_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx()));
|
||||
@@ -93,7 +97,8 @@ mod tests {
|
||||
#[test]
|
||||
fn successful_execution_with_native_equivalent_code_gives_ok() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TRANSACTION_FEE).to_vec() => vec![0u8; 8]
|
||||
];
|
||||
|
||||
let r = Executor::new().call(&mut t, COMPACT_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx()));
|
||||
@@ -108,7 +113,8 @@ mod tests {
|
||||
#[test]
|
||||
fn successful_execution_with_foreign_code_gives_ok() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TRANSACTION_FEE).to_vec() => vec![0u8; 8]
|
||||
];
|
||||
|
||||
let r = Executor::new().call(&mut t, BLOATY_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx()));
|
||||
@@ -152,11 +158,11 @@ mod tests {
|
||||
construct_block(
|
||||
1,
|
||||
[69u8; 32].into(),
|
||||
hex!("970ae19447bef129c88ee80c72797fa9dfeda4ca1a26d10102b669d776eb0ccf").into(),
|
||||
hex!("cfb76a83e40aa6a0d3f92255e6229e74808cae31d9f46053f31129b797540d03").into(),
|
||||
vec![Transaction {
|
||||
signed: Alice.into(),
|
||||
nonce: 0,
|
||||
function: Function::StakingTransfer(Bob.into(), 69),
|
||||
function: dispatch::PubCall::Staking(staking::public::Call::transfer(Bob.into(), 69)),
|
||||
}]
|
||||
)
|
||||
}
|
||||
@@ -165,17 +171,17 @@ mod tests {
|
||||
construct_block(
|
||||
2,
|
||||
block1().1,
|
||||
hex!("347ece6ef0d193bd7c2bfbda17706b82eb24c0965f415784a44b138f0df034cd").into(),
|
||||
hex!("c713bd003e303648e8d904bcfa44084865c9b70c398547e678028cc7cf60907f").into(),
|
||||
vec![
|
||||
Transaction {
|
||||
signed: Bob.into(),
|
||||
nonce: 0,
|
||||
function: Function::StakingTransfer(Alice.into(), 5),
|
||||
function: dispatch::PubCall::Staking(staking::public::Call::transfer(Alice.into(), 5)),
|
||||
},
|
||||
Transaction {
|
||||
signed: Alice.into(),
|
||||
nonce: 1,
|
||||
function: Function::StakingTransfer(Bob.into(), 15),
|
||||
function: dispatch::PubCall::Staking(staking::public::Call::transfer(Bob.into(), 15)),
|
||||
}
|
||||
]
|
||||
)
|
||||
@@ -188,15 +194,15 @@ mod tests {
|
||||
Executor::new().call(&mut t, COMPACT_CODE, "execute_block", &block1().0).unwrap();
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(balance(&Alice), 42);
|
||||
assert_eq!(balance(&Alice), 41);
|
||||
assert_eq!(balance(&Bob), 69);
|
||||
});
|
||||
|
||||
Executor::new().call(&mut t, COMPACT_CODE, "execute_block", &block2().0).unwrap();
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(balance(&Alice), 32);
|
||||
assert_eq!(balance(&Bob), 79);
|
||||
assert_eq!(balance(&Alice), 30);
|
||||
assert_eq!(balance(&Bob), 78);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -207,22 +213,23 @@ mod tests {
|
||||
WasmExecutor.call(&mut t, COMPACT_CODE, "execute_block", &block1().0).unwrap();
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(balance(&Alice), 42);
|
||||
assert_eq!(balance(&Alice), 41);
|
||||
assert_eq!(balance(&Bob), 69);
|
||||
});
|
||||
|
||||
WasmExecutor.call(&mut t, COMPACT_CODE, "execute_block", &block2().0).unwrap();
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(balance(&Alice), 32);
|
||||
assert_eq!(balance(&Bob), 79);
|
||||
assert_eq!(balance(&Alice), 30);
|
||||
assert_eq!(balance(&Bob), 78);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panic_execution_gives_error() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TRANSACTION_FEE).to_vec() => vec![0u8; 8]
|
||||
];
|
||||
|
||||
let foreign_code = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm");
|
||||
@@ -233,7 +240,8 @@ mod tests {
|
||||
#[test]
|
||||
fn successful_execution_gives_ok() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TRANSACTION_FEE).to_vec() => vec![0u8; 8]
|
||||
];
|
||||
|
||||
let foreign_code = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm");
|
||||
|
||||
@@ -21,7 +21,6 @@ use primitives::bytes;
|
||||
use primitives::H256;
|
||||
use rstd::vec::Vec;
|
||||
use codec::{Input, Slicable};
|
||||
use transaction::UncheckedTransaction;
|
||||
|
||||
pub use primitives::block::Id;
|
||||
|
||||
@@ -31,9 +30,6 @@ pub type Number = u64;
|
||||
/// Hash used to refer to a block hash.
|
||||
pub type HeaderHash = H256;
|
||||
|
||||
/// Hash used to refer to a transaction hash.
|
||||
pub type TransactionHash = H256;
|
||||
|
||||
/// Execution log (event)
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
@@ -69,35 +65,6 @@ impl Slicable for Digest {
|
||||
}
|
||||
}
|
||||
|
||||
/// The block "body": A bunch of transactions.
|
||||
pub type Body = Vec<UncheckedTransaction>;
|
||||
|
||||
/// A block on the chain.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub struct Block {
|
||||
/// The block header.
|
||||
pub header: Header,
|
||||
/// All relay-chain transactions.
|
||||
pub transactions: Body,
|
||||
}
|
||||
|
||||
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 })
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
|
||||
v.extend(self.header.encode());
|
||||
v.extend(self.transactions.encode());
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
/// Header for a block.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
|
||||
@@ -45,11 +45,9 @@ macro_rules! try_opt {
|
||||
}
|
||||
|
||||
pub mod block;
|
||||
pub mod transaction;
|
||||
|
||||
pub use self::block::{Header, Block, Log, Digest};
|
||||
pub use self::block::{Header, Log, Digest};
|
||||
pub use self::block::Number as BlockNumber;
|
||||
pub use self::transaction::{Transaction, UncheckedTransaction, Function, Proposal, VoteThreshold};
|
||||
|
||||
/// Alias to Ed25519 pubkey that identifies an account on the relay chain. This will almost
|
||||
/// certainly continue to be the same as the substrate's `AuthorityId`.
|
||||
|
||||
@@ -1,495 +0,0 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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.
|
||||
|
||||
// Substrate Demo 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 Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Transaction type.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use codec::{Input, Slicable, NonTrivialSlicable};
|
||||
use {AccountId, SessionKey};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::fmt;
|
||||
|
||||
use block::Number as BlockNumber;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[repr(u8)]
|
||||
enum InternalFunctionId {
|
||||
SystemSetCode = 0x00,
|
||||
|
||||
SessionSetLength = 0x10,
|
||||
SessionForceNewSession = 0x11,
|
||||
|
||||
StakingSetSessionsPerEra = 0x20,
|
||||
StakingSetBondingDuration = 0x21,
|
||||
StakingSetValidatorCount = 0x22,
|
||||
StakingForceNewEra = 0x23,
|
||||
|
||||
DemocracyCancelReferendum = 0x30,
|
||||
DemocracyStartReferendum = 0x31,
|
||||
|
||||
CouncilSetDesiredSeats = 0x40,
|
||||
CouncilRemoveMember = 0x41,
|
||||
CouncilSetPresentationDuration = 0x42,
|
||||
CouncilSetTermDuration = 0x43,
|
||||
|
||||
CouncilVoteSetCooloffPeriod = 0x50,
|
||||
CouncilVoteSetVotingPeriod = 0x51,
|
||||
}
|
||||
|
||||
impl InternalFunctionId {
|
||||
/// Derive `Some` value from a `u8`, or `None` if it's invalid.
|
||||
fn from_u8(value: u8) -> Option<InternalFunctionId> {
|
||||
let functions = [
|
||||
InternalFunctionId::SystemSetCode,
|
||||
InternalFunctionId::SessionSetLength,
|
||||
InternalFunctionId::SessionForceNewSession,
|
||||
InternalFunctionId::StakingSetSessionsPerEra,
|
||||
InternalFunctionId::StakingSetBondingDuration,
|
||||
InternalFunctionId::StakingSetValidatorCount,
|
||||
InternalFunctionId::StakingForceNewEra,
|
||||
InternalFunctionId::DemocracyCancelReferendum,
|
||||
InternalFunctionId::DemocracyStartReferendum,
|
||||
InternalFunctionId::CouncilSetDesiredSeats,
|
||||
InternalFunctionId::CouncilRemoveMember,
|
||||
InternalFunctionId::CouncilSetPresentationDuration,
|
||||
InternalFunctionId::CouncilSetTermDuration,
|
||||
InternalFunctionId::CouncilVoteSetCooloffPeriod,
|
||||
InternalFunctionId::CouncilVoteSetVotingPeriod,
|
||||
];
|
||||
functions.iter().map(|&f| f).find(|&f| value == f as u8)
|
||||
}
|
||||
}
|
||||
|
||||
/// A means of determining whether a referendum has gone through or not.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub enum VoteThreshold {
|
||||
/// A supermajority of approvals is needed to pass this vote.
|
||||
SuperMajorityApprove,
|
||||
/// A supermajority of rejects is needed to fail this vote.
|
||||
SuperMajorityAgainst,
|
||||
/// A simple majority of approvals is needed to pass this vote.
|
||||
SimpleMajority,
|
||||
}
|
||||
|
||||
impl Slicable for VoteThreshold {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
u8::decode(input).and_then(|v| match v {
|
||||
0 => Some(VoteThreshold::SuperMajorityApprove),
|
||||
1 => Some(VoteThreshold::SuperMajorityAgainst),
|
||||
2 => Some(VoteThreshold::SimpleMajority),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
match *self {
|
||||
VoteThreshold::SuperMajorityApprove => 0u8,
|
||||
VoteThreshold::SuperMajorityAgainst => 1u8,
|
||||
VoteThreshold::SimpleMajority => 2u8,
|
||||
}.using_encoded(f)
|
||||
}
|
||||
}
|
||||
impl NonTrivialSlicable for VoteThreshold {}
|
||||
|
||||
/// Internal functions that can be dispatched to.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Proposal {
|
||||
SystemSetCode(Vec<u8>),
|
||||
SessionSetLength(BlockNumber),
|
||||
SessionForceNewSession,
|
||||
StakingSetSessionsPerEra(BlockNumber),
|
||||
StakingSetBondingDuration(BlockNumber),
|
||||
StakingSetValidatorCount(u32),
|
||||
StakingForceNewEra,
|
||||
DemocracyStartReferendum(Box<Proposal>, VoteThreshold),
|
||||
DemocracyCancelReferendum(u32),
|
||||
CouncilSetDesiredSeats(u32),
|
||||
CouncilRemoveMember(AccountId),
|
||||
CouncilSetPresentationDuration(BlockNumber),
|
||||
CouncilSetTermDuration(BlockNumber),
|
||||
CouncilVoteSetCooloffPeriod(BlockNumber),
|
||||
CouncilVoteSetVotingPeriod(BlockNumber),
|
||||
}
|
||||
|
||||
impl Slicable for Proposal {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
let id = u8::decode(input).and_then(InternalFunctionId::from_u8)?;
|
||||
let function = match id {
|
||||
InternalFunctionId::SystemSetCode =>
|
||||
Proposal::SystemSetCode(Slicable::decode(input)?),
|
||||
InternalFunctionId::SessionSetLength =>
|
||||
Proposal::SessionSetLength(Slicable::decode(input)?),
|
||||
InternalFunctionId::SessionForceNewSession =>
|
||||
Proposal::SessionForceNewSession,
|
||||
InternalFunctionId::StakingSetSessionsPerEra =>
|
||||
Proposal::StakingSetSessionsPerEra(Slicable::decode(input)?),
|
||||
InternalFunctionId::StakingSetBondingDuration =>
|
||||
Proposal::StakingSetBondingDuration(Slicable::decode(input)?),
|
||||
InternalFunctionId::StakingSetValidatorCount =>
|
||||
Proposal::StakingSetValidatorCount(Slicable::decode(input)?),
|
||||
InternalFunctionId::StakingForceNewEra =>
|
||||
Proposal::StakingForceNewEra,
|
||||
InternalFunctionId::DemocracyStartReferendum => {
|
||||
let a = Slicable::decode(input)?;
|
||||
let b = Slicable::decode(input)?;
|
||||
Proposal::DemocracyStartReferendum(Box::new(a), b)
|
||||
}
|
||||
InternalFunctionId::DemocracyCancelReferendum =>
|
||||
Proposal::DemocracyCancelReferendum(Slicable::decode(input)?),
|
||||
InternalFunctionId::CouncilSetDesiredSeats =>
|
||||
Proposal::CouncilSetDesiredSeats(Slicable::decode(input)?),
|
||||
InternalFunctionId::CouncilRemoveMember =>
|
||||
Proposal::CouncilRemoveMember(Slicable::decode(input)?),
|
||||
InternalFunctionId::CouncilSetPresentationDuration =>
|
||||
Proposal::CouncilSetPresentationDuration(Slicable::decode(input)?),
|
||||
InternalFunctionId::CouncilSetTermDuration =>
|
||||
Proposal::CouncilSetTermDuration(Slicable::decode(input)?),
|
||||
InternalFunctionId::CouncilVoteSetCooloffPeriod =>
|
||||
Proposal::CouncilVoteSetCooloffPeriod(Slicable::decode(input)?),
|
||||
InternalFunctionId::CouncilVoteSetVotingPeriod =>
|
||||
Proposal::CouncilVoteSetVotingPeriod(Slicable::decode(input)?),
|
||||
};
|
||||
|
||||
Some(function)
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
match *self {
|
||||
Proposal::SystemSetCode(ref data) => {
|
||||
(InternalFunctionId::SystemSetCode as u8).using_encoded(|s| v.extend(s));
|
||||
data.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
Proposal::SessionSetLength(ref data) => {
|
||||
(InternalFunctionId::SessionSetLength as u8).using_encoded(|s| v.extend(s));
|
||||
data.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
Proposal::SessionForceNewSession => {
|
||||
(InternalFunctionId::SessionForceNewSession as u8).using_encoded(|s| v.extend(s));
|
||||
}
|
||||
Proposal::StakingSetSessionsPerEra(ref data) => {
|
||||
(InternalFunctionId::StakingSetSessionsPerEra as u8).using_encoded(|s| v.extend(s));
|
||||
data.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
Proposal::StakingSetBondingDuration(ref data) => {
|
||||
(InternalFunctionId::StakingSetBondingDuration as u8).using_encoded(|s| v.extend(s));
|
||||
data.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
Proposal::StakingSetValidatorCount(ref data) => {
|
||||
(InternalFunctionId::StakingSetValidatorCount as u8).using_encoded(|s| v.extend(s));
|
||||
data.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
Proposal::StakingForceNewEra => {
|
||||
(InternalFunctionId::StakingForceNewEra as u8).using_encoded(|s| v.extend(s));
|
||||
}
|
||||
Proposal::DemocracyCancelReferendum(ref data) => {
|
||||
(InternalFunctionId::DemocracyCancelReferendum as u8).using_encoded(|s| v.extend(s));
|
||||
data.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
_ => { unimplemented!() }
|
||||
}
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
/// Public functions that can be dispatched to.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[repr(u8)]
|
||||
enum FunctionId {
|
||||
TimestampSet = 0x00,
|
||||
|
||||
SessionSetKey = 0x10,
|
||||
|
||||
StakingStake = 0x20,
|
||||
StakingUnstake = 0x21,
|
||||
StakingTransfer = 0x22,
|
||||
|
||||
CouncilVotePropose = 0x30,
|
||||
CouncilVoteVote = 0x31,
|
||||
CouncilVoteVeto = 0x32,
|
||||
|
||||
CouncilSetApprovals = 0x40,
|
||||
CouncilReapInactiveVoter = 0x41,
|
||||
CouncilRetractVoter = 0x42,
|
||||
CouncilSubmitCandidacy = 0x43,
|
||||
CouncilPresentWinner = 0x44,
|
||||
|
||||
DemocracyPropose = 0x50,
|
||||
DemocracySecond = 0x51,
|
||||
DemocracyVote = 0x52,
|
||||
}
|
||||
|
||||
impl FunctionId {
|
||||
/// Derive `Some` value from a `u8`, or `None` if it's invalid.
|
||||
fn from_u8(value: u8) -> Option<FunctionId> {
|
||||
use self::*;
|
||||
let functions = [FunctionId::StakingStake, FunctionId::StakingUnstake,
|
||||
FunctionId::StakingTransfer, FunctionId::SessionSetKey, FunctionId::TimestampSet,
|
||||
FunctionId::CouncilVotePropose, FunctionId::CouncilVoteVote, FunctionId::CouncilVoteVeto,
|
||||
FunctionId::CouncilSetApprovals, FunctionId::CouncilReapInactiveVoter,
|
||||
FunctionId::CouncilRetractVoter, FunctionId::CouncilSubmitCandidacy,
|
||||
FunctionId::CouncilPresentWinner, FunctionId::DemocracyPropose,
|
||||
FunctionId::DemocracySecond, FunctionId::DemocracyVote,
|
||||
];
|
||||
functions.iter().map(|&f| f).find(|&f| value == f as u8)
|
||||
}
|
||||
}
|
||||
|
||||
/// Functions on the runtime.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Function {
|
||||
TimestampSet(u64),
|
||||
|
||||
SessionSetKey(SessionKey),
|
||||
|
||||
StakingStake,
|
||||
StakingUnstake,
|
||||
StakingTransfer(AccountId, u64),
|
||||
|
||||
CouncilVotePropose(Proposal),
|
||||
CouncilVoteVote([u8; 32], bool),
|
||||
CouncilVoteVeto([u8; 32]),
|
||||
|
||||
CouncilSetApprovals(Vec<bool>, u32),
|
||||
CouncilReapInactiveVoter(u32, AccountId, u32, u32),
|
||||
CouncilRetractVoter(u32),
|
||||
CouncilSubmitCandidacy(u32),
|
||||
CouncilPresentWinner(AccountId, u64, u32),
|
||||
|
||||
DemocracyPropose(Proposal, u64),
|
||||
DemocracySecond(u32),
|
||||
DemocracyVote(u32, bool),
|
||||
}
|
||||
|
||||
impl Slicable for Function {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
let id = u8::decode(input).and_then(FunctionId::from_u8)?;
|
||||
Some(match id {
|
||||
FunctionId::TimestampSet =>
|
||||
Function::TimestampSet(Slicable::decode(input)?),
|
||||
FunctionId::SessionSetKey =>
|
||||
Function::SessionSetKey(Slicable::decode(input)?),
|
||||
FunctionId::StakingStake => Function::StakingStake,
|
||||
FunctionId::StakingUnstake => Function::StakingUnstake,
|
||||
FunctionId::StakingTransfer => {
|
||||
let to = Slicable::decode(input)?;
|
||||
let amount = Slicable::decode(input)?;
|
||||
Function::StakingTransfer(to, amount)
|
||||
}
|
||||
FunctionId::CouncilVotePropose => Function::CouncilVotePropose(Slicable::decode(input)?),
|
||||
FunctionId::CouncilVoteVote => {
|
||||
let a = Slicable::decode(input)?;
|
||||
let b = Slicable::decode(input)?;
|
||||
Function::CouncilVoteVote(a, b)
|
||||
}
|
||||
FunctionId::CouncilVoteVeto => Function::CouncilVoteVeto(Slicable::decode(input)?),
|
||||
FunctionId::CouncilSetApprovals => {
|
||||
let a = Slicable::decode(input)?;
|
||||
let b = Slicable::decode(input)?;
|
||||
Function::CouncilSetApprovals(a, b)
|
||||
}
|
||||
FunctionId::CouncilReapInactiveVoter => {
|
||||
let a = Slicable::decode(input)?;
|
||||
let b = Slicable::decode(input)?;
|
||||
let c = Slicable::decode(input)?;
|
||||
let d = Slicable::decode(input)?;
|
||||
Function::CouncilReapInactiveVoter(a, b, c, d)
|
||||
}
|
||||
FunctionId::CouncilRetractVoter => Function::CouncilRetractVoter(Slicable::decode(input)?),
|
||||
FunctionId::CouncilSubmitCandidacy => Function::CouncilSubmitCandidacy(Slicable::decode(input)?),
|
||||
FunctionId::CouncilPresentWinner => {
|
||||
let a = Slicable::decode(input)?;
|
||||
let b = Slicable::decode(input)?;
|
||||
let c = Slicable::decode(input)?;
|
||||
Function::CouncilPresentWinner(a, b, c)
|
||||
}
|
||||
FunctionId::DemocracyPropose => {
|
||||
let a = Slicable::decode(input)?;
|
||||
let b = Slicable::decode(input)?;
|
||||
Function::DemocracyPropose(a, b)
|
||||
}
|
||||
FunctionId::DemocracySecond => Function::DemocracySecond(Slicable::decode(input)?),
|
||||
FunctionId::DemocracyVote => {
|
||||
let a = Slicable::decode(input)?;
|
||||
let b = Slicable::decode(input)?;
|
||||
Function::DemocracyVote(a, b)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
match *self {
|
||||
Function::TimestampSet(ref data) => {
|
||||
(FunctionId::TimestampSet as u8).using_encoded(|s| v.extend(s));
|
||||
data.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
Function::SessionSetKey(ref data) => {
|
||||
(FunctionId::SessionSetKey as u8).using_encoded(|s| v.extend(s));
|
||||
data.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
Function::StakingStake => {
|
||||
(FunctionId::StakingStake as u8).using_encoded(|s| v.extend(s));
|
||||
}
|
||||
Function::StakingUnstake => {
|
||||
(FunctionId::StakingUnstake as u8).using_encoded(|s| v.extend(s));
|
||||
}
|
||||
Function::StakingTransfer(ref to, ref amount) => {
|
||||
(FunctionId::StakingTransfer as u8).using_encoded(|s| v.extend(s));
|
||||
to.using_encoded(|s| v.extend(s));
|
||||
amount.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
_ => { unimplemented!() }
|
||||
}
|
||||
|
||||
v
|
||||
}
|
||||
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
f(self.encode().as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
/// A vetted and verified transaction from the external world.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub struct Transaction {
|
||||
/// Who signed it (note this is not a signature).
|
||||
pub signed: super::AccountId,
|
||||
/// The number of transactions have come before from the same signer.
|
||||
pub nonce: super::TxOrder,
|
||||
/// The function that should be called.
|
||||
pub function: Function,
|
||||
}
|
||||
|
||||
impl Slicable for Transaction {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Transaction {
|
||||
signed: try_opt!(Slicable::decode(input)),
|
||||
nonce: try_opt!(Slicable::decode(input)),
|
||||
function: try_opt!(Slicable::decode(input)),
|
||||
})
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
|
||||
self.signed.using_encoded(|s| v.extend(s));
|
||||
self.nonce.using_encoded(|s| v.extend(s));
|
||||
self.function.using_encoded(|s| v.extend(s));
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
impl ::codec::NonTrivialSlicable for Transaction {}
|
||||
|
||||
/// A transactions right from the external world. Unchecked.
|
||||
#[derive(Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
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,
|
||||
}
|
||||
|
||||
impl Slicable for UncheckedTransaction {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
// This is a little more complicated than usual since the binary format must be compatible
|
||||
// with substrate's generic `Vec<u8>` type. Basically this just means accepting that there
|
||||
// will be a prefix of u32, which has the total number of bytes following (we don't need
|
||||
// to use this).
|
||||
let _length_do_not_remove_me_see_above: u32 = try_opt!(Slicable::decode(input));
|
||||
|
||||
Some(UncheckedTransaction {
|
||||
transaction: try_opt!(Slicable::decode(input)),
|
||||
signature: try_opt!(Slicable::decode(input)),
|
||||
})
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
|
||||
// need to prefix with the total length as u32 to ensure it's binary comptible with
|
||||
// Vec<u8>. we'll make room for it here, then overwrite once we know the length.
|
||||
v.extend(&[0u8; 4]);
|
||||
|
||||
self.transaction.signed.using_encoded(|s| v.extend(s));
|
||||
self.transaction.nonce.using_encoded(|s| v.extend(s));
|
||||
self.transaction.function.using_encoded(|s| v.extend(s));
|
||||
self.signature.using_encoded(|s| v.extend(s));
|
||||
|
||||
let length = (v.len() - 4) as u32;
|
||||
length.using_encoded(|s| v[0..4].copy_from_slice(s));
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
impl ::codec::NonTrivialSlicable for UncheckedTransaction {}
|
||||
|
||||
impl PartialEq for UncheckedTransaction {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.signature.iter().eq(other.signature.iter()) && self.transaction == other.transaction
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl fmt::Debug for UncheckedTransaction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "UncheckedTransaction({:?})", self.transaction)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use primitives;
|
||||
use ::codec::Slicable;
|
||||
use primitives::hexdisplay::HexDisplay;
|
||||
|
||||
#[test]
|
||||
fn serialize_unchecked() {
|
||||
let tx = UncheckedTransaction {
|
||||
transaction: Transaction {
|
||||
signed: [1; 32],
|
||||
nonce: 999u64,
|
||||
function: Function::TimestampSet(135135),
|
||||
},
|
||||
signature: primitives::hash::H512([0; 64]),
|
||||
};
|
||||
// 71000000
|
||||
// 0101010101010101010101010101010101010101010101010101010101010101
|
||||
// e703000000000000
|
||||
// 00
|
||||
// df0f0200
|
||||
// 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
|
||||
let v = Slicable::encode(&tx);
|
||||
println!("{}", HexDisplay::from(&v));
|
||||
assert_eq!(UncheckedTransaction::decode(&mut &v[..]).unwrap(), tx);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,8 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
rustc-hex = "1.0"
|
||||
hex-literal = "0.1.0"
|
||||
log = { version = "0.3", optional = true }
|
||||
serde = { version = "1.0", default_features = false }
|
||||
serde_derive = { version = "1.0", optional = true }
|
||||
substrate-codec = { path = "../../substrate/codec" }
|
||||
substrate-runtime-std = { path = "../../substrate/runtime-std" }
|
||||
substrate-runtime-io = { path = "../../substrate/runtime-io" }
|
||||
@@ -25,5 +27,7 @@ std = [
|
||||
"substrate-runtime-support/std",
|
||||
"substrate-primitives/std",
|
||||
"demo-primitives/std",
|
||||
"serde_derive",
|
||||
"serde/std",
|
||||
"log"
|
||||
]
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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.
|
||||
|
||||
// Substrate Demo 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 Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Block and header type definitions.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use codec::{Input, Slicable};
|
||||
use transaction::UncheckedTransaction;
|
||||
|
||||
pub use demo_primitives::block::{Header, Digest, Log, Number, HeaderHash};
|
||||
|
||||
/// The block "body": A bunch of transactions.
|
||||
pub type Body = Vec<UncheckedTransaction>;
|
||||
|
||||
/// A block on the chain.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub struct Block {
|
||||
/// The block header.
|
||||
pub header: Header,
|
||||
/// All relay-chain transactions.
|
||||
pub transactions: Body,
|
||||
}
|
||||
|
||||
impl Slicable for Block {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
let (header, transactions) = Slicable::decode(input)?;
|
||||
Some(Block { header, transactions })
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
|
||||
v.extend(self.header.encode());
|
||||
v.extend(self.transactions.encode());
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
@@ -16,79 +16,231 @@
|
||||
|
||||
//! Dispatch system. Just dispatches calls.
|
||||
|
||||
use demo_primitives::{Function, Proposal, AccountId};
|
||||
use runtime::{staking, system, session, democracy, council, council_vote, timestamp};
|
||||
use runtime::{staking, democracy};
|
||||
pub use rstd::prelude::Vec;
|
||||
pub use codec::{Slicable, Input, NonTrivialSlicable};
|
||||
|
||||
/// Dispatch a proposal.
|
||||
pub fn proposal(proposal: Proposal) {
|
||||
match proposal {
|
||||
Proposal::SystemSetCode(ref a) =>
|
||||
system::privileged::set_code(a),
|
||||
Proposal::SessionSetLength(a) =>
|
||||
session::privileged::set_length(a),
|
||||
Proposal::SessionForceNewSession =>
|
||||
session::privileged::force_new_session(),
|
||||
Proposal::StakingSetSessionsPerEra(a) =>
|
||||
staking::privileged::set_sessions_per_era(a),
|
||||
Proposal::StakingSetBondingDuration(a) =>
|
||||
staking::privileged::set_bonding_duration(a),
|
||||
Proposal::StakingSetValidatorCount(a) =>
|
||||
staking::privileged::set_validator_count(a),
|
||||
Proposal::StakingForceNewEra =>
|
||||
staking::privileged::force_new_era(),
|
||||
Proposal::DemocracyCancelReferendum(a) =>
|
||||
democracy::privileged::cancel_referendum(a),
|
||||
Proposal::DemocracyStartReferendum(a, b) =>
|
||||
democracy::privileged::start_referendum(*a, b),
|
||||
Proposal::CouncilSetDesiredSeats(a) =>
|
||||
council::privileged::set_desired_seats(a),
|
||||
Proposal::CouncilRemoveMember(a) =>
|
||||
council::privileged::remove_member(&a),
|
||||
Proposal::CouncilSetPresentationDuration(a) =>
|
||||
council::privileged::set_presentation_duration(a),
|
||||
Proposal::CouncilSetTermDuration(a) =>
|
||||
council::privileged::set_term_duration(a),
|
||||
Proposal::CouncilVoteSetCooloffPeriod(a) =>
|
||||
council_vote::privileged::set_cooloff_period(a),
|
||||
Proposal::CouncilVoteSetVotingPeriod(a) =>
|
||||
council_vote::privileged::set_voting_period(a),
|
||||
/// Implement a dispatch module to create a pairing of a dispatch trait and enum.
|
||||
#[macro_export]
|
||||
macro_rules! impl_dispatch {
|
||||
(
|
||||
pub mod $mod_name:ident;
|
||||
$(
|
||||
fn $fn_name:ident(
|
||||
$(
|
||||
$param_name:ident : $param:ty
|
||||
),*
|
||||
)
|
||||
= $id:expr ;
|
||||
)*
|
||||
) => {
|
||||
pub mod $mod_name {
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[repr(u32)]
|
||||
#[allow(non_camel_case_types)]
|
||||
enum Id {
|
||||
$(
|
||||
#[allow(non_camel_case_types)]
|
||||
$fn_name = $id,
|
||||
)*
|
||||
}
|
||||
|
||||
impl Id {
|
||||
/// Derive `Some` value from a `u8`, or `None` if it's invalid.
|
||||
fn from_u8(value: u8) -> Option<Id> {
|
||||
match value {
|
||||
$(
|
||||
$id => Some(Id::$fn_name),
|
||||
)*
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Call {
|
||||
$(
|
||||
#[allow(non_camel_case_types)]
|
||||
$fn_name ( $( $param ),* )
|
||||
,)*
|
||||
}
|
||||
|
||||
pub trait Dispatch: Sized {
|
||||
$(
|
||||
fn $fn_name (self, $( $param_name: $param ),* );
|
||||
)*
|
||||
}
|
||||
|
||||
impl Call {
|
||||
pub fn dispatch<D: Dispatch>(self, d: D) {
|
||||
match self {
|
||||
$(
|
||||
Call::$fn_name( $( $param_name ),* ) =>
|
||||
d.$fn_name( $( $param_name ),* ),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::dispatch::Slicable for Call {
|
||||
fn decode<I: $crate::dispatch::Input>(input: &mut I) -> Option<Self> {
|
||||
let id = u8::decode(input).and_then(Id::from_u8)?;
|
||||
Some(match id {
|
||||
$(
|
||||
Id::$fn_name => {
|
||||
$(
|
||||
let $param_name = $crate::dispatch::Slicable::decode(input)?;
|
||||
)*
|
||||
Call :: $fn_name( $( $param_name ),* )
|
||||
}
|
||||
)*
|
||||
})
|
||||
}
|
||||
|
||||
fn encode(&self) -> $crate::dispatch::Vec<u8> {
|
||||
let mut v = $crate::dispatch::Vec::new();
|
||||
match *self {
|
||||
$(
|
||||
Call::$fn_name(
|
||||
$(
|
||||
ref $param_name
|
||||
),*
|
||||
) => {
|
||||
(Id::$fn_name as u8).using_encoded(|s| v.extend(s));
|
||||
$(
|
||||
$param_name.using_encoded(|s| v.extend(s));
|
||||
)*
|
||||
}
|
||||
)*
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
f(self.encode().as_slice())
|
||||
}
|
||||
}
|
||||
impl $crate::dispatch::NonTrivialSlicable for Call {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Dispatch a function.
|
||||
pub fn function(function: &Function, transactor: &AccountId) {
|
||||
match *function {
|
||||
Function::StakingStake =>
|
||||
staking::public::stake(transactor),
|
||||
Function::StakingUnstake =>
|
||||
staking::public::unstake(transactor),
|
||||
Function::StakingTransfer(dest, value) =>
|
||||
staking::public::transfer(transactor, &dest, value),
|
||||
Function::SessionSetKey(session) =>
|
||||
session::public::set_key(transactor, &session),
|
||||
Function::TimestampSet(t) =>
|
||||
timestamp::public::set(t),
|
||||
Function::CouncilVotePropose(ref a) =>
|
||||
council_vote::public::propose(transactor, a),
|
||||
Function::CouncilVoteVote(ref a, b) =>
|
||||
council_vote::public::vote(transactor, a, b),
|
||||
Function::CouncilVoteVeto(ref a) =>
|
||||
council_vote::public::veto(transactor, a),
|
||||
Function::CouncilSetApprovals(ref a, b) =>
|
||||
council::public::set_approvals(transactor, a, b),
|
||||
Function::CouncilReapInactiveVoter(a, ref b, c, d) =>
|
||||
council::public::reap_inactive_voter(transactor, a, b, c, d),
|
||||
Function::CouncilRetractVoter(a) =>
|
||||
council::public::retract_voter(transactor, a),
|
||||
Function::CouncilSubmitCandidacy(a) =>
|
||||
council::public::submit_candidacy(transactor, a),
|
||||
Function::CouncilPresentWinner(ref a, b, c) =>
|
||||
council::public::present_winner(transactor, a, b, c),
|
||||
Function::DemocracyPropose(ref a, b) =>
|
||||
democracy::public::propose(transactor, a, b),
|
||||
Function::DemocracySecond(a) =>
|
||||
democracy::public::second(transactor, a),
|
||||
Function::DemocracyVote(a, b) =>
|
||||
democracy::public::vote(transactor, a, b),
|
||||
macro_rules! impl_meta_dispatch {
|
||||
(
|
||||
pub mod $super_name:ident;
|
||||
path $path:ident;
|
||||
trait $trait:ty;
|
||||
$(
|
||||
$camelcase:ident(mod $sub_name:ident) = $id:expr ;
|
||||
)*
|
||||
) => {
|
||||
pub mod $super_name {
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[repr(u32)]
|
||||
#[allow(non_camel_case_types)]
|
||||
enum Id {
|
||||
$(
|
||||
#[allow(non_camel_case_types)]
|
||||
$camelcase = $id,
|
||||
)*
|
||||
}
|
||||
|
||||
impl Id {
|
||||
/// Derive `Some` value from a `u8`, or `None` if it's invalid.
|
||||
fn from_u8(value: u8) -> Option<Id> {
|
||||
match value {
|
||||
$(
|
||||
$id => Some(Id::$camelcase),
|
||||
)*
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Call {
|
||||
$(
|
||||
#[allow(non_camel_case_types)]
|
||||
$camelcase ( $crate::runtime::$sub_name::$path::Call )
|
||||
,)*
|
||||
}
|
||||
|
||||
impl Call {
|
||||
pub fn dispatch(self, d: $trait) {
|
||||
match self {
|
||||
$(
|
||||
Call::$camelcase(x) => x.dispatch(d),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::dispatch::Slicable for Call {
|
||||
fn decode<I: $crate::dispatch::Input>(input: &mut I) -> Option<Self> {
|
||||
let id = u8::decode(input).and_then(Id::from_u8)?;
|
||||
Some(match id {
|
||||
$(
|
||||
Id::$camelcase =>
|
||||
Call::$camelcase( $crate::dispatch::Slicable::decode(input)? ),
|
||||
)*
|
||||
})
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = $crate::dispatch::Vec::new();
|
||||
match *self {
|
||||
$(
|
||||
Call::$camelcase( ref sub ) => {
|
||||
(Id::$camelcase as u8).using_encoded(|s| v.extend(s));
|
||||
sub.using_encoded(|s| v.extend(s));
|
||||
}
|
||||
)*
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
f(self.encode().as_slice())
|
||||
}
|
||||
}
|
||||
impl $crate::dispatch::NonTrivialSlicable for Call {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_meta_dispatch! {
|
||||
pub mod public;
|
||||
path public;
|
||||
trait staking::PublicPass;
|
||||
Session(mod session) = 1;
|
||||
Staking(mod staking) = 2;
|
||||
Timestamp(mod timestamp) = 3;
|
||||
Democracy(mod democracy) = 5;
|
||||
Council(mod council) = 6;
|
||||
CouncilVote(mod council) = 7;
|
||||
}
|
||||
|
||||
impl_meta_dispatch! {
|
||||
pub mod privileged;
|
||||
path privileged;
|
||||
trait democracy::PrivPass;
|
||||
System(mod system) = 0;
|
||||
Session(mod session) = 1;
|
||||
Staking(mod staking) = 2;
|
||||
Democracy(mod democracy) = 5;
|
||||
Council(mod council) = 6;
|
||||
CouncilVote(mod council) = 7;
|
||||
}
|
||||
|
||||
pub use self::privileged::Call as PrivCall;
|
||||
pub use self::public::Call as PubCall;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! The Substrate Demo runtime. This can be compiled with #[no_std], ready for Wasm.
|
||||
//! The Substrate Demo runtime. This can be compiled with ``#[no_std]`, ready for Wasm.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
#[cfg(any(feature = "std", test))] extern crate substrate_keyring as keyring;
|
||||
|
||||
#[cfg(feature = "std")] #[macro_use] extern crate serde_derive;
|
||||
#[cfg(feature = "std")] extern crate serde;
|
||||
|
||||
#[cfg(feature = "std")] extern crate rustc_hex;
|
||||
|
||||
extern crate substrate_codec as codec;
|
||||
@@ -33,48 +36,12 @@ extern crate demo_primitives;
|
||||
|
||||
extern crate integer_sqrt;
|
||||
|
||||
#[macro_use] pub mod dispatch;
|
||||
|
||||
pub mod block;
|
||||
pub mod transaction;
|
||||
pub mod environment;
|
||||
pub mod runtime;
|
||||
pub mod api;
|
||||
pub mod dispatch;
|
||||
|
||||
#[cfg(feature = "std")] pub mod genesismap;
|
||||
|
||||
/// Type definitions and helpers for transactions.
|
||||
pub mod transaction {
|
||||
use rstd::ops;
|
||||
use demo_primitives::Signature;
|
||||
pub use demo_primitives::{Transaction, UncheckedTransaction};
|
||||
|
||||
/// A type-safe indicator that a transaction has been checked.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
pub struct CheckedTransaction(UncheckedTransaction);
|
||||
|
||||
impl CheckedTransaction {
|
||||
/// Get a reference to the checked signature.
|
||||
pub fn signature(&self) -> &Signature {
|
||||
&self.0.signature
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for CheckedTransaction {
|
||||
type Target = Transaction;
|
||||
|
||||
fn deref(&self) -> &Transaction {
|
||||
&self.0.transaction
|
||||
}
|
||||
}
|
||||
|
||||
/// Check the signature on a transaction.
|
||||
///
|
||||
/// On failure, return the transaction back.
|
||||
pub fn check(tx: UncheckedTransaction) -> Result<CheckedTransaction, UncheckedTransaction> {
|
||||
let msg = ::codec::Slicable::encode(&tx.transaction);
|
||||
if ::runtime_io::ed25519_verify(&tx.signature.0, &msg, &tx.transaction.signed) {
|
||||
Ok(CheckedTransaction(tx))
|
||||
} else {
|
||||
Err(tx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,9 +20,11 @@ use rstd::prelude::*;
|
||||
use codec::{KeyedVec, Slicable, Input, NonTrivialSlicable};
|
||||
use runtime_support::Hashable;
|
||||
use runtime_support::storage;
|
||||
use demo_primitives::{Proposal, AccountId, Hash, BlockNumber};
|
||||
use demo_primitives::{AccountId, Hash, BlockNumber};
|
||||
use runtime::{system, democracy, council};
|
||||
use runtime::staking::Balance;
|
||||
use runtime::staking::{PublicPass, Balance};
|
||||
use runtime::democracy::PrivPass;
|
||||
use dispatch::PrivCall as Proposal;
|
||||
|
||||
type ProposalHash = [u8; 32];
|
||||
|
||||
@@ -119,12 +121,17 @@ fn take_proposal_if_expiring_at(n: BlockNumber) -> Option<(Proposal, ProposalHas
|
||||
}
|
||||
}
|
||||
|
||||
pub mod public {
|
||||
use super::*;
|
||||
impl_dispatch! {
|
||||
pub mod public;
|
||||
fn propose(proposal: Box<Proposal>) = 0;
|
||||
fn vote(proposal: ProposalHash, approve: bool) = 1;
|
||||
fn veto(proposal_hash: ProposalHash) = 2;
|
||||
}
|
||||
|
||||
pub fn propose(signed: &AccountId, proposal: &Proposal) {
|
||||
impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
fn propose(self, proposal: Box<Proposal>) {
|
||||
let expiry = system::block_number() + voting_period();
|
||||
assert!(will_still_be_councillor_at(signed, expiry));
|
||||
assert!(will_still_be_councillor_at(&self, expiry));
|
||||
|
||||
let proposal_hash = proposal.blake2_256();
|
||||
|
||||
@@ -135,64 +142,69 @@ pub mod public {
|
||||
proposals.sort_by_key(|&(expiry, _)| expiry);
|
||||
set_proposals(&proposals);
|
||||
|
||||
storage::put(&proposal_hash.to_keyed_vec(PROPOSAL_OF), proposal);
|
||||
storage::put(&proposal_hash.to_keyed_vec(PROPOSAL_VOTERS), &vec![*signed]);
|
||||
storage::put(&(proposal_hash, *signed).to_keyed_vec(COUNCIL_VOTE_OF), &true);
|
||||
storage::put(&proposal_hash.to_keyed_vec(PROPOSAL_OF), &proposal);
|
||||
storage::put(&proposal_hash.to_keyed_vec(PROPOSAL_VOTERS), &vec![*self]);
|
||||
storage::put(&(proposal_hash, *self).to_keyed_vec(COUNCIL_VOTE_OF), &true);
|
||||
}
|
||||
|
||||
pub fn vote(signed: &AccountId, proposal: &ProposalHash, approve: bool) {
|
||||
if vote_of(signed, proposal).is_none() {
|
||||
let mut voters = proposal_voters(proposal);
|
||||
voters.push(*signed);
|
||||
fn vote(self, proposal: ProposalHash, approve: bool) {
|
||||
if vote_of(&self, &proposal).is_none() {
|
||||
let mut voters = proposal_voters(&proposal);
|
||||
voters.push(*self);
|
||||
storage::put(&proposal.to_keyed_vec(PROPOSAL_VOTERS), &voters);
|
||||
}
|
||||
storage::put(&(*proposal, *signed).to_keyed_vec(COUNCIL_VOTE_OF), &approve);
|
||||
storage::put(&(proposal, *self).to_keyed_vec(COUNCIL_VOTE_OF), &approve);
|
||||
}
|
||||
|
||||
pub fn veto(signed: &AccountId, proposal_hash: &ProposalHash) {
|
||||
assert!(is_councillor(signed), "only councillors may veto council proposals");
|
||||
fn veto(self, proposal_hash: ProposalHash) {
|
||||
assert!(is_councillor(&self), "only councillors may veto council proposals");
|
||||
assert!(storage::exists(&proposal_hash.to_keyed_vec(PROPOSAL_VOTERS)), "proposal must exist to be vetoed");
|
||||
|
||||
let mut existing_vetoers = veto_of(&proposal_hash)
|
||||
.map(|pair| pair.1)
|
||||
.unwrap_or_else(Vec::new);
|
||||
let insert_position = existing_vetoers.binary_search(signed)
|
||||
let insert_position = existing_vetoers.binary_search(&self)
|
||||
.expect_err("a councillor may not veto a proposal twice");
|
||||
existing_vetoers.insert(insert_position, *signed);
|
||||
existing_vetoers.insert(insert_position, *self);
|
||||
set_veto_of(&proposal_hash, system::block_number() + cooloff_period(), existing_vetoers);
|
||||
|
||||
set_proposals(&proposals().into_iter().filter(|&(_, h)| h != *proposal_hash).collect::<Vec<_>>());
|
||||
set_proposals(&proposals().into_iter().filter(|&(_, h)| h != proposal_hash).collect::<Vec<_>>());
|
||||
storage::kill(&proposal_hash.to_keyed_vec(PROPOSAL_VOTERS));
|
||||
storage::kill(&proposal_hash.to_keyed_vec(PROPOSAL_OF));
|
||||
for (c, _) in council::active_council() {
|
||||
storage::kill(&(*proposal_hash, c).to_keyed_vec(COUNCIL_VOTE_OF));
|
||||
storage::kill(&(proposal_hash, c).to_keyed_vec(COUNCIL_VOTE_OF));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod privileged {
|
||||
use super::*;
|
||||
impl_dispatch! {
|
||||
pub mod privileged;
|
||||
fn set_cooloff_period(blocks: BlockNumber) = 0;
|
||||
fn set_voting_period(blocks: BlockNumber) = 1;
|
||||
}
|
||||
|
||||
pub fn set_cooloff_period(blocks: BlockNumber) {
|
||||
impl privileged::Dispatch for PrivPass {
|
||||
fn set_cooloff_period(self, blocks: BlockNumber) {
|
||||
storage::put(COOLOFF_PERIOD, &blocks);
|
||||
}
|
||||
|
||||
pub fn set_voting_period(blocks: BlockNumber) {
|
||||
fn set_voting_period(self, blocks: BlockNumber) {
|
||||
storage::put(VOTING_PERIOD, &blocks);
|
||||
}
|
||||
}
|
||||
|
||||
pub mod internal {
|
||||
use super::*;
|
||||
use runtime::democracy::privileged::start_referendum;
|
||||
use demo_primitives::VoteThreshold;
|
||||
use runtime::democracy::privileged::Dispatch;
|
||||
use runtime::democracy::VoteThreshold;
|
||||
use runtime::democracy::internal::start_referendum;
|
||||
|
||||
pub fn end_block(now: BlockNumber) {
|
||||
while let Some((proposal, proposal_hash)) = take_proposal_if_expiring_at(now) {
|
||||
let tally = take_tally(&proposal_hash);
|
||||
if let &Proposal::DemocracyCancelReferendum(ref_index) = &proposal {
|
||||
if let &Proposal::Democracy(democracy::privileged::Call::cancel_referendum(ref_index)) = &proposal {
|
||||
if let (_, 0, 0) = tally {
|
||||
democracy::privileged::cancel_referendum(ref_index);
|
||||
democracy::internal::cancel_referendum(ref_index);
|
||||
}
|
||||
} else {
|
||||
if tally.0 > tally.1 + tally.2 {
|
||||
@@ -239,8 +251,11 @@ mod tests {
|
||||
use codec::{KeyedVec, Joiner};
|
||||
use keyring::Keyring::{Alice, Bob, Charlie, Dave};
|
||||
use environment::with_env;
|
||||
use demo_primitives::{AccountId, Proposal, VoteThreshold};
|
||||
use demo_primitives::AccountId;
|
||||
use runtime::democracy::VoteThreshold;
|
||||
use runtime::{staking, council, democracy};
|
||||
use super::public::Dispatch;
|
||||
use super::privileged::Dispatch as PrivDispatch;
|
||||
|
||||
fn new_test_ext() -> TestExternalities {
|
||||
testing::externalities()
|
||||
@@ -266,19 +281,31 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
fn sessions_per_era_proposal(value: u64) -> Proposal {
|
||||
Proposal::Staking(staking::privileged::Call::set_sessions_per_era(value))
|
||||
}
|
||||
|
||||
fn bonding_duration_proposal(value: u64) -> Proposal {
|
||||
Proposal::Staking(staking::privileged::Call::set_bonding_duration(value))
|
||||
}
|
||||
|
||||
fn cancel_referendum_proposal(id: u32) -> Proposal {
|
||||
Proposal::Democracy(democracy::privileged::Call::cancel_referendum(id))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn referendum_cancellation_should_work_when_unanimous() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
let proposal = Proposal::StakingSetBondingDuration(42);
|
||||
democracy::privileged::start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
democracy::internal::start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove);
|
||||
assert_eq!(democracy::active_referendums(), vec![(0, 4, proposal, VoteThreshold::SuperMajorityApprove)]);
|
||||
|
||||
let cancellation = Proposal::DemocracyCancelReferendum(0);
|
||||
let cancellation = cancel_referendum_proposal(0);
|
||||
let hash = cancellation.blake2_256();
|
||||
public::propose(&Alice, &cancellation);
|
||||
public::vote(&Bob, &hash, true);
|
||||
public::vote(&Charlie, &hash, true);
|
||||
PublicPass::new(&Alice).propose(Box::new(cancellation));
|
||||
PublicPass::new(&Bob).vote(hash, true);
|
||||
PublicPass::new(&Charlie).vote(hash, true);
|
||||
assert_eq!(proposals(), vec![(2, hash)]);
|
||||
internal::end_block(1);
|
||||
|
||||
@@ -293,14 +320,14 @@ mod tests {
|
||||
fn referendum_cancellation_should_fail_when_not_unanimous() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
let proposal = Proposal::StakingSetBondingDuration(42);
|
||||
democracy::privileged::start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
democracy::internal::start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove);
|
||||
|
||||
let cancellation = Proposal::DemocracyCancelReferendum(0);
|
||||
let cancellation = cancel_referendum_proposal(0);
|
||||
let hash = cancellation.blake2_256();
|
||||
public::propose(&Alice, &cancellation);
|
||||
public::vote(&Bob, &hash, true);
|
||||
public::vote(&Charlie, &hash, false);
|
||||
PublicPass::new(&Alice).propose(Box::new(cancellation));
|
||||
PublicPass::new(&Bob).vote(hash, true);
|
||||
PublicPass::new(&Charlie).vote(hash, false);
|
||||
internal::end_block(1);
|
||||
|
||||
with_env(|e| e.block_number = 2);
|
||||
@@ -313,13 +340,13 @@ mod tests {
|
||||
fn referendum_cancellation_should_fail_when_abstentions() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
let proposal = Proposal::StakingSetBondingDuration(42);
|
||||
democracy::privileged::start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
democracy::internal::start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove);
|
||||
|
||||
let cancellation = Proposal::DemocracyCancelReferendum(0);
|
||||
let cancellation = cancel_referendum_proposal(0);
|
||||
let hash = cancellation.blake2_256();
|
||||
public::propose(&Alice, &cancellation);
|
||||
public::vote(&Bob, &hash, true);
|
||||
PublicPass::new(&Alice).propose(Box::new(cancellation));
|
||||
PublicPass::new(&Bob).vote(hash, true);
|
||||
internal::end_block(1);
|
||||
|
||||
with_env(|e| e.block_number = 2);
|
||||
@@ -332,10 +359,10 @@ mod tests {
|
||||
fn veto_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
let proposal = Proposal::StakingSetBondingDuration(42);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let hash = proposal.blake2_256();
|
||||
public::propose(&Alice, &proposal);
|
||||
public::veto(&Bob, &hash);
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
PublicPass::new(&Bob).veto(hash);
|
||||
assert_eq!(proposals().len(), 0);
|
||||
assert_eq!(democracy::active_referendums().len(), 0);
|
||||
});
|
||||
@@ -346,14 +373,14 @@ mod tests {
|
||||
fn double_veto_should_panic() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
let proposal = Proposal::StakingSetBondingDuration(42);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let hash = proposal.blake2_256();
|
||||
public::propose(&Alice, &proposal);
|
||||
public::veto(&Bob, &hash);
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
PublicPass::new(&Bob).veto(hash);
|
||||
|
||||
with_env(|e| e.block_number = 3);
|
||||
public::propose(&Alice, &proposal);
|
||||
public::veto(&Bob, &hash);
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
PublicPass::new(&Bob).veto(hash);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -362,13 +389,13 @@ mod tests {
|
||||
fn retry_in_cooloff_should_panic() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
let proposal = Proposal::StakingSetBondingDuration(42);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let hash = proposal.blake2_256();
|
||||
public::propose(&Alice, &proposal);
|
||||
public::veto(&Bob, &hash);
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
PublicPass::new(&Bob).veto(hash);
|
||||
|
||||
with_env(|e| e.block_number = 2);
|
||||
public::propose(&Alice, &proposal);
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -376,21 +403,21 @@ mod tests {
|
||||
fn retry_after_cooloff_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
let proposal = Proposal::StakingSetBondingDuration(42);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let hash = proposal.blake2_256();
|
||||
public::propose(&Alice, &proposal);
|
||||
public::veto(&Bob, &hash);
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
PublicPass::new(&Bob).veto(hash);
|
||||
|
||||
with_env(|e| e.block_number = 3);
|
||||
public::propose(&Alice, &proposal);
|
||||
public::vote(&Bob, &hash, false);
|
||||
public::vote(&Charlie, &hash, true);
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
PublicPass::new(&Bob).vote(hash, false);
|
||||
PublicPass::new(&Charlie).vote(hash, true);
|
||||
internal::end_block(3);
|
||||
|
||||
with_env(|e| e.block_number = 4);
|
||||
internal::end_block(4);
|
||||
assert_eq!(proposals().len(), 0);
|
||||
assert_eq!(democracy::active_referendums(), vec![(0, 7, Proposal::StakingSetBondingDuration(42), VoteThreshold::SimpleMajority)]);
|
||||
assert_eq!(democracy::active_referendums(), vec![(0, 7, bonding_duration_proposal(42), VoteThreshold::SimpleMajority)]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -398,14 +425,14 @@ mod tests {
|
||||
fn alternative_double_veto_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
let proposal = Proposal::StakingSetBondingDuration(42);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let hash = proposal.blake2_256();
|
||||
public::propose(&Alice, &proposal);
|
||||
public::veto(&Bob, &hash);
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
PublicPass::new(&Bob).veto(hash);
|
||||
|
||||
with_env(|e| e.block_number = 3);
|
||||
public::propose(&Alice, &proposal);
|
||||
public::veto(&Charlie, &hash);
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
PublicPass::new(&Charlie).veto(hash);
|
||||
assert_eq!(proposals().len(), 0);
|
||||
assert_eq!(democracy::active_referendums().len(), 0);
|
||||
});
|
||||
@@ -415,9 +442,9 @@ mod tests {
|
||||
fn simple_propose_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
let proposal = Proposal::StakingSetBondingDuration(42);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let hash = proposal.blake2_256();
|
||||
public::propose(&Alice, &proposal);
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
assert_eq!(proposals().len(), 1);
|
||||
assert_eq!(proposal_voters(&hash), vec![Alice.to_raw_public()]);
|
||||
assert_eq!(vote_of(&Alice, &hash), Some(true));
|
||||
@@ -429,8 +456,9 @@ mod tests {
|
||||
fn unvoted_proposal_should_expire_without_action() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
public::propose(&Alice, &Proposal::StakingSetBondingDuration(42));
|
||||
assert_eq!(tally(&Proposal::StakingSetBondingDuration(42).blake2_256()), (1, 0, 2));
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
assert_eq!(tally(&proposal.blake2_256()), (1, 0, 2));
|
||||
internal::end_block(1);
|
||||
|
||||
with_env(|e| e.block_number = 2);
|
||||
@@ -444,16 +472,17 @@ mod tests {
|
||||
fn unanimous_proposal_should_expire_with_biased_referendum() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
public::propose(&Alice, &Proposal::StakingSetBondingDuration(42));
|
||||
public::vote(&Bob, &Proposal::StakingSetBondingDuration(42).blake2_256(), true);
|
||||
public::vote(&Charlie, &Proposal::StakingSetBondingDuration(42).blake2_256(), true);
|
||||
assert_eq!(tally(&Proposal::StakingSetBondingDuration(42).blake2_256()), (3, 0, 0));
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
PublicPass::new(&Bob).vote(proposal.blake2_256(), true);
|
||||
PublicPass::new(&Charlie).vote(proposal.blake2_256(), true);
|
||||
assert_eq!(tally(&proposal.blake2_256()), (3, 0, 0));
|
||||
internal::end_block(1);
|
||||
|
||||
with_env(|e| e.block_number = 2);
|
||||
internal::end_block(2);
|
||||
assert_eq!(proposals().len(), 0);
|
||||
assert_eq!(democracy::active_referendums(), vec![(0, 5, Proposal::StakingSetBondingDuration(42), VoteThreshold::SuperMajorityAgainst)]);
|
||||
assert_eq!(democracy::active_referendums(), vec![(0, 5, proposal, VoteThreshold::SuperMajorityAgainst)]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -461,16 +490,17 @@ mod tests {
|
||||
fn majority_proposal_should_expire_with_unbiased_referendum() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
public::propose(&Alice, &Proposal::StakingSetBondingDuration(42));
|
||||
public::vote(&Bob, &Proposal::StakingSetBondingDuration(42).blake2_256(), true);
|
||||
public::vote(&Charlie, &Proposal::StakingSetBondingDuration(42).blake2_256(), false);
|
||||
assert_eq!(tally(&Proposal::StakingSetBondingDuration(42).blake2_256()), (2, 1, 0));
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
PublicPass::new(&Bob).vote(proposal.blake2_256(), true);
|
||||
PublicPass::new(&Charlie).vote(proposal.blake2_256(), false);
|
||||
assert_eq!(tally(&proposal.blake2_256()), (2, 1, 0));
|
||||
internal::end_block(1);
|
||||
|
||||
with_env(|e| e.block_number = 2);
|
||||
internal::end_block(2);
|
||||
assert_eq!(proposals().len(), 0);
|
||||
assert_eq!(democracy::active_referendums(), vec![(0, 5, Proposal::StakingSetBondingDuration(42), VoteThreshold::SimpleMajority)]);
|
||||
assert_eq!(democracy::active_referendums(), vec![(0, 5, proposal, VoteThreshold::SimpleMajority)]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -479,7 +509,8 @@ mod tests {
|
||||
fn propose_by_public_should_panic() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
public::propose(&Dave, &Proposal::StakingSetBondingDuration(42));
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
PublicPass::new(&Dave).propose(Box::new(proposal));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,13 +20,58 @@ use rstd::prelude::*;
|
||||
use integer_sqrt::IntegerSquareRoot;
|
||||
use codec::{KeyedVec, Slicable, Input, NonTrivialSlicable};
|
||||
use runtime_support::storage;
|
||||
use demo_primitives::{Proposal, AccountId, Hash, BlockNumber, VoteThreshold};
|
||||
use demo_primitives::{AccountId, Hash, BlockNumber};
|
||||
use dispatch::PrivCall as Proposal;
|
||||
use runtime::{staking, system, session};
|
||||
use runtime::staking::Balance;
|
||||
use runtime::staking::{PublicPass, Balance};
|
||||
|
||||
/// A token for privileged dispatch. Can only be created in this module.
|
||||
pub struct PrivPass((),);
|
||||
|
||||
impl PrivPass {
|
||||
fn new() -> PrivPass { PrivPass((),) }
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn test() -> PrivPass { PrivPass((),) }
|
||||
}
|
||||
|
||||
/// A proposal index.
|
||||
pub type PropIndex = u32;
|
||||
/// A referendum index.
|
||||
pub type ReferendumIndex = u32;
|
||||
|
||||
/// A means of determining if a vote is past pass threshold.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub enum VoteThreshold {
|
||||
/// A supermajority of approvals is needed to pass this vote.
|
||||
SuperMajorityApprove,
|
||||
/// A supermajority of rejects is needed to fail this vote.
|
||||
SuperMajorityAgainst,
|
||||
/// A simple majority of approvals is needed to pass this vote.
|
||||
SimpleMajority,
|
||||
}
|
||||
|
||||
impl Slicable for VoteThreshold {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
u8::decode(input).and_then(|v| match v {
|
||||
0 => Some(VoteThreshold::SuperMajorityApprove),
|
||||
1 => Some(VoteThreshold::SuperMajorityAgainst),
|
||||
2 => Some(VoteThreshold::SimpleMajority),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
match *self {
|
||||
VoteThreshold::SuperMajorityApprove => 0u8,
|
||||
VoteThreshold::SuperMajorityAgainst => 1u8,
|
||||
VoteThreshold::SimpleMajority => 2u8,
|
||||
}.using_encoded(f)
|
||||
}
|
||||
}
|
||||
impl NonTrivialSlicable for VoteThreshold {}
|
||||
|
||||
trait Approved {
|
||||
/// Given `approve` votes for and `against` votes against from a total electorate size of
|
||||
/// `electorate` (`electorate - (approve + against)` are abstainers), then returns true if the
|
||||
@@ -151,55 +196,79 @@ pub fn next_free_ref_index() -> ReferendumIndex {
|
||||
storage::get_or_default(REFERENDUM_COUNT)
|
||||
}
|
||||
|
||||
pub mod public {
|
||||
use super::*;
|
||||
impl_dispatch! {
|
||||
pub mod public;
|
||||
fn propose(proposal: Box<Proposal>, value: Balance) = 0;
|
||||
fn second(proposal: PropIndex) = 1;
|
||||
fn vote(ref_index: ReferendumIndex, approve_proposal: bool) = 2;
|
||||
}
|
||||
|
||||
impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
/// Propose a sensitive action to be taken.
|
||||
pub fn propose(signed: &AccountId, proposal: &Proposal, value: Balance) {
|
||||
fn propose(self, proposal: Box<Proposal>, value: Balance) {
|
||||
assert!(value >= minimum_deposit());
|
||||
assert!(staking::internal::deduct_unbonded(signed, value));
|
||||
assert!(staking::internal::deduct_unbonded(&self, value));
|
||||
|
||||
let index: PropIndex = storage::get_or_default(PUBLIC_PROP_COUNT);
|
||||
storage::put(PUBLIC_PROP_COUNT, &(index + 1));
|
||||
storage::put(&index.to_keyed_vec(DEPOSIT_OF), &(value, vec![*signed]));
|
||||
storage::put(&index.to_keyed_vec(DEPOSIT_OF), &(value, vec![*self]));
|
||||
|
||||
let mut props = public_props();
|
||||
props.push((index, proposal.clone(), *signed));
|
||||
props.push((index, (*proposal).clone(), *self));
|
||||
storage::put(PUBLIC_PROPS, &props);
|
||||
}
|
||||
|
||||
/// Propose a sensitive action to be taken.
|
||||
pub fn second(signed: &AccountId, proposal: PropIndex) {
|
||||
fn second(self, proposal: PropIndex) {
|
||||
let key = proposal.to_keyed_vec(DEPOSIT_OF);
|
||||
let mut deposit: (Balance, Vec<AccountId>) =
|
||||
storage::get(&key).expect("can only second an existing proposal");
|
||||
assert!(staking::internal::deduct_unbonded(signed, deposit.0));
|
||||
assert!(staking::internal::deduct_unbonded(&self, deposit.0));
|
||||
|
||||
deposit.1.push(*signed);
|
||||
deposit.1.push(*self);
|
||||
storage::put(&key, &deposit);
|
||||
}
|
||||
|
||||
/// Vote in a referendum. If `approve_proposal` is true, the vote is to enact the proposal;
|
||||
/// false would be a vote to keep the status quo..
|
||||
pub fn vote(signed: &AccountId, ref_index: ReferendumIndex, approve_proposal: bool) {
|
||||
fn vote(self, ref_index: ReferendumIndex, approve_proposal: bool) {
|
||||
if !is_active_referendum(ref_index) {
|
||||
panic!("vote given for invalid referendum.")
|
||||
}
|
||||
if staking::balance(signed) == 0 {
|
||||
if staking::balance(&self) == 0 {
|
||||
panic!("transactor must have balance to signal approval.");
|
||||
}
|
||||
let key = (*signed, ref_index).to_keyed_vec(VOTE_OF);
|
||||
let key = (*self, ref_index).to_keyed_vec(VOTE_OF);
|
||||
if !storage::exists(&key) {
|
||||
let mut voters = voters_for(ref_index);
|
||||
voters.push(signed.clone());
|
||||
voters.push(self.clone());
|
||||
storage::put(&ref_index.to_keyed_vec(VOTERS_FOR), &voters);
|
||||
}
|
||||
storage::put(&key, &approve_proposal);
|
||||
}
|
||||
}
|
||||
|
||||
pub mod privileged {
|
||||
impl_dispatch! {
|
||||
pub mod privileged;
|
||||
fn start_referendum(proposal: Box<Proposal>, vote_threshold: VoteThreshold) = 0;
|
||||
fn cancel_referendum(ref_index: ReferendumIndex) = 1;
|
||||
}
|
||||
|
||||
impl privileged::Dispatch for PrivPass {
|
||||
/// Start a referendum.
|
||||
fn start_referendum(self, proposal: Box<Proposal>, vote_threshold: VoteThreshold) {
|
||||
inject_referendum(system::block_number() + voting_period(), *proposal, vote_threshold);
|
||||
}
|
||||
|
||||
/// Remove a referendum.
|
||||
fn cancel_referendum(self, ref_index: ReferendumIndex) {
|
||||
clear_referendum(ref_index);
|
||||
}
|
||||
}
|
||||
|
||||
pub mod internal {
|
||||
use super::*;
|
||||
use dispatch;
|
||||
|
||||
/// Can be called directly by the council.
|
||||
pub fn start_referendum(proposal: Proposal, vote_threshold: VoteThreshold) {
|
||||
@@ -210,12 +279,6 @@ pub mod privileged {
|
||||
pub fn cancel_referendum(ref_index: ReferendumIndex) {
|
||||
clear_referendum(ref_index);
|
||||
}
|
||||
}
|
||||
|
||||
pub mod internal {
|
||||
use super::*;
|
||||
use demo_primitives::Proposal;
|
||||
use dispatch;
|
||||
|
||||
/// Current era is ending; we should finish up any proposals.
|
||||
pub fn end_block(now: BlockNumber) {
|
||||
@@ -245,7 +308,7 @@ pub mod internal {
|
||||
let total_stake = staking::total_stake();
|
||||
clear_referendum(index);
|
||||
if vote_threshold.approved(approve, against, total_stake) {
|
||||
dispatch::proposal(proposal);
|
||||
proposal.dispatch(PrivPass::new());
|
||||
}
|
||||
storage::put(NEXT_TALLY, &(index + 1));
|
||||
}
|
||||
@@ -307,6 +370,7 @@ pub mod testing {
|
||||
twox_128(staking::SESSIONS_PER_ERA).to_vec() => vec![].and(&1u64),
|
||||
twox_128(staking::VALIDATOR_COUNT).to_vec() => vec![].and(&3u64),
|
||||
twox_128(staking::CURRENT_ERA).to_vec() => vec![].and(&1u64),
|
||||
twox_128(staking::TRANSACTION_FEE).to_vec() => vec![].and(&1u64),
|
||||
|
||||
twox_128(LAUNCH_PERIOD).to_vec() => vec![].and(&1u64),
|
||||
twox_128(VOTING_PERIOD).to_vec() => vec![].and(&1u64),
|
||||
@@ -322,7 +386,11 @@ mod tests {
|
||||
use codec::{KeyedVec, Joiner};
|
||||
use keyring::Keyring::*;
|
||||
use environment::with_env;
|
||||
use demo_primitives::{AccountId, Proposal};
|
||||
use demo_primitives::AccountId;
|
||||
use dispatch::PrivCall as Proposal;
|
||||
use runtime::staking::PublicPass;
|
||||
use super::public::Dispatch;
|
||||
use super::privileged::Dispatch as PrivDispatch;
|
||||
use runtime::{staking, session, democracy};
|
||||
|
||||
fn new_test_ext() -> TestExternalities {
|
||||
@@ -343,13 +411,18 @@ mod tests {
|
||||
|
||||
// TODO: test VoteThreshold
|
||||
|
||||
fn propose_sessions_per_era(who: &AccountId, value: u64, locked: Balance) {
|
||||
PublicPass::test(who).
|
||||
propose(Box::new(Proposal::Staking(staking::privileged::Call::set_sessions_per_era(value))), locked);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn locked_for_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
public::propose(&Alice, &Proposal::StakingSetSessionsPerEra(2), 2u64);
|
||||
public::propose(&Alice, &Proposal::StakingSetSessionsPerEra(4), 4u64);
|
||||
public::propose(&Alice, &Proposal::StakingSetSessionsPerEra(3), 3u64);
|
||||
propose_sessions_per_era(&Alice, 2, 2u64);
|
||||
propose_sessions_per_era(&Alice, 4, 4u64);
|
||||
propose_sessions_per_era(&Alice, 3, 3u64);
|
||||
assert_eq!(locked_for(0), Some(2));
|
||||
assert_eq!(locked_for(1), Some(4));
|
||||
assert_eq!(locked_for(2), Some(3));
|
||||
@@ -360,12 +433,12 @@ mod tests {
|
||||
fn single_proposal_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
public::propose(&Alice, &Proposal::StakingSetSessionsPerEra(2), 1u64);
|
||||
propose_sessions_per_era(&Alice, 2, 1u64);
|
||||
democracy::internal::end_block(system::block_number());
|
||||
|
||||
with_env(|e| e.block_number = 2);
|
||||
let r = 0;
|
||||
public::vote(&Alice, r, true);
|
||||
PublicPass::test(&Alice).vote(r, true);
|
||||
|
||||
assert_eq!(next_free_ref_index(), 1);
|
||||
assert_eq!(voters_for(r), vec![Alice.to_raw_public()]);
|
||||
@@ -383,11 +456,11 @@ mod tests {
|
||||
fn deposit_for_proposals_should_be_taken() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
public::propose(&Alice, &Proposal::StakingSetSessionsPerEra(2), 5u64);
|
||||
public::second(&Bob, 0);
|
||||
public::second(&Eve, 0);
|
||||
public::second(&Eve, 0);
|
||||
public::second(&Eve, 0);
|
||||
propose_sessions_per_era(&Alice, 2, 5u64);
|
||||
PublicPass::test(&Bob).second(0);
|
||||
PublicPass::test(&Eve).second(0);
|
||||
PublicPass::test(&Eve).second(0);
|
||||
PublicPass::test(&Eve).second(0);
|
||||
assert_eq!(staking::balance(&Alice), 5u64);
|
||||
assert_eq!(staking::balance(&Bob), 15u64);
|
||||
assert_eq!(staking::balance(&Eve), 35u64);
|
||||
@@ -398,11 +471,11 @@ mod tests {
|
||||
fn deposit_for_proposals_should_be_returned() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
public::propose(&Alice, &Proposal::StakingSetSessionsPerEra(2), 5u64);
|
||||
public::second(&Bob, 0);
|
||||
public::second(&Eve, 0);
|
||||
public::second(&Eve, 0);
|
||||
public::second(&Eve, 0);
|
||||
propose_sessions_per_era(&Alice, 2, 5u64);
|
||||
PublicPass::test(&Bob).second(0);
|
||||
PublicPass::test(&Eve).second(0);
|
||||
PublicPass::test(&Eve).second(0);
|
||||
PublicPass::test(&Eve).second(0);
|
||||
democracy::internal::end_block(system::block_number());
|
||||
assert_eq!(staking::balance(&Alice), 10u64);
|
||||
assert_eq!(staking::balance(&Bob), 20u64);
|
||||
@@ -415,7 +488,7 @@ mod tests {
|
||||
fn proposal_with_deposit_below_minimum_should_panic() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
public::propose(&Alice, &Proposal::StakingSetSessionsPerEra(2), 0u64);
|
||||
propose_sessions_per_era(&Alice, 2, 0u64);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -424,7 +497,7 @@ mod tests {
|
||||
fn poor_proposer_should_panic() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
public::propose(&Alice, &Proposal::StakingSetSessionsPerEra(2), 11u64);
|
||||
propose_sessions_per_era(&Alice, 2, 11u64);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -433,46 +506,55 @@ mod tests {
|
||||
fn poor_seconder_should_panic() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
public::propose(&Bob, &Proposal::StakingSetSessionsPerEra(2), 11u64);
|
||||
public::second(&Alice, 0);
|
||||
propose_sessions_per_era(&Bob, 2, 11u64);
|
||||
PublicPass::test(&Alice).second(0);
|
||||
});
|
||||
}
|
||||
|
||||
fn propose_bonding_duration(who: &AccountId, value: u64, locked: Balance) {
|
||||
PublicPass::test(who).
|
||||
propose(Box::new(Proposal::Staking(staking::privileged::Call::set_bonding_duration(value))), locked);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runners_up_should_come_after() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 0);
|
||||
public::propose(&Alice, &Proposal::StakingSetBondingDuration(2), 2u64);
|
||||
public::propose(&Alice, &Proposal::StakingSetBondingDuration(4), 4u64);
|
||||
public::propose(&Alice, &Proposal::StakingSetBondingDuration(3), 3u64);
|
||||
propose_bonding_duration(&Alice, 2, 2u64);
|
||||
propose_bonding_duration(&Alice, 4, 4u64);
|
||||
propose_bonding_duration(&Alice, 3, 3u64);
|
||||
democracy::internal::end_block(system::block_number());
|
||||
|
||||
with_env(|e| e.block_number = 1);
|
||||
public::vote(&Alice, 0, true);
|
||||
PublicPass::test(&Alice).vote(0, true);
|
||||
democracy::internal::end_block(system::block_number());
|
||||
staking::internal::check_new_era();
|
||||
assert_eq!(staking::bonding_duration(), 4u64);
|
||||
|
||||
with_env(|e| e.block_number = 2);
|
||||
public::vote(&Alice, 1, true);
|
||||
PublicPass::test(&Alice).vote(1, true);
|
||||
democracy::internal::end_block(system::block_number());
|
||||
staking::internal::check_new_era();
|
||||
assert_eq!(staking::bonding_duration(), 3u64);
|
||||
|
||||
with_env(|e| e.block_number = 3);
|
||||
public::vote(&Alice, 2, true);
|
||||
PublicPass::test(&Alice).vote(2, true);
|
||||
democracy::internal::end_block(system::block_number());
|
||||
staking::internal::check_new_era();
|
||||
assert_eq!(staking::bonding_duration(), 2u64);
|
||||
});
|
||||
}
|
||||
|
||||
fn sessions_per_era_propsal(value: u64) -> Proposal {
|
||||
Proposal::Staking(staking::privileged::Call::set_sessions_per_era(value))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_passing_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
let r = inject_referendum(1, Proposal::StakingSetSessionsPerEra(2), VoteThreshold::SuperMajorityApprove);
|
||||
public::vote(&Alice, r, true);
|
||||
let r = inject_referendum(1, sessions_per_era_propsal(2), VoteThreshold::SuperMajorityApprove);
|
||||
PublicPass::test(&Alice).vote(r, true);
|
||||
|
||||
assert_eq!(voters_for(r), vec![Alice.to_raw_public()]);
|
||||
assert_eq!(vote_of(&Alice, r), Some(true));
|
||||
@@ -489,9 +571,9 @@ mod tests {
|
||||
fn cancel_referendum_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
let r = inject_referendum(1, Proposal::StakingSetSessionsPerEra(2), VoteThreshold::SuperMajorityApprove);
|
||||
public::vote(&Alice, r, true);
|
||||
privileged::cancel_referendum(r);
|
||||
let r = inject_referendum(1, sessions_per_era_propsal(2), VoteThreshold::SuperMajorityApprove);
|
||||
PublicPass::test(&Alice).vote(r, true);
|
||||
PrivPass::new().cancel_referendum(r);
|
||||
|
||||
democracy::internal::end_block(system::block_number());
|
||||
staking::internal::check_new_era();
|
||||
@@ -504,8 +586,8 @@ mod tests {
|
||||
fn simple_failing_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
let r = inject_referendum(1, Proposal::StakingSetSessionsPerEra(2), VoteThreshold::SuperMajorityApprove);
|
||||
public::vote(&Alice, r, false);
|
||||
let r = inject_referendum(1, sessions_per_era_propsal(2), VoteThreshold::SuperMajorityApprove);
|
||||
PublicPass::test(&Alice).vote(r, false);
|
||||
|
||||
assert_eq!(voters_for(r), vec![Alice.to_raw_public()]);
|
||||
assert_eq!(vote_of(&Alice, r), Some(false));
|
||||
@@ -522,13 +604,13 @@ mod tests {
|
||||
fn controversial_voting_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
let r = inject_referendum(1, Proposal::StakingSetSessionsPerEra(2), VoteThreshold::SuperMajorityApprove);
|
||||
public::vote(&Alice, r, true);
|
||||
public::vote(&Bob, r, false);
|
||||
public::vote(&Charlie, r, false);
|
||||
public::vote(&Dave, r, true);
|
||||
public::vote(&Eve, r, false);
|
||||
public::vote(&Ferdie, r, true);
|
||||
let r = inject_referendum(1, sessions_per_era_propsal(2), VoteThreshold::SuperMajorityApprove);
|
||||
PublicPass::test(&Alice).vote(r, true);
|
||||
PublicPass::test(&Bob).vote(r, false);
|
||||
PublicPass::test(&Charlie).vote(r, false);
|
||||
PublicPass::test(&Dave).vote(r, true);
|
||||
PublicPass::test(&Eve).vote(r, false);
|
||||
PublicPass::test(&Ferdie).vote(r, true);
|
||||
|
||||
assert_eq!(tally(r), (110, 100));
|
||||
|
||||
@@ -543,9 +625,9 @@ mod tests {
|
||||
fn controversial_low_turnout_voting_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
let r = inject_referendum(1, Proposal::StakingSetSessionsPerEra(2), VoteThreshold::SuperMajorityApprove);
|
||||
public::vote(&Eve, r, false);
|
||||
public::vote(&Ferdie, r, true);
|
||||
let r = inject_referendum(1, sessions_per_era_propsal(2), VoteThreshold::SuperMajorityApprove);
|
||||
PublicPass::test(&Eve).vote(r, false);
|
||||
PublicPass::test(&Ferdie).vote(r, true);
|
||||
|
||||
assert_eq!(tally(r), (60, 50));
|
||||
|
||||
@@ -563,10 +645,10 @@ mod tests {
|
||||
assert_eq!(staking::total_stake(), 210u64);
|
||||
|
||||
with_env(|e| e.block_number = 1);
|
||||
let r = inject_referendum(1, Proposal::StakingSetSessionsPerEra(2), VoteThreshold::SuperMajorityApprove);
|
||||
public::vote(&Dave, r, true);
|
||||
public::vote(&Eve, r, false);
|
||||
public::vote(&Ferdie, r, true);
|
||||
let r = inject_referendum(1, sessions_per_era_propsal(2), VoteThreshold::SuperMajorityApprove);
|
||||
PublicPass::test(&Dave).vote(r, true);
|
||||
PublicPass::test(&Eve).vote(r, false);
|
||||
PublicPass::test(&Ferdie).vote(r, true);
|
||||
|
||||
assert_eq!(tally(r), (100, 50));
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ use codec::KeyedVec;
|
||||
use runtime_support::{storage, StorageVec};
|
||||
use demo_primitives::{AccountId, SessionKey, BlockNumber};
|
||||
use runtime::{system, staking, consensus};
|
||||
use runtime::democracy::PrivPass;
|
||||
use runtime::staking::PublicPass;
|
||||
|
||||
pub const SESSION_LENGTH: &[u8] = b"ses:len";
|
||||
pub const CURRENT_INDEX: &[u8] = b"ses:ind";
|
||||
@@ -62,28 +64,35 @@ pub fn last_length_change() -> BlockNumber {
|
||||
storage::get_or(LAST_LENGTH_CHANGE, 0)
|
||||
}
|
||||
|
||||
pub mod public {
|
||||
use super::*;
|
||||
impl_dispatch! {
|
||||
pub mod public;
|
||||
fn set_key(key: SessionKey) = 0;
|
||||
}
|
||||
|
||||
impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
/// Sets the session key of `_validator` to `_key`. This doesn't take effect until the next
|
||||
/// session.
|
||||
pub fn set_key(validator: &AccountId, key: &SessionKey) {
|
||||
fn set_key(self, key: SessionKey) {
|
||||
// set new value for next session
|
||||
storage::put(&validator.to_keyed_vec(NEXT_KEY_FOR), key);
|
||||
storage::put(&self.to_keyed_vec(NEXT_KEY_FOR), &key);
|
||||
}
|
||||
}
|
||||
|
||||
pub mod privileged {
|
||||
use super::*;
|
||||
impl_dispatch! {
|
||||
pub mod privileged;
|
||||
fn set_length(new: BlockNumber) = 0;
|
||||
fn force_new_session() = 1;
|
||||
}
|
||||
|
||||
impl privileged::Dispatch for PrivPass {
|
||||
/// Set a new era length. Won't kick in until the next era change (at current length).
|
||||
pub fn set_length(new: BlockNumber) {
|
||||
fn set_length(self, new: BlockNumber) {
|
||||
storage::put(NEXT_SESSION_LENGTH, &new);
|
||||
}
|
||||
|
||||
/// Forces a new session.
|
||||
pub fn force_new_session() {
|
||||
rotate_session();
|
||||
fn force_new_session(self) {
|
||||
internal::rotate_session();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,10 +119,9 @@ pub mod internal {
|
||||
rotate_session();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Move onto next session: register the new authority set.
|
||||
fn rotate_session() {
|
||||
/// Move onto next session: register the new authority set.
|
||||
pub fn rotate_session() {
|
||||
// Increment current session index.
|
||||
storage::put(CURRENT_INDEX, &(current_index() + 1));
|
||||
|
||||
@@ -131,6 +139,7 @@ fn rotate_session() {
|
||||
consensus::internal::set_authority(i as u32, &n);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
@@ -161,7 +170,7 @@ pub mod testing {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::public::*;
|
||||
use super::privileged::*;
|
||||
use super::privileged::Dispatch as PrivDispatch;
|
||||
use super::internal::*;
|
||||
use runtime_io::{with_externalities, twox_128, TestExternalities};
|
||||
use codec::{KeyedVec, Joiner};
|
||||
@@ -200,14 +209,14 @@ mod tests {
|
||||
with_externalities(&mut t, || {
|
||||
// Block 1: Change to length 3; no visible change.
|
||||
with_env(|e| e.block_number = 1);
|
||||
set_length(3);
|
||||
PrivPass::test().set_length(3);
|
||||
check_rotate_session();
|
||||
assert_eq!(length(), 2);
|
||||
assert_eq!(current_index(), 0);
|
||||
|
||||
// Block 2: Length now changed to 3. Index incremented.
|
||||
with_env(|e| e.block_number = 2);
|
||||
set_length(3);
|
||||
PrivPass::test().set_length(3);
|
||||
check_rotate_session();
|
||||
assert_eq!(length(), 3);
|
||||
assert_eq!(current_index(), 1);
|
||||
@@ -220,7 +229,7 @@ mod tests {
|
||||
|
||||
// Block 4: Change to length 2; no visible change.
|
||||
with_env(|e| e.block_number = 4);
|
||||
set_length(2);
|
||||
PrivPass::test().set_length(2);
|
||||
check_rotate_session();
|
||||
assert_eq!(length(), 3);
|
||||
assert_eq!(current_index(), 1);
|
||||
@@ -261,7 +270,7 @@ mod tests {
|
||||
|
||||
// Block 3: Set new key for validator 2; no visible change.
|
||||
with_env(|e| e.block_number = 3);
|
||||
set_key(&[20; 32], &[22; 32]);
|
||||
PublicPass::test(&[20; 32]).set_key([22; 32]);
|
||||
assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]);
|
||||
|
||||
check_rotate_session();
|
||||
|
||||
@@ -17,14 +17,14 @@
|
||||
//! Staking manager: Handles balances and periodically determines the best set of validators.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::cmp;
|
||||
use rstd::{ops, cmp};
|
||||
use rstd::cell::RefCell;
|
||||
use rstd::collections::btree_map::{BTreeMap, Entry};
|
||||
use runtime_io::{print, blake2_256};
|
||||
use codec::KeyedVec;
|
||||
use codec::{Slicable, Input, KeyedVec};
|
||||
use runtime_support::{storage, StorageVec};
|
||||
use demo_primitives::{BlockNumber, AccountId};
|
||||
use runtime::{system, session};
|
||||
use runtime::{system, session, democracy};
|
||||
|
||||
/// The balance of an account.
|
||||
pub type Balance = u64;
|
||||
@@ -41,6 +41,7 @@ pub const LAST_ERA_LENGTH_CHANGE: &[u8] = b"sta:lec";
|
||||
pub const TOTAL_STAKE: &[u8] = b"sta:tot";
|
||||
pub const INTENTION_AT: &[u8] = b"sta:wil:";
|
||||
pub const INTENTION_COUNT: &[u8] = b"sta:wil:len";
|
||||
pub const TRANSACTION_FEE: &[u8] = b"sta:fee";
|
||||
|
||||
pub const BALANCE_OF: &[u8] = b"sta:bal:";
|
||||
pub const RESERVED_BALANCE_OF: &[u8] = b"sta:lbo:";
|
||||
@@ -54,6 +55,11 @@ impl StorageVec for IntentionStorageVec {
|
||||
const PREFIX: &'static[u8] = INTENTION_AT;
|
||||
}
|
||||
|
||||
/// The fee to be paid for making a transaction.
|
||||
pub fn transaction_fee() -> Balance {
|
||||
storage::get(TRANSACTION_FEE).expect("All basic parameters should be defined")
|
||||
}
|
||||
|
||||
/// The length of the bonding duration in eras.
|
||||
pub fn bonding_duration() -> BlockNumber {
|
||||
storage::get_or_default(BONDING_DURATION)
|
||||
@@ -133,17 +139,131 @@ pub fn total_stake() -> Balance {
|
||||
storage::get_or(TOTAL_STAKE, 0)
|
||||
}
|
||||
|
||||
pub struct PublicPass<'a> (&'a AccountId);
|
||||
|
||||
const NOBODY: AccountId = [0u8; 32];
|
||||
|
||||
impl<'a> PublicPass<'a> {
|
||||
pub fn new(transactor: &AccountId) -> PublicPass {
|
||||
let b = free_balance(&transactor);
|
||||
let transaction_fee = transaction_fee();
|
||||
assert!(b >= transaction_fee, "attempt to transact without enough funds to pay fee");
|
||||
internal::set_free_balance(&transactor, b - transaction_fee);
|
||||
PublicPass(transactor)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn test(signed: &AccountId) -> PublicPass {
|
||||
PublicPass(signed)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn nobody() -> PublicPass<'static> {
|
||||
PublicPass(&NOBODY)
|
||||
}
|
||||
|
||||
/// Create a smart-contract account.
|
||||
pub fn create(self, code: &[u8], value: Balance) {
|
||||
// commit anything that made it this far to storage
|
||||
if let Some(commit) = private::effect_create(self.0, code, value, private::DirectExt) {
|
||||
private::commit_state(commit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ops::Deref for PublicPass<'a> {
|
||||
type Target = AccountId;
|
||||
fn deref(&self) -> &AccountId {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl_dispatch! {
|
||||
pub mod public;
|
||||
fn transfer(dest: AccountId, value: Balance) = 0;
|
||||
fn stake() = 1;
|
||||
fn unstake() = 2;
|
||||
}
|
||||
|
||||
impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
/// Transfer some unlocked staking balance to another staker.
|
||||
/// TODO: probably want to state gas-limit and gas-price.
|
||||
fn transfer(self, dest: AccountId, value: Balance) {
|
||||
// commit anything that made it this far to storage
|
||||
if let Some(commit) = private::effect_transfer(&self, &dest, value, private::DirectExt) {
|
||||
private::commit_state(commit);
|
||||
}
|
||||
}
|
||||
|
||||
/// Declare the desire to stake for the transactor.
|
||||
///
|
||||
/// Effects will be felt at the beginning of the next era.
|
||||
fn stake(self) {
|
||||
let mut intentions = IntentionStorageVec::items();
|
||||
// can't be in the list twice.
|
||||
assert!(intentions.iter().find(|&t| *t == *self).is_none(), "Cannot stake if already staked.");
|
||||
intentions.push(self.clone());
|
||||
IntentionStorageVec::set_items(&intentions);
|
||||
storage::put(&self.to_keyed_vec(BONDAGE_OF), &u64::max_value());
|
||||
}
|
||||
|
||||
/// Retract the desire to stake for the transactor.
|
||||
///
|
||||
/// Effects will be felt at the beginning of the next era.
|
||||
fn unstake(self) {
|
||||
let mut intentions = IntentionStorageVec::items();
|
||||
if let Some(position) = intentions.iter().position(|&t| t == *self) {
|
||||
intentions.swap_remove(position);
|
||||
} else {
|
||||
panic!("Cannot unstake if not already staked.");
|
||||
}
|
||||
IntentionStorageVec::set_items(&intentions);
|
||||
storage::put(&self.to_keyed_vec(BONDAGE_OF), &(current_era() + bonding_duration()));
|
||||
}
|
||||
}
|
||||
|
||||
impl_dispatch! {
|
||||
pub mod privileged;
|
||||
fn set_sessions_per_era(new: BlockNumber) = 0;
|
||||
fn set_bonding_duration(new: BlockNumber) = 1;
|
||||
fn set_validator_count(new: u32) = 2;
|
||||
fn force_new_era() = 3;
|
||||
}
|
||||
|
||||
impl privileged::Dispatch for democracy::PrivPass {
|
||||
/// Set the number of sessions in an era.
|
||||
fn set_sessions_per_era(self, new: BlockNumber) {
|
||||
storage::put(NEXT_SESSIONS_PER_ERA, &new);
|
||||
}
|
||||
|
||||
/// The length of the bonding duration in eras.
|
||||
fn set_bonding_duration(self, new: BlockNumber) {
|
||||
storage::put(BONDING_DURATION, &new);
|
||||
}
|
||||
|
||||
/// The length of a staking era in sessions.
|
||||
fn set_validator_count(self, new: u32) {
|
||||
storage::put(VALIDATOR_COUNT, &new);
|
||||
}
|
||||
|
||||
/// Force there to be a new era. This also forces a new session immediately after.
|
||||
fn force_new_era(self) {
|
||||
new_era();
|
||||
session::internal::rotate_session();
|
||||
}
|
||||
}
|
||||
|
||||
// Each identity's stake may be in one of three bondage states, given by an integer:
|
||||
// - n | n <= current_era(): inactive: free to be transferred.
|
||||
// - ~0: active: currently representing a validator.
|
||||
// - n | n > current_era(): deactivating: recently representing a validator and not yet
|
||||
// ready for transfer.
|
||||
|
||||
pub mod public {
|
||||
mod private {
|
||||
use super::*;
|
||||
|
||||
#[derive(Default)]
|
||||
struct ChangeEntry {
|
||||
pub struct ChangeEntry {
|
||||
balance: Option<Balance>,
|
||||
code: Option<Vec<u8>>,
|
||||
storage: BTreeMap<Vec<u8>, Option<Vec<u8>>>,
|
||||
@@ -157,7 +277,7 @@ pub mod public {
|
||||
|
||||
type State = BTreeMap<AccountId, ChangeEntry>;
|
||||
|
||||
trait Externalities {
|
||||
pub trait Externalities {
|
||||
fn get_storage(&self, account: &AccountId, location: &[u8]) -> Option<Vec<u8>>;
|
||||
fn get_code(&self, account: &AccountId) -> Vec<u8>;
|
||||
fn get_balance(&self, account: &AccountId) -> Balance;
|
||||
@@ -173,7 +293,7 @@ pub mod public {
|
||||
do_get_balance: F5,
|
||||
}
|
||||
|
||||
struct DirectExt;
|
||||
pub struct DirectExt;
|
||||
impl Externalities for DirectExt {
|
||||
fn get_storage(&self, account: &AccountId, location: &[u8]) -> Option<Vec<u8>> {
|
||||
let mut v = account.to_keyed_vec(STORAGE_OF);
|
||||
@@ -204,7 +324,7 @@ pub mod public {
|
||||
}
|
||||
}
|
||||
|
||||
fn commit_state(s: State) {
|
||||
pub fn commit_state(s: State) {
|
||||
for (address, changed) in s.into_iter() {
|
||||
if let Some(balance) = changed.balance {
|
||||
storage::put(&address.to_keyed_vec(BALANCE_OF), &balance);
|
||||
@@ -245,15 +365,7 @@ pub mod public {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a smart-contract account.
|
||||
pub fn create(transactor: &AccountId, code: &[u8], value: Balance) {
|
||||
// commit anything that made it this far to storage
|
||||
if let Some(commit) = effect_create(transactor, code, value, DirectExt) {
|
||||
commit_state(commit);
|
||||
}
|
||||
}
|
||||
|
||||
fn effect_create<E: Externalities>(
|
||||
pub fn effect_create<E: Externalities>(
|
||||
transactor: &AccountId,
|
||||
code: &[u8],
|
||||
value: Balance,
|
||||
@@ -282,16 +394,7 @@ pub mod public {
|
||||
Some(local)
|
||||
}
|
||||
|
||||
/// Transfer some unlocked staking balance to another staker.
|
||||
/// TODO: probably want to state gas-limit and gas-price.
|
||||
pub fn transfer(transactor: &AccountId, dest: &AccountId, value: Balance) {
|
||||
// commit anything that made it this far to storage
|
||||
if let Some(commit) = effect_transfer(transactor, dest, value, DirectExt) {
|
||||
commit_state(commit);
|
||||
}
|
||||
}
|
||||
|
||||
fn effect_transfer<E: Externalities>(
|
||||
pub fn effect_transfer<E: Externalities>(
|
||||
transactor: &AccountId,
|
||||
dest: &AccountId,
|
||||
value: Balance,
|
||||
@@ -362,57 +465,6 @@ pub mod public {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Declare the desire to stake for the transactor.
|
||||
///
|
||||
/// Effects will be felt at the beginning of the next era.
|
||||
pub fn stake(transactor: &AccountId) {
|
||||
let mut intentions = IntentionStorageVec::items();
|
||||
// can't be in the list twice.
|
||||
assert!(intentions.iter().find(|t| *t == transactor).is_none(), "Cannot stake if already staked.");
|
||||
intentions.push(transactor.clone());
|
||||
IntentionStorageVec::set_items(&intentions);
|
||||
storage::put(&transactor.to_keyed_vec(BONDAGE_OF), &u64::max_value());
|
||||
}
|
||||
|
||||
/// Retract the desire to stake for the transactor.
|
||||
///
|
||||
/// Effects will be felt at the beginning of the next era.
|
||||
pub fn unstake(transactor: &AccountId) {
|
||||
let mut intentions = IntentionStorageVec::items();
|
||||
if let Some(position) = intentions.iter().position(|t| t == transactor) {
|
||||
intentions.swap_remove(position);
|
||||
} else {
|
||||
panic!("Cannot unstake if not already staked.");
|
||||
}
|
||||
IntentionStorageVec::set_items(&intentions);
|
||||
storage::put(&transactor.to_keyed_vec(BONDAGE_OF), &(current_era() + bonding_duration()));
|
||||
}
|
||||
}
|
||||
|
||||
pub mod privileged {
|
||||
use super::*;
|
||||
|
||||
/// Set the number of sessions in an era.
|
||||
pub fn set_sessions_per_era(new: BlockNumber) {
|
||||
storage::put(NEXT_SESSIONS_PER_ERA, &new);
|
||||
}
|
||||
|
||||
/// The length of the bonding duration in eras.
|
||||
pub fn set_bonding_duration(new: BlockNumber) {
|
||||
storage::put(BONDING_DURATION, &new);
|
||||
}
|
||||
|
||||
/// The length of a staking era in sessions.
|
||||
pub fn set_validator_count(new: u32) {
|
||||
storage::put(VALIDATOR_COUNT, &new);
|
||||
}
|
||||
|
||||
/// Force there to be a new era. This also forces a new session immediately after.
|
||||
pub fn force_new_era() {
|
||||
new_era();
|
||||
session::privileged::force_new_session();
|
||||
}
|
||||
}
|
||||
|
||||
pub mod internal {
|
||||
@@ -540,6 +592,8 @@ pub mod testing {
|
||||
use codec::{Joiner, KeyedVec};
|
||||
use keyring::Keyring::*;
|
||||
use runtime::session;
|
||||
use super::public::{Call, Dispatch};
|
||||
use super::privileged::{Dispatch as PrivDispatch, Call as PrivCall};
|
||||
|
||||
pub fn externalities(session_length: u64, sessions_per_era: u64, current_era: u64) -> TestExternalities {
|
||||
let extras: TestExternalities = map![
|
||||
@@ -549,6 +603,7 @@ pub mod testing {
|
||||
twox_128(&2u32.to_keyed_vec(INTENTION_AT)).to_vec() => Charlie.to_raw_public_vec(),
|
||||
twox_128(SESSIONS_PER_ERA).to_vec() => vec![].and(&sessions_per_era),
|
||||
twox_128(VALIDATOR_COUNT).to_vec() => vec![].and(&3u64),
|
||||
twox_128(TRANSACTION_FEE).to_vec() => vec![].and(&1u64),
|
||||
twox_128(CURRENT_ERA).to_vec() => vec![].and(¤t_era),
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
];
|
||||
@@ -560,7 +615,6 @@ pub mod testing {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::internal::*;
|
||||
use super::public::*;
|
||||
use super::privileged::*;
|
||||
|
||||
use runtime_io::{with_externalities, twox_128, TestExternalities};
|
||||
@@ -569,6 +623,9 @@ mod tests {
|
||||
use environment::with_env;
|
||||
use demo_primitives::AccountId;
|
||||
use runtime::{staking, session};
|
||||
use runtime::democracy::PrivPass;
|
||||
use runtime::staking::public::{Call, Dispatch};
|
||||
use runtime::staking::privileged::{Call as PCall, Dispatch as PDispatch};
|
||||
|
||||
#[test]
|
||||
fn staking_should_work() {
|
||||
@@ -581,6 +638,7 @@ mod tests {
|
||||
twox_128(VALIDATOR_COUNT).to_vec() => vec![].and(&2u32),
|
||||
twox_128(BONDING_DURATION).to_vec() => vec![].and(&3u64),
|
||||
twox_128(TOTAL_STAKE).to_vec() => vec![].and(&100u64),
|
||||
twox_128(TRANSACTION_FEE).to_vec() => vec![].and(&0u64),
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&10u64),
|
||||
twox_128(&Bob.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&20u64),
|
||||
twox_128(&Charlie.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&30u64),
|
||||
@@ -595,9 +653,9 @@ mod tests {
|
||||
|
||||
// Block 1: Add three validators. No obvious change.
|
||||
with_env(|e| e.block_number = 1);
|
||||
stake(&Alice);
|
||||
stake(&Bob);
|
||||
stake(&Dave);
|
||||
public::Call::stake().dispatch(PublicPass::new(&Alice));
|
||||
PublicPass::new(&Bob).stake();
|
||||
PublicPass::new(&Dave).stake();
|
||||
check_new_era();
|
||||
assert_eq!(session::validators(), vec![[10u8; 32], [20u8; 32]]);
|
||||
|
||||
@@ -608,8 +666,8 @@ mod tests {
|
||||
|
||||
// Block 3: Unstake highest, introduce another staker. No change yet.
|
||||
with_env(|e| e.block_number = 3);
|
||||
stake(&Charlie);
|
||||
unstake(&Dave);
|
||||
PublicPass::new(&Charlie).stake();
|
||||
PublicPass::new(&Dave).unstake();
|
||||
check_new_era();
|
||||
|
||||
// Block 4: New era - validators change.
|
||||
@@ -619,7 +677,7 @@ mod tests {
|
||||
|
||||
// Block 5: Transfer stake from highest to lowest. No change yet.
|
||||
with_env(|e| e.block_number = 5);
|
||||
transfer(&Dave, &Alice, 40);
|
||||
PublicPass::new(&Dave).transfer(Alice.to_raw_public(), 40);
|
||||
check_new_era();
|
||||
|
||||
// Block 6: Lowest now validator.
|
||||
@@ -629,7 +687,7 @@ mod tests {
|
||||
|
||||
// Block 7: Unstake three. No change yet.
|
||||
with_env(|e| e.block_number = 7);
|
||||
unstake(&Charlie);
|
||||
PublicPass::new(&Charlie).unstake();
|
||||
check_new_era();
|
||||
assert_eq!(session::validators(), vec![Alice.to_raw_public(), Charlie.into()]);
|
||||
|
||||
@@ -668,7 +726,7 @@ mod tests {
|
||||
|
||||
// Block 3: Schedule an era length change; no visible changes.
|
||||
with_env(|e| e.block_number = 3);
|
||||
set_sessions_per_era(3);
|
||||
PrivPass::test().set_sessions_per_era(3);
|
||||
check_new_era();
|
||||
assert_eq!(sessions_per_era(), 2u64);
|
||||
assert_eq!(last_era_length_change(), 0u64);
|
||||
@@ -719,9 +777,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn staking_balance_transfer_works() {
|
||||
with_externalities(&mut TestExternalities::default(), || {
|
||||
set_free_balance(&Alice, 111);
|
||||
transfer(&Alice, &Bob, 69);
|
||||
with_externalities(&mut testing::externalities(1, 3, 1), || {
|
||||
set_free_balance(&Alice, 112);
|
||||
PublicPass::new(&Alice).transfer(Bob.to_raw_public(), 69);
|
||||
assert_eq!(balance(&Alice), 42);
|
||||
assert_eq!(balance(&Bob), 69);
|
||||
});
|
||||
@@ -732,8 +790,8 @@ mod tests {
|
||||
fn staking_balance_transfer_when_bonded_panics() {
|
||||
with_externalities(&mut TestExternalities::default(), || {
|
||||
set_free_balance(&Alice, 111);
|
||||
stake(&Alice);
|
||||
transfer(&Alice, &Bob, 69);
|
||||
PublicPass::new(&Alice).stake();
|
||||
PublicPass::new(&Alice).transfer(Bob.to_raw_public(), 69);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -760,7 +818,7 @@ mod tests {
|
||||
with_externalities(&mut TestExternalities::default(), || {
|
||||
set_free_balance(&Alice, 111);
|
||||
reserve_balance(&Alice, 69);
|
||||
transfer(&Alice, &Bob, 69);
|
||||
PublicPass::new(&Alice).transfer(Bob.to_raw_public(), 69);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -23,15 +23,18 @@ use runtime_io::{print, storage_root, enumerated_trie_root};
|
||||
use codec::{KeyedVec, Slicable};
|
||||
use runtime_support::{Hashable, storage};
|
||||
use environment::with_env;
|
||||
use demo_primitives::{AccountId, Hash, TxOrder, BlockNumber, Block, Header,
|
||||
UncheckedTransaction, Function, Log};
|
||||
use demo_primitives::{AccountId, Hash, TxOrder, BlockNumber, Header, Log};
|
||||
use block::Block;
|
||||
use transaction::UncheckedTransaction;
|
||||
use runtime::{staking, session};
|
||||
use runtime::democracy::PrivPass;
|
||||
use dispatch;
|
||||
|
||||
pub const NONCE_OF: &[u8] = b"sys:non:";
|
||||
pub const BLOCK_HASH_AT: &[u8] = b"sys:old:";
|
||||
pub const CODE: &[u8] = b"sys:cod";
|
||||
|
||||
|
||||
/// The current block number being processed. Set by `execute_block`.
|
||||
pub fn block_number() -> BlockNumber {
|
||||
with_env(|e| e.block_number)
|
||||
@@ -42,12 +45,15 @@ pub fn block_hash(number: BlockNumber) -> Hash {
|
||||
storage::get_or_default(&number.to_keyed_vec(BLOCK_HASH_AT))
|
||||
}
|
||||
|
||||
pub mod privileged {
|
||||
use super::*;
|
||||
impl_dispatch! {
|
||||
pub mod privileged;
|
||||
fn set_code(new: Vec<u8>) = 0;
|
||||
}
|
||||
|
||||
impl privileged::Dispatch for PrivPass {
|
||||
/// Set the new code.
|
||||
pub fn set_code(new: &[u8]) {
|
||||
storage::unhashed::put_raw(b":code", new);
|
||||
fn set_code(self, new: Vec<u8>) {
|
||||
storage::unhashed::put_raw(b":code", &new);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,6 +143,7 @@ fn execute_transaction(utx: UncheckedTransaction) {
|
||||
Err(_) => panic!("All transactions should be properly signed"),
|
||||
};
|
||||
|
||||
{
|
||||
// check nonce
|
||||
let nonce_key = tx.signed.to_keyed_vec(NONCE_OF);
|
||||
let expected_nonce: TxOrder = storage::get_or(&nonce_key, 0);
|
||||
@@ -144,9 +151,11 @@ fn execute_transaction(utx: UncheckedTransaction) {
|
||||
|
||||
// increment nonce in storage
|
||||
storage::put(&nonce_key, &(expected_nonce + 1));
|
||||
}
|
||||
|
||||
// decode parameters and dispatch
|
||||
dispatch::function(&tx.function, &tx.signed);
|
||||
let tx = tx.drain().transaction;
|
||||
tx.function.dispatch(staking::PublicPass::new(&tx.signed));
|
||||
}
|
||||
|
||||
fn initial_checks(block: &Block) {
|
||||
@@ -226,27 +235,31 @@ mod tests {
|
||||
use keyring::Keyring::*;
|
||||
use environment::with_env;
|
||||
use primitives::hexdisplay::HexDisplay;
|
||||
use demo_primitives::{Header, Digest, UncheckedTransaction, Transaction, Function};
|
||||
use demo_primitives::{Header, Digest};
|
||||
use transaction::{UncheckedTransaction, Transaction};
|
||||
use runtime::staking;
|
||||
use dispatch::public::Call as PubCall;
|
||||
use runtime::staking::public::Call as StakingCall;
|
||||
|
||||
#[test]
|
||||
fn staking_balance_transfer_dispatch_works() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&One.to_raw_public().to_keyed_vec(staking::BALANCE_OF)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
twox_128(&One.to_raw_public().to_keyed_vec(staking::BALANCE_OF)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TRANSACTION_FEE).to_vec() => vec![10u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
];
|
||||
|
||||
let tx = UncheckedTransaction {
|
||||
transaction: Transaction {
|
||||
signed: One.into(),
|
||||
nonce: 0,
|
||||
function: Function::StakingTransfer(Two.into(), 69),
|
||||
function: PubCall::Staking(StakingCall::transfer(Two.into(), 69)),
|
||||
},
|
||||
signature: hex!("5f9832c5a4a39e2dd4a3a0c5b400e9836beb362cb8f7d845a8291a2ae6fe366612e080e4acd0b5a75c3d0b6ee69614a68fb63698c1e76bf1f2dcd8fa617ddf05").into(),
|
||||
signature: hex!("3a682213cb10e8e375fe0817fe4d220a4622d910088809ed7fc8b4ea3871531dbadb22acfedd28a100a0b7bd2d274e0ff873655b13c88f4640b5569db3222706").into(),
|
||||
};
|
||||
|
||||
with_externalities(&mut t, || {
|
||||
internal::execute_transaction(tx, Header::from_block_number(1));
|
||||
assert_eq!(staking::balance(&One), 42);
|
||||
assert_eq!(staking::balance(&One), 32);
|
||||
assert_eq!(staking::balance(&Two), 69);
|
||||
});
|
||||
}
|
||||
@@ -262,7 +275,7 @@ mod tests {
|
||||
let h = Header {
|
||||
parent_hash: [69u8; 32].into(),
|
||||
number: 1,
|
||||
state_root: hex!("f4f6408fe3ce1d78d30bb7ed625b32f91e45b8b566023df309cfd93c6f4af9a4").into(),
|
||||
state_root: hex!("584e0c1f4d4b96153591e3906d756762493dffeb5fa7159e7107014aec8d9c3d").into(),
|
||||
transaction_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(),
|
||||
digest: Digest { logs: vec![], },
|
||||
};
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
//! Timestamp manager: just handles the current timestamp.
|
||||
|
||||
use runtime_support::storage;
|
||||
use runtime::staking::PublicPass;
|
||||
|
||||
pub type Timestamp = u64;
|
||||
|
||||
@@ -27,11 +28,14 @@ pub fn get() -> Timestamp {
|
||||
storage::get_or_default(CURRENT_TIMESTAMP)
|
||||
}
|
||||
|
||||
pub mod public {
|
||||
use super::*;
|
||||
impl_dispatch! {
|
||||
pub mod public;
|
||||
fn set(now: Timestamp) = 0;
|
||||
}
|
||||
|
||||
impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
/// Set the current time.
|
||||
pub fn set(now: Timestamp) {
|
||||
fn set(self, now: Timestamp) {
|
||||
storage::put(CURRENT_TIMESTAMP, &now);
|
||||
}
|
||||
}
|
||||
@@ -44,6 +48,8 @@ mod tests {
|
||||
use runtime_io::{with_externalities, twox_128, TestExternalities};
|
||||
use runtime::timestamp;
|
||||
use codec::{Joiner, KeyedVec};
|
||||
use demo_primitives::AccountId;
|
||||
use runtime::staking::PublicPass;
|
||||
|
||||
#[test]
|
||||
fn timestamp_works() {
|
||||
@@ -53,7 +59,7 @@ mod tests {
|
||||
|
||||
with_externalities(&mut t, || {
|
||||
assert_eq!(get(), 42);
|
||||
set(69);
|
||||
PublicPass::nobody().set(69);
|
||||
assert_eq!(get(), 69);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
|
||||
// Substrate Demo 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.
|
||||
|
||||
// Substrate Demo 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 Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Transaction type.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::ops;
|
||||
use codec::{Input, Slicable};
|
||||
use demo_primitives::{AccountId, TxOrder, Signature};
|
||||
use dispatch::PubCall;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::fmt;
|
||||
|
||||
/// A vetted and verified transaction from the external world.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub struct Transaction {
|
||||
/// Who signed it (note this is not a signature).
|
||||
pub signed: AccountId,
|
||||
/// The number of transactions have come before from the same signer.
|
||||
pub nonce: TxOrder,
|
||||
/// The function that should be called.
|
||||
pub function: PubCall,
|
||||
}
|
||||
|
||||
impl Slicable for Transaction {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Transaction {
|
||||
signed: Slicable::decode(input)?,
|
||||
nonce: Slicable::decode(input)?,
|
||||
function: Slicable::decode(input)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
|
||||
self.signed.using_encoded(|s| v.extend(s));
|
||||
self.nonce.using_encoded(|s| v.extend(s));
|
||||
self.function.using_encoded(|s| v.extend(s));
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
impl ::codec::NonTrivialSlicable for Transaction {}
|
||||
|
||||
/// A transactions right from the external world. Unchecked.
|
||||
#[derive(Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
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: Signature,
|
||||
}
|
||||
|
||||
impl Slicable for UncheckedTransaction {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
// This is a little more complicated than usual since the binary format must be compatible
|
||||
// with substrate's generic `Vec<u8>` type. Basically this just means accepting that there
|
||||
// will be a prefix of u32, which has the total number of bytes following (we don't need
|
||||
// to use this).
|
||||
let _length_do_not_remove_me_see_above: u32 = Slicable::decode(input)?;
|
||||
|
||||
Some(UncheckedTransaction {
|
||||
transaction: Slicable::decode(input)?,
|
||||
signature: Slicable::decode(input)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
|
||||
// need to prefix with the total length as u32 to ensure it's binary comptible with
|
||||
// Vec<u8>. we'll make room for it here, then overwrite once we know the length.
|
||||
v.extend(&[0u8; 4]);
|
||||
|
||||
self.transaction.signed.using_encoded(|s| v.extend(s));
|
||||
self.transaction.nonce.using_encoded(|s| v.extend(s));
|
||||
self.transaction.function.using_encoded(|s| v.extend(s));
|
||||
self.signature.using_encoded(|s| v.extend(s));
|
||||
|
||||
let length = (v.len() - 4) as u32;
|
||||
length.using_encoded(|s| v[0..4].copy_from_slice(s));
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
impl ::codec::NonTrivialSlicable for UncheckedTransaction {}
|
||||
|
||||
impl PartialEq for UncheckedTransaction {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.signature.iter().eq(other.signature.iter()) && self.transaction == other.transaction
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl fmt::Debug for UncheckedTransaction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "UncheckedTransaction({:?})", self.transaction)
|
||||
}
|
||||
}
|
||||
|
||||
/// A type-safe indicator that a transaction has been checked.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
pub struct CheckedTransaction(UncheckedTransaction);
|
||||
|
||||
impl CheckedTransaction {
|
||||
/// Get a reference to the checked signature.
|
||||
pub fn signature(&self) -> &Signature {
|
||||
&self.0.signature
|
||||
}
|
||||
|
||||
/// Get the inner object.
|
||||
pub fn drain(self) -> UncheckedTransaction {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for CheckedTransaction {
|
||||
type Target = Transaction;
|
||||
|
||||
fn deref(&self) -> &Transaction {
|
||||
&self.0.transaction
|
||||
}
|
||||
}
|
||||
|
||||
/// Check the signature on a transaction.
|
||||
///
|
||||
/// On failure, return the transaction back.
|
||||
pub fn check(tx: UncheckedTransaction) -> Result<CheckedTransaction, UncheckedTransaction> {
|
||||
let msg = ::codec::Slicable::encode(&tx.transaction);
|
||||
if ::runtime_io::ed25519_verify(&tx.signature.0, &msg, &tx.transaction.signed) {
|
||||
Ok(CheckedTransaction(tx))
|
||||
} else {
|
||||
Err(tx)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use primitives;
|
||||
use codec::Slicable;
|
||||
use primitives::hexdisplay::HexDisplay;
|
||||
use dispatch::public::Call;
|
||||
use runtime::timestamp::public::Call as TimestampCall;
|
||||
|
||||
#[test]
|
||||
fn serialize_unchecked() {
|
||||
let tx = UncheckedTransaction {
|
||||
transaction: Transaction {
|
||||
signed: [1; 32],
|
||||
nonce: 999u64,
|
||||
function: Call::Timestamp(TimestampCall::set(135135)),
|
||||
},
|
||||
signature: primitives::hash::H512([0; 64]),
|
||||
};
|
||||
// 71000000
|
||||
// 0101010101010101010101010101010101010101010101010101010101010101
|
||||
// e703000000000000
|
||||
// 00
|
||||
// df0f0200
|
||||
// 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
|
||||
let v = Slicable::encode(&tx);
|
||||
println!("{}", HexDisplay::from(&v));
|
||||
assert_eq!(UncheckedTransaction::decode(&mut &v[..]).unwrap(), tx);
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -1,81 +0,0 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Endian manager.
|
||||
|
||||
/// Trait to allow conversion to a know endian representation when sensitive.
|
||||
/// Types implementing this trait must have a size > 0.
|
||||
// note: the copy bound and static lifetimes are necessary for safety of `Slicable` blanket
|
||||
// implementation.
|
||||
pub trait EndianSensitive: Copy + 'static {
|
||||
fn to_le(self) -> Self { self }
|
||||
fn to_be(self) -> Self { self }
|
||||
fn from_le(self) -> Self { self }
|
||||
fn from_be(self) -> Self { self }
|
||||
fn as_be_then<T, F: FnOnce(&Self) -> T>(&self, f: F) -> T { f(&self) }
|
||||
fn as_le_then<T, F: FnOnce(&Self) -> T>(&self, f: F) -> T { f(&self) }
|
||||
}
|
||||
|
||||
macro_rules! impl_endians {
|
||||
( $( $t:ty ),* ) => { $(
|
||||
impl EndianSensitive for $t {
|
||||
fn to_le(self) -> Self { <$t>::to_le(self) }
|
||||
fn to_be(self) -> Self { <$t>::to_be(self) }
|
||||
fn from_le(self) -> Self { <$t>::from_le(self) }
|
||||
fn from_be(self) -> Self { <$t>::from_be(self) }
|
||||
fn as_be_then<T, F: FnOnce(&Self) -> T>(&self, f: F) -> T { let d = self.to_be(); f(&d) }
|
||||
fn as_le_then<T, F: FnOnce(&Self) -> T>(&self, f: F) -> T { let d = self.to_le(); f(&d) }
|
||||
}
|
||||
)* }
|
||||
}
|
||||
macro_rules! impl_non_endians {
|
||||
( $( $t:ty ),* ) => { $(
|
||||
impl EndianSensitive for $t {}
|
||||
)* }
|
||||
}
|
||||
|
||||
// NOTE: See test to ensure correctness.
|
||||
impl EndianSensitive for bool {}
|
||||
|
||||
impl_endians!(u16, u32, u64, usize, i16, i32, i64, isize);
|
||||
impl_non_endians!(u8, i8, [u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8],
|
||||
[u8; 10], [u8; 12], [u8; 14], [u8; 16], [u8; 20], [u8; 24], [u8; 28], [u8; 32], [u8; 40],
|
||||
[u8; 48], [u8; 56], [u8; 64], [u8; 80], [u8; 96], [u8; 112], [u8; 128]);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::EndianSensitive;
|
||||
|
||||
#[test]
|
||||
fn endian_sensitive_is_copy() {
|
||||
fn _takes_copy<T: Copy>() { }
|
||||
fn _takes_endian_sensitive<T: EndianSensitive>() { _takes_copy::<T>() }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn endian_sensitive_outlives_static() {
|
||||
fn _takes_static<T: 'static>() { }
|
||||
fn _takes_endian_sensitive<T: EndianSensitive>() { _takes_static::<T>() }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bool_is_not_endian_sensitive() {
|
||||
let b = true;
|
||||
assert_eq!(b.to_be(), b.to_le());
|
||||
let b = false;
|
||||
assert_eq!(b.to_be(), b.to_le());
|
||||
}
|
||||
}
|
||||
@@ -23,12 +23,10 @@
|
||||
#[cfg_attr(not(feature = "std"), macro_use)]
|
||||
extern crate substrate_runtime_std as rstd;
|
||||
|
||||
mod endiansensitive;
|
||||
mod slicable;
|
||||
mod joiner;
|
||||
mod keyedvec;
|
||||
|
||||
pub use self::endiansensitive::EndianSensitive;
|
||||
pub use self::slicable::{Input, Slicable, NonTrivialSlicable};
|
||||
pub use self::joiner::Joiner;
|
||||
pub use self::keyedvec::KeyedVec;
|
||||
|
||||
@@ -16,10 +16,9 @@
|
||||
|
||||
//! Serialisation.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::{mem, slice};
|
||||
use rstd::vec::Vec;
|
||||
use super::joiner::Joiner;
|
||||
use super::endiansensitive::EndianSensitive;
|
||||
|
||||
/// Trait that allows reading of data into a slice.
|
||||
pub trait Input {
|
||||
@@ -56,39 +55,6 @@ pub trait Slicable: Sized {
|
||||
// TODO: under specialization, remove this and simply specialize in place serializable types.
|
||||
pub trait NonTrivialSlicable: Slicable {}
|
||||
|
||||
impl<T: EndianSensitive> Slicable for T {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
let size = mem::size_of::<T>();
|
||||
assert!(size > 0, "EndianSensitive can never be implemented for a zero-sized type.");
|
||||
let mut val: T = unsafe { mem::zeroed() };
|
||||
|
||||
unsafe {
|
||||
let raw: &mut [u8] = slice::from_raw_parts_mut(
|
||||
&mut val as *mut T as *mut u8,
|
||||
size
|
||||
);
|
||||
if input.read(raw) != size { return None }
|
||||
}
|
||||
Some(val.from_le())
|
||||
}
|
||||
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
self.as_le_then(|le| {
|
||||
let size = mem::size_of::<T>();
|
||||
let value_slice = unsafe {
|
||||
let ptr = le as *const _ as *const u8;
|
||||
if size != 0 {
|
||||
slice::from_raw_parts(ptr, size)
|
||||
} else {
|
||||
&[]
|
||||
}
|
||||
};
|
||||
|
||||
f(value_slice)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Slicable for Option<bool> {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
u8::decode(input).and_then(|v| match v {
|
||||
@@ -109,6 +75,16 @@ impl Slicable for Option<bool> {
|
||||
}
|
||||
impl NonTrivialSlicable for Option<bool> {}
|
||||
|
||||
impl<T: Slicable> Slicable for Box<T> {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Box::new(T::decode(input)?))
|
||||
}
|
||||
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
self.as_ref().using_encoded(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Slicable for Vec<u8> {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
u32::decode(input).and_then(move |len| {
|
||||
@@ -173,7 +149,7 @@ macro_rules! impl_vec_simple_array {
|
||||
($($size:expr),*) => {
|
||||
$(
|
||||
impl<T> Slicable for Vec<[T; $size]>
|
||||
where [T; $size]: EndianSensitive
|
||||
where [T; $size]: Slicable
|
||||
{
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
u32::decode(input).and_then(move |len| {
|
||||
@@ -325,6 +301,108 @@ mod inner_tuple_impl {
|
||||
tuple_impl!(A, B, C, D, E, F, G, H, I, J, K,);
|
||||
}
|
||||
|
||||
/// Trait to allow conversion to a know endian representation when sensitive.
|
||||
/// Types implementing this trait must have a size > 0.
|
||||
// note: the copy bound and static lifetimes are necessary for safety of `Slicable` blanket
|
||||
// implementation.
|
||||
trait EndianSensitive: Copy + 'static {
|
||||
fn to_le(self) -> Self { self }
|
||||
fn to_be(self) -> Self { self }
|
||||
fn from_le(self) -> Self { self }
|
||||
fn from_be(self) -> Self { self }
|
||||
fn as_be_then<T, F: FnOnce(&Self) -> T>(&self, f: F) -> T { f(&self) }
|
||||
fn as_le_then<T, F: FnOnce(&Self) -> T>(&self, f: F) -> T { f(&self) }
|
||||
}
|
||||
|
||||
macro_rules! impl_endians {
|
||||
( $( $t:ty ),* ) => { $(
|
||||
impl EndianSensitive for $t {
|
||||
fn to_le(self) -> Self { <$t>::to_le(self) }
|
||||
fn to_be(self) -> Self { <$t>::to_be(self) }
|
||||
fn from_le(self) -> Self { <$t>::from_le(self) }
|
||||
fn from_be(self) -> Self { <$t>::from_be(self) }
|
||||
fn as_be_then<T, F: FnOnce(&Self) -> T>(&self, f: F) -> T { let d = self.to_be(); f(&d) }
|
||||
fn as_le_then<T, F: FnOnce(&Self) -> T>(&self, f: F) -> T { let d = self.to_le(); f(&d) }
|
||||
}
|
||||
|
||||
impl Slicable for $t {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
let size = mem::size_of::<$t>();
|
||||
assert!(size > 0, "EndianSensitive can never be implemented for a zero-sized type.");
|
||||
let mut val: $t = unsafe { mem::zeroed() };
|
||||
|
||||
unsafe {
|
||||
let raw: &mut [u8] = slice::from_raw_parts_mut(
|
||||
&mut val as *mut $t as *mut u8,
|
||||
size
|
||||
);
|
||||
if input.read(raw) != size { return None }
|
||||
}
|
||||
Some(val.from_le())
|
||||
}
|
||||
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
self.as_le_then(|le| {
|
||||
let size = mem::size_of::<$t>();
|
||||
let value_slice = unsafe {
|
||||
let ptr = le as *const _ as *const u8;
|
||||
if size != 0 {
|
||||
slice::from_raw_parts(ptr, size)
|
||||
} else {
|
||||
&[]
|
||||
}
|
||||
};
|
||||
|
||||
f(value_slice)
|
||||
})
|
||||
}
|
||||
}
|
||||
)* }
|
||||
}
|
||||
macro_rules! impl_non_endians {
|
||||
( $( $t:ty ),* ) => { $(
|
||||
impl EndianSensitive for $t {}
|
||||
|
||||
impl Slicable for $t {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
let size = mem::size_of::<$t>();
|
||||
assert!(size > 0, "EndianSensitive can never be implemented for a zero-sized type.");
|
||||
let mut val: $t = unsafe { mem::zeroed() };
|
||||
|
||||
unsafe {
|
||||
let raw: &mut [u8] = slice::from_raw_parts_mut(
|
||||
&mut val as *mut $t as *mut u8,
|
||||
size
|
||||
);
|
||||
if input.read(raw) != size { return None }
|
||||
}
|
||||
Some(val.from_le())
|
||||
}
|
||||
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
self.as_le_then(|le| {
|
||||
let size = mem::size_of::<$t>();
|
||||
let value_slice = unsafe {
|
||||
let ptr = le as *const _ as *const u8;
|
||||
if size != 0 {
|
||||
slice::from_raw_parts(ptr, size)
|
||||
} else {
|
||||
&[]
|
||||
}
|
||||
};
|
||||
|
||||
f(value_slice)
|
||||
})
|
||||
}
|
||||
}
|
||||
)* }
|
||||
}
|
||||
|
||||
impl_endians!(u16, u32, u64, usize, i16, i32, i64, isize);
|
||||
impl_non_endians!(u8, i8, [u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8],
|
||||
[u8; 10], [u8; 12], [u8; 14], [u8; 16], [u8; 20], [u8; 24], [u8; 28], [u8; 32], [u8; 40],
|
||||
[u8; 48], [u8; 56], [u8; 64], [u8; 80], [u8; 96], [u8; 112], [u8; 128], bool);
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
@@ -244,7 +244,7 @@ mod test {
|
||||
let pair = Pair::from_seed(b"12345678901234567890123456789012");
|
||||
let public = pair.public();
|
||||
assert_eq!(public, Public::from_raw(hex!("2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee")));
|
||||
let message = hex!("2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee00000000000000002228000000d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a4500000000000000");
|
||||
let message = hex!("2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee00000000000000000200d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a4500000000000000");
|
||||
let signature = pair.sign(&message[..]);
|
||||
println!("Correct signature: {}", HexDisplay::from(&signature.0));
|
||||
assert!(verify_strong(&signature, &message[..], &public));
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
Reference in New Issue
Block a user