Runtime dispatch calls return Result (#192)

* Merge remote-tracking branch 'origin/master' into gav-xts-dont-panic

* Update wasm.

* consensus, session and staking all panic-safe.

* Democracy doesn't panic in apply.

* Fix tests.

* Extra helper macro, council depanicked.

* Fix one test.

* Fix up all council tests. No panics!

* Council voting depanicked.

* Dispatch returns result.

* session & staking tests updated

* Fix democracy tests.

* Fix council tests.

* Fix up polkadot parachains in runtime

* Fix borked merge
This commit is contained in:
Gav Wood
2018-06-01 18:32:40 +02:00
committed by GitHub
parent 1dada4f7a0
commit a306074eb7
13 changed files with 657 additions and 594 deletions
+13 -10
View File
@@ -26,6 +26,7 @@ use polkadot_primitives::parachain::{Id, Chain, DutyRoster, CandidateReceipt};
use {system, session};
use runtime_support::{StorageValue, StorageMap};
use runtime_support::dispatch::Result;
#[cfg(any(feature = "std", test))]
use rstd::marker::PhantomData;
@@ -44,7 +45,7 @@ decl_module! {
pub struct Module<T: Trait>;
pub enum Call where aux: <T as Trait>::PublicAux {
// provide candidate receipts for parachains, in ascending order by id.
fn set_heads(aux, heads: Vec<CandidateReceipt>) = 0;
fn set_heads(aux, heads: Vec<CandidateReceipt>) -> Result = 0;
}
}
@@ -137,13 +138,13 @@ impl<T: Trait> Module<T> {
<Parachains<T>>::put(parachains);
}
fn set_heads(aux: &<T as Trait>::PublicAux, heads: Vec<CandidateReceipt>) {
assert!(aux.is_empty());
assert!(!<DidUpdate<T>>::exists(), "Parachain heads must be updated only once in the block");
assert!(
fn set_heads(aux: &<T as Trait>::PublicAux, heads: Vec<CandidateReceipt>) -> Result {
ensure!(aux.is_empty(), "set_heads must not be signed");
ensure!(!<DidUpdate<T>>::exists(), "Parachain heads must be updated only once in the block");
ensure!(
<system::Module<T>>::extrinsic_index() == T::SET_POSITION,
"Parachain heads update extrinsic must be at position {} in the block",
T::SET_POSITION
"Parachain heads update extrinsic must be at position {} in the block"
// , T::SET_POSITION
);
let active_parachains = Self::active_parachains();
@@ -151,10 +152,10 @@ impl<T: Trait> Module<T> {
// perform this check before writing to storage.
for head in &heads {
assert!(
ensure!(
iter.find(|&p| p == &head.parachain_index).is_some(),
"Submitted candidate for unregistered or out-of-order parachain {}",
head.parachain_index.into_inner()
"Submitted candidate for unregistered or out-of-order parachain {}"
// , head.parachain_index.into_inner()
);
}
@@ -164,6 +165,8 @@ impl<T: Trait> Module<T> {
}
<DidUpdate<T>>::put(true);
Ok(())
}
}
@@ -29,6 +29,7 @@ pub use std::ptr;
pub use std::rc;
pub use std::slice;
pub use std::vec;
pub use std::result;
pub mod collections {
pub use std::collections::btree_map;
@@ -37,6 +37,7 @@ pub use core::num;
pub use core::ops;
pub use core::ptr;
pub use core::slice;
pub use core::result;
pub mod collections {
pub use alloc::btree_map;
@@ -19,20 +19,23 @@
pub use rstd::prelude::{Vec, Clone, Eq, PartialEq};
#[cfg(feature = "std")]
pub use std::fmt;
pub use rstd::result;
pub use rstd::marker::PhantomData;
#[cfg(feature = "std")]
use serde;
pub use codec::{Slicable, Input};
pub type Result = result::Result<(), &'static str>;
pub trait Dispatchable {
type Trait;
fn dispatch(self);
fn dispatch(self) -> Result;
}
pub trait AuxDispatchable {
type Aux;
type Trait;
fn dispatch(self, aux: &Self::Aux);
fn dispatch(self, aux: &Self::Aux) -> Result;
}
#[cfg(feature = "std")]
@@ -104,7 +107,7 @@ macro_rules! decl_dispatch {
$(
$param_name:ident : $param:ty
),*
)
) -> $result:ty
= $id:expr ;
)*
}
@@ -114,7 +117,7 @@ macro_rules! decl_dispatch {
impl for $mod_type<$trait_instance: $trait_name>;
pub enum $call_type;
$(
fn $fn_name( $( $param_name: $param ),* ) = $id;
fn $fn_name( $( $param_name: $param ),* ) -> $result = $id;
)*
}
decl_dispatch! {
@@ -131,7 +134,7 @@ macro_rules! decl_dispatch {
$(
, $param_name:ident : $param:ty
)*
)
) -> $result:ty
= $id:expr ;
)*
}
@@ -141,7 +144,7 @@ macro_rules! decl_dispatch {
impl for $mod_type<$trait_instance: $trait_name>;
pub enum $call_type where aux: $aux_type;
$(
fn $fn_name(aux $(, $param_name: $param )*)= $id;
fn $fn_name(aux $(, $param_name: $param )*) -> $result = $id;
)*
}
decl_dispatch! {
@@ -154,11 +157,11 @@ macro_rules! decl_dispatch {
impl for $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
) => {
impl<$trait_instance: $trait_name> $mod_type<$trait_instance> {
pub fn aux_dispatch<D: $crate::dispatch::AuxDispatchable<Trait = $trait_instance>>(d: D, aux: &D::Aux) {
d.dispatch(aux);
pub fn aux_dispatch<D: $crate::dispatch::AuxDispatchable<Trait = $trait_instance>>(d: D, aux: &D::Aux) -> $crate::dispatch::Result {
d.dispatch(aux)
}
pub fn dispatch<D: $crate::dispatch::Dispatchable<Trait = $trait_instance>>(d: D) {
d.dispatch();
pub fn dispatch<D: $crate::dispatch::Dispatchable<Trait = $trait_instance>>(d: D) -> $crate::dispatch::Result {
d.dispatch()
}
}
}
@@ -176,19 +179,20 @@ macro_rules! __decl_dispatch_module_without_aux {
$param_name:ident : $param:ty
),*
)
-> $result:ty
= $id:expr ;
)*
) => {
__decl_dispatch_module_common! {
impl for $mod_type<$trait_instance: $trait_name>;
pub enum $call_type;
$( fn $fn_name( $( $param_name : $param ),* ) = $id ; )*
$( fn $fn_name( $( $param_name : $param ),* ) -> $result = $id ; )*
}
impl<$trait_instance: $trait_name> $crate::dispatch::Dispatchable
for $call_type<$trait_instance>
{
type Trait = $trait_instance;
fn dispatch(self) {
fn dispatch(self) -> $crate::dispatch::Result {
match self {
$(
$call_type::$fn_name( $( $param_name ),* ) =>
@@ -218,20 +222,21 @@ macro_rules! __decl_dispatch_module_with_aux {
, $param_name:ident : $param:ty
)*
)
-> $result:ty
= $id:expr ;
)*
) => {
__decl_dispatch_module_common! {
impl for $mod_type<$trait_instance: $trait_name>;
pub enum $call_type;
$( fn $fn_name( $( $param_name : $param ),* ) = $id ; )*
$( fn $fn_name( $( $param_name : $param ),* ) -> $result = $id ; )*
}
impl<$trait_instance: $trait_name> $crate::dispatch::AuxDispatchable
for $call_type<$trait_instance>
{
type Trait = $trait_instance;
type Aux = $aux_type;
fn dispatch(self, aux: &Self::Aux) {
fn dispatch(self, aux: &Self::Aux) -> $crate::dispatch::Result {
match self {
$(
$call_type::$fn_name( $( $param_name ),* ) =>
@@ -261,6 +266,7 @@ macro_rules! __decl_dispatch_module_common {
$param_name:ident : $param:ty
),*
)
-> $result:ty
= $id:expr ;
)*
) => {
@@ -320,7 +326,7 @@ macro_rules! __decl_dispatch_module_common {
impl<$trait_instance: $trait_name> $crate::dispatch::fmt::Debug
for $call_type<$trait_instance>
{
fn fmt(&self, f: &mut $crate::dispatch::fmt::Formatter) -> Result<(), $crate::dispatch::fmt::Error> {
fn fmt(&self, f: &mut $crate::dispatch::fmt::Formatter) -> $crate::dispatch::result::Result<(), $crate::dispatch::fmt::Error> {
match *self {
$(
$call_type::$fn_name( $( ref $param_name ),* ) =>
@@ -408,7 +414,7 @@ macro_rules! impl_outer_dispatch {
impl $crate::dispatch::AuxDispatchable for $call_type {
type Aux = $aux;
type Trait = $call_type;
fn dispatch(self, aux: &$aux) {
fn dispatch(self, aux: &$aux) -> $crate::dispatch::Result {
match self {
$(
$call_type::$camelcase(call) => call.dispatch(&aux),
@@ -448,7 +454,7 @@ macro_rules! impl_outer_dispatch {
impl_outer_dispatch_common! { $call_type, $($camelcase = $id,)* }
impl $crate::dispatch::Dispatchable for $call_type {
type Trait = $call_type;
fn dispatch(self) {
fn dispatch(self) -> $crate::dispatch::Result {
match self {
$(
$call_type::$camelcase(call) => call.dispatch(),
+20 -35
View File
@@ -46,11 +46,11 @@ pub use self::hashable::Hashable;
pub use self::dispatch::{Parameter, Dispatchable, Callable, AuxDispatchable, AuxCallable, IsSubType, IsAuxSubType};
pub use runtime_io::print;
#[macro_export]
macro_rules! fail {
( $y:expr ) => {{
$crate::print($y);
return;
return Err($y);
}}
}
@@ -60,46 +60,31 @@ macro_rules! ensure {
if !$x {
fail!($y);
}
}};
($x:expr) => {{
if !$x {
$crate::print("Bailing! Cannot ensure: ");
$crate::print(stringify!($x));
return;
}
}}
}
#[macro_export]
macro_rules! ensure_unwrap {
($x:expr, $y: expr) => {
if let Some(v) = $x {
v
} else {
fail!{$y}
}
}
}
#[macro_export]
macro_rules! ensure_unwrap_err {
($x:expr, $y: expr) => {
if let Err(v) = $x {
v
} else {
fail!{$y}
}
}
}
#[macro_export]
#[cfg(feature = "std")]
macro_rules! assert_noop {
( $( $x:tt )* ) => {
( $x:expr , $y:expr ) => {
let h = runtime_io::storage_root();
{
$( $x )*
}
assert_err!($x, $y);
assert_eq!(h, runtime_io::storage_root());
}
}
#[macro_export]
#[cfg(feature = "std")]
macro_rules! assert_err {
( $x:expr , $y:expr ) => {
assert_eq!($x, Err($y));
}
}
#[macro_export]
#[cfg(feature = "std")]
macro_rules! assert_ok {
( $x:expr ) => {
assert!($x.is_ok());
}
}
@@ -33,6 +33,7 @@ extern crate substrate_primitives;
use rstd::prelude::*;
use runtime_support::{storage, Parameter};
use runtime_support::dispatch::Result;
use runtime_support::storage::unhashed::StorageVec;
use primitives::traits::RefInto;
use substrate_primitives::bft::MisbehaviorReport;
@@ -59,11 +60,11 @@ pub trait Trait: system::Trait {
decl_module! {
pub struct Module<T: Trait>;
pub enum Call where aux: T::PublicAux {
fn report_misbehavior(aux, report: MisbehaviorReport) = 0;
fn report_misbehavior(aux, report: MisbehaviorReport) -> Result = 0;
}
pub enum PrivCall {
fn set_code(new: Vec<u8>) = 0;
fn set_storage(items: Vec<KeyValue>) = 1;
fn set_code(new: Vec<u8>) -> Result = 0;
fn set_storage(items: Vec<KeyValue>) -> Result = 1;
}
}
@@ -74,20 +75,23 @@ impl<T: Trait> Module<T> {
}
/// Set the new code.
fn set_code(new: Vec<u8>) {
fn set_code(new: Vec<u8>) -> Result {
storage::unhashed::put_raw(CODE, &new);
Ok(())
}
/// Set some items of storage.
fn set_storage(items: Vec<KeyValue>) {
fn set_storage(items: Vec<KeyValue>) -> Result {
for i in &items {
storage::unhashed::put_raw(&i.0, &i.1);
}
Ok(())
}
/// Report some misbehaviour.
fn report_misbehavior(_aux: &T::PublicAux, _report: MisbehaviorReport) {
fn report_misbehavior(_aux: &T::PublicAux, _report: MisbehaviorReport) -> Result {
// TODO.
Ok(())
}
/// Set the current set of authorities' session keys.
File diff suppressed because it is too large Load Diff
@@ -19,21 +19,22 @@
use rstd::prelude::*;
use rstd::borrow::Borrow;
use primitives::traits::{Executable, RefInto};
use runtime_io::Hashing;
use runtime_io::{Hashing, print};
use runtime_support::{StorageValue, StorageMap, IsSubType};
use runtime_support::dispatch::Result;
use {system, democracy};
use super::{Trait, Module as Council};
decl_module! {
pub struct Module<T: Trait>;
pub enum Call where aux: T::PublicAux {
fn propose(aux, proposal: Box<T::Proposal>) = 0;
fn vote(aux, proposal: T::Hash, approve: bool) = 1;
fn veto(aux, proposal_hash: T::Hash) = 2;
fn propose(aux, proposal: Box<T::Proposal>) -> Result = 0;
fn vote(aux, proposal: T::Hash, approve: bool) -> Result = 1;
fn veto(aux, proposal_hash: T::Hash) -> Result = 2;
}
pub enum PrivCall {
fn set_cooloff_period(blocks: T::BlockNumber) = 0;
fn set_voting_period(blocks: T::BlockNumber) = 1;
fn set_cooloff_period(blocks: T::BlockNumber) -> Result = 0;
fn set_voting_period(blocks: T::BlockNumber) -> Result = 1;
}
}
@@ -73,14 +74,14 @@ impl<T: Trait> Module<T> {
}
// Dispatch
fn propose(aux: &T::PublicAux, proposal: Box<T::Proposal>) {
fn propose(aux: &T::PublicAux, proposal: Box<T::Proposal>) -> Result {
let expiry = <system::Module<T>>::block_number() + Self::voting_period();
ensure!(Self::will_still_be_councillor_at(aux.ref_into(), expiry));
ensure!(Self::will_still_be_councillor_at(aux.ref_into(), expiry), "proposer would not be on council");
let proposal_hash = T::Hashing::hash_of(&proposal);
ensure!(!<ProposalOf<T>>::exists(proposal_hash), "No duplicate proposals allowed");
ensure!(!Self::is_vetoed(&proposal_hash));
ensure!(!<ProposalOf<T>>::exists(proposal_hash), "duplicate proposals not allowed");
ensure!(!Self::is_vetoed(&proposal_hash), "proposal is vetoed");
let mut proposals = Self::proposals();
proposals.push((expiry, proposal_hash));
@@ -90,26 +91,28 @@ impl<T: Trait> Module<T> {
<ProposalOf<T>>::insert(proposal_hash, *proposal);
<ProposalVoters<T>>::insert(proposal_hash, vec![aux.ref_into().clone()]);
<CouncilVoteOf<T>>::insert((proposal_hash, aux.ref_into().clone()), true);
Ok(())
}
fn vote(aux: &T::PublicAux, proposal: T::Hash, approve: bool) {
fn vote(aux: &T::PublicAux, proposal: T::Hash, approve: bool) -> Result {
if Self::vote_of((proposal, aux.ref_into().clone())).is_none() {
let mut voters = Self::proposal_voters(&proposal);
voters.push(aux.ref_into().clone());
<ProposalVoters<T>>::insert(proposal, voters);
}
<CouncilVoteOf<T>>::insert((proposal, aux.ref_into().clone()), approve);
Ok(())
}
fn veto(aux: &T::PublicAux, proposal_hash: T::Hash) {
fn veto(aux: &T::PublicAux, proposal_hash: T::Hash) -> Result {
ensure!(Self::is_councillor(aux.ref_into()), "only councillors may veto council proposals");
ensure!(<ProposalVoters<T>>::exists(&proposal_hash), "proposal must exist to be vetoed");
let mut existing_vetoers = Self::veto_of(&proposal_hash)
.map(|pair| pair.1)
.unwrap_or_else(Vec::new);
let insert_position = ensure_unwrap_err!(existing_vetoers.binary_search(aux.ref_into()),
"a councillor may not veto a proposal twice");
let insert_position = existing_vetoers.binary_search(aux.ref_into())
.err().ok_or("a councillor may not veto a proposal twice")?;
existing_vetoers.insert(insert_position, aux.ref_into().clone());
Self::set_veto_of(&proposal_hash, <system::Module<T>>::block_number() + Self::cooloff_period(), existing_vetoers);
@@ -119,14 +122,17 @@ impl<T: Trait> Module<T> {
for (c, _) in <Council<T>>::active_council() {
<CouncilVoteOf<T>>::remove((proposal_hash, c));
}
Ok(())
}
fn set_cooloff_period(blocks: T::BlockNumber) {
fn set_cooloff_period(blocks: T::BlockNumber) -> Result {
<CooloffPeriod<T>>::put(blocks);
Ok(())
}
fn set_voting_period(blocks: T::BlockNumber) {
fn set_voting_period(blocks: T::BlockNumber) -> Result {
<VotingPeriod<T>>::put(blocks);
Ok(())
}
// private
@@ -169,7 +175,7 @@ impl<T: Trait> Module<T> {
}
}
fn end_block(now: T::BlockNumber) {
fn end_block(now: T::BlockNumber) -> Result {
while let Some((proposal, proposal_hash)) = Self::take_proposal_if_expiring_at(now) {
let tally = Self::take_tally(&proposal_hash);
if let Some(&democracy::PrivCall::cancel_referendum(ref_index)) = IsSubType::<democracy::Module<T>>::is_sub_type(&proposal) {
@@ -180,20 +186,27 @@ impl<T: Trait> Module<T> {
if tally.0 > tally.1 + tally.2 {
Self::kill_veto_of(&proposal_hash);
match tally {
(_, 0, 0) => <democracy::Module<T>>::internal_start_referendum(proposal, democracy::VoteThreshold::SuperMajorityAgainst),
_ => <democracy::Module<T>>::internal_start_referendum(proposal, democracy::VoteThreshold::SimpleMajority),
(_, 0, 0) => <democracy::Module<T>>::internal_start_referendum(proposal, democracy::VoteThreshold::SuperMajorityAgainst).map(|_| ())?,
_ => <democracy::Module<T>>::internal_start_referendum(proposal, democracy::VoteThreshold::SimpleMajority).map(|_| ())?,
};
}
}
}
Ok(())
}
}
impl<T: Trait> Executable for Council<T> {
fn execute() {
let n = <system::Module<T>>::block_number();
Self::end_block(n);
<Module<T>>::end_block(n);
if let Err(e) = Self::end_block(n) {
print("Guru meditation");
print(e);
}
if let Err(e) = <Module<T>>::end_block(n) {
print("Guru meditation");
print(e);
}
}
}
@@ -239,19 +252,19 @@ mod tests {
with_externalities(&mut new_test_ext(true), || {
System::set_block_number(1);
let proposal = bonding_duration_proposal(42);
Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove);
assert_ok!(Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove));
assert_eq!(Democracy::active_referendums(), vec![(0, 4, proposal, VoteThreshold::SuperMajorityApprove)]);
let cancellation = cancel_referendum_proposal(0);
let hash = cancellation.blake2_256().into();
CouncilVoting::propose(&1, Box::new(cancellation));
CouncilVoting::vote(&2, hash, true);
CouncilVoting::vote(&3, hash, true);
assert_ok!(CouncilVoting::propose(&1, Box::new(cancellation)));
assert_ok!(CouncilVoting::vote(&2, hash, true));
assert_ok!(CouncilVoting::vote(&3, hash, true));
assert_eq!(CouncilVoting::proposals(), vec![(2, hash)]);
CouncilVoting::end_block(System::block_number());
assert_ok!(CouncilVoting::end_block(System::block_number()));
System::set_block_number(2);
CouncilVoting::end_block(System::block_number());
assert_ok!(CouncilVoting::end_block(System::block_number()));
assert_eq!(Democracy::active_referendums(), vec![]);
assert_eq!(Staking::bonding_duration(), 0);
});
@@ -262,17 +275,17 @@ mod tests {
with_externalities(&mut new_test_ext(true), || {
System::set_block_number(1);
let proposal = bonding_duration_proposal(42);
Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove);
assert_ok!(Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove));
let cancellation = cancel_referendum_proposal(0);
let hash = cancellation.blake2_256().into();
CouncilVoting::propose(&1, Box::new(cancellation));
CouncilVoting::vote(&2, hash, true);
CouncilVoting::vote(&3, hash, false);
CouncilVoting::end_block(System::block_number());
assert_ok!(CouncilVoting::propose(&1, Box::new(cancellation)));
assert_ok!(CouncilVoting::vote(&2, hash, true));
assert_ok!(CouncilVoting::vote(&3, hash, false));
assert_ok!(CouncilVoting::end_block(System::block_number()));
System::set_block_number(2);
CouncilVoting::end_block(System::block_number());
assert_ok!(CouncilVoting::end_block(System::block_number()));
assert_eq!(Democracy::active_referendums(), vec![(0, 4, proposal, VoteThreshold::SuperMajorityApprove)]);
});
}
@@ -282,16 +295,16 @@ mod tests {
with_externalities(&mut new_test_ext(true), || {
System::set_block_number(1);
let proposal = bonding_duration_proposal(42);
Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove);
assert_ok!(Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove));
let cancellation = cancel_referendum_proposal(0);
let hash = cancellation.blake2_256().into();
CouncilVoting::propose(&1, Box::new(cancellation));
CouncilVoting::vote(&2, hash, true);
CouncilVoting::end_block(System::block_number());
assert_ok!(CouncilVoting::propose(&1, Box::new(cancellation)));
assert_ok!(CouncilVoting::vote(&2, hash, true));
assert_ok!(CouncilVoting::end_block(System::block_number()));
System::set_block_number(2);
CouncilVoting::end_block(System::block_number());
assert_ok!(CouncilVoting::end_block(System::block_number()));
assert_eq!(Democracy::active_referendums(), vec![(0, 4, proposal, VoteThreshold::SuperMajorityApprove)]);
});
}
@@ -302,8 +315,8 @@ mod tests {
System::set_block_number(1);
let proposal = bonding_duration_proposal(42);
let hash = proposal.blake2_256().into();
CouncilVoting::propose(&1, Box::new(proposal.clone()));
CouncilVoting::veto(&2, hash);
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
assert_ok!(CouncilVoting::veto(&2, hash));
assert_eq!(CouncilVoting::proposals().len(), 0);
assert_eq!(Democracy::active_referendums().len(), 0);
});
@@ -315,12 +328,12 @@ mod tests {
System::set_block_number(1);
let proposal = bonding_duration_proposal(42);
let hash = proposal.blake2_256().into();
CouncilVoting::propose(&1, Box::new(proposal.clone()));
CouncilVoting::veto(&2, hash);
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
assert_ok!(CouncilVoting::veto(&2, hash));
System::set_block_number(3);
CouncilVoting::propose(&1, Box::new(proposal.clone()));
assert_noop!{CouncilVoting::veto(&2, hash)};
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
assert_noop!(CouncilVoting::veto(&2, hash), "a councillor may not veto a proposal twice");
});
}
@@ -330,11 +343,11 @@ mod tests {
System::set_block_number(1);
let proposal = bonding_duration_proposal(42);
let hash = proposal.blake2_256().into();
CouncilVoting::propose(&1, Box::new(proposal.clone()));
CouncilVoting::veto(&2, hash);
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
assert_ok!(CouncilVoting::veto(&2, hash));
System::set_block_number(2);
assert_noop!{CouncilVoting::propose(&1, Box::new(proposal.clone()))};
assert_noop!(CouncilVoting::propose(&1, Box::new(proposal.clone())), "proposal is vetoed");
});
}
@@ -344,17 +357,17 @@ mod tests {
System::set_block_number(1);
let proposal = bonding_duration_proposal(42);
let hash = proposal.blake2_256().into();
CouncilVoting::propose(&1, Box::new(proposal.clone()));
CouncilVoting::veto(&2, hash);
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
assert_ok!(CouncilVoting::veto(&2, hash));
System::set_block_number(3);
CouncilVoting::propose(&1, Box::new(proposal.clone()));
CouncilVoting::vote(&2, hash, false);
CouncilVoting::vote(&3, hash, true);
CouncilVoting::end_block(System::block_number());
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
assert_ok!(CouncilVoting::vote(&2, hash, false));
assert_ok!(CouncilVoting::vote(&3, hash, true));
assert_ok!(CouncilVoting::end_block(System::block_number()));
System::set_block_number(4);
CouncilVoting::end_block(System::block_number());
assert_ok!(CouncilVoting::end_block(System::block_number()));
assert_eq!(CouncilVoting::proposals().len(), 0);
assert_eq!(Democracy::active_referendums(), vec![(0, 7, bonding_duration_proposal(42), VoteThreshold::SimpleMajority)]);
});
@@ -366,12 +379,12 @@ mod tests {
System::set_block_number(1);
let proposal = bonding_duration_proposal(42);
let hash = proposal.blake2_256().into();
CouncilVoting::propose(&1, Box::new(proposal.clone()));
CouncilVoting::veto(&2, hash);
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
assert_ok!(CouncilVoting::veto(&2, hash));
System::set_block_number(3);
CouncilVoting::propose(&1, Box::new(proposal.clone()));
CouncilVoting::veto(&3, hash);
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
assert_ok!(CouncilVoting::veto(&3, hash));
assert_eq!(CouncilVoting::proposals().len(), 0);
assert_eq!(Democracy::active_referendums().len(), 0);
});
@@ -383,7 +396,7 @@ mod tests {
System::set_block_number(1);
let proposal = bonding_duration_proposal(42);
let hash = proposal.blake2_256().into();
CouncilVoting::propose(&1, Box::new(proposal.clone()));
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
assert_eq!(CouncilVoting::proposals().len(), 1);
assert_eq!(CouncilVoting::proposal_voters(&hash), vec![1]);
assert_eq!(CouncilVoting::vote_of((hash, 1)), Some(true));
@@ -396,12 +409,12 @@ mod tests {
with_externalities(&mut new_test_ext(true), || {
System::set_block_number(1);
let proposal = bonding_duration_proposal(42);
CouncilVoting::propose(&1, Box::new(proposal.clone()));
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
assert_eq!(CouncilVoting::tally(&proposal.blake2_256().into()), (1, 0, 2));
CouncilVoting::end_block(System::block_number());
assert_ok!(CouncilVoting::end_block(System::block_number()));
System::set_block_number(2);
CouncilVoting::end_block(System::block_number());
assert_ok!(CouncilVoting::end_block(System::block_number()));
assert_eq!(CouncilVoting::proposals().len(), 0);
assert_eq!(Democracy::active_referendums().len(), 0);
});
@@ -412,14 +425,14 @@ mod tests {
with_externalities(&mut new_test_ext(true), || {
System::set_block_number(1);
let proposal = bonding_duration_proposal(42);
CouncilVoting::propose(&1, Box::new(proposal.clone()));
CouncilVoting::vote(&2, proposal.blake2_256().into(), true);
CouncilVoting::vote(&3, proposal.blake2_256().into(), true);
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
assert_ok!(CouncilVoting::vote(&2, proposal.blake2_256().into(), true));
assert_ok!(CouncilVoting::vote(&3, proposal.blake2_256().into(), true));
assert_eq!(CouncilVoting::tally(&proposal.blake2_256().into()), (3, 0, 0));
CouncilVoting::end_block(System::block_number());
assert_ok!(CouncilVoting::end_block(System::block_number()));
System::set_block_number(2);
CouncilVoting::end_block(System::block_number());
assert_ok!(CouncilVoting::end_block(System::block_number()));
assert_eq!(CouncilVoting::proposals().len(), 0);
assert_eq!(Democracy::active_referendums(), vec![(0, 5, proposal, VoteThreshold::SuperMajorityAgainst)]);
});
@@ -430,14 +443,14 @@ mod tests {
with_externalities(&mut new_test_ext(true), || {
System::set_block_number(1);
let proposal = bonding_duration_proposal(42);
CouncilVoting::propose(&1, Box::new(proposal.clone()));
CouncilVoting::vote(&2, proposal.blake2_256().into(), true);
CouncilVoting::vote(&3, proposal.blake2_256().into(), false);
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
assert_ok!(CouncilVoting::vote(&2, proposal.blake2_256().into(), true));
assert_ok!(CouncilVoting::vote(&3, proposal.blake2_256().into(), false));
assert_eq!(CouncilVoting::tally(&proposal.blake2_256().into()), (2, 1, 0));
CouncilVoting::end_block(System::block_number());
assert_ok!(CouncilVoting::end_block(System::block_number()));
System::set_block_number(2);
CouncilVoting::end_block(System::block_number());
assert_ok!(CouncilVoting::end_block(System::block_number()));
assert_eq!(CouncilVoting::proposals().len(), 0);
assert_eq!(Democracy::active_referendums(), vec![(0, 5, proposal, VoteThreshold::SimpleMajority)]);
});
@@ -448,7 +461,7 @@ mod tests {
with_externalities(&mut new_test_ext(true), || {
System::set_block_number(1);
let proposal = bonding_duration_proposal(42);
assert_noop!{CouncilVoting::propose(&4, Box::new(proposal))};
assert_noop!(CouncilVoting::propose(&4, Box::new(proposal)), "proposer would not be on council");
});
}
}
@@ -39,8 +39,10 @@ extern crate substrate_runtime_staking as staking;
extern crate substrate_runtime_system as system;
use rstd::prelude::*;
use rstd::result;
use primitives::traits::{Zero, Executable, RefInto, As};
use runtime_support::{StorageValue, StorageMap, Parameter, Dispatchable, IsSubType};
use runtime_support::dispatch::Result;
mod vote_threshold;
pub use vote_threshold::{Approved, VoteThreshold};
@@ -57,13 +59,13 @@ pub trait Trait: staking::Trait + Sized {
decl_module! {
pub struct Module<T: Trait>;
pub enum Call where aux: T::PublicAux {
fn propose(aux, proposal: Box<T::Proposal>, value: T::Balance) = 0;
fn second(aux, proposal: PropIndex) = 1;
fn vote(aux, ref_index: ReferendumIndex, approve_proposal: bool) = 2;
fn propose(aux, proposal: Box<T::Proposal>, value: T::Balance) -> Result = 0;
fn second(aux, proposal: PropIndex) -> Result = 1;
fn vote(aux, ref_index: ReferendumIndex, approve_proposal: bool) -> Result = 2;
}
pub enum PrivCall {
fn start_referendum(proposal: Box<T::Proposal>, vote_threshold: VoteThreshold) = 0;
fn cancel_referendum(ref_index: ReferendumIndex) = 1;
fn start_referendum(proposal: Box<T::Proposal>, vote_threshold: VoteThreshold) -> Result = 0;
fn cancel_referendum(ref_index: ReferendumIndex) -> Result = 1;
}
}
@@ -143,9 +145,10 @@ impl<T: Trait> Module<T> {
// dispatching.
/// Propose a sensitive action to be taken.
fn propose(aux: &T::PublicAux, proposal: Box<T::Proposal>, value: T::Balance) {
ensure!(value >= Self::minimum_deposit());
ensure!(<staking::Module<T>>::deduct_unbonded(aux.ref_into(), value));
fn propose(aux: &T::PublicAux, proposal: Box<T::Proposal>, value: T::Balance) -> Result {
ensure!(value >= Self::minimum_deposit(), "value too low");
<staking::Module<T>>::deduct_unbonded(aux.ref_into(), value)
.map_err(|_| "proposer's balance too low")?;
let index = Self::public_prop_count();
<PublicPropCount<T>>::put(index + 1);
@@ -154,51 +157,55 @@ impl<T: Trait> Module<T> {
let mut props = Self::public_props();
props.push((index, (*proposal).clone(), aux.ref_into().clone()));
<PublicProps<T>>::put(props);
Ok(())
}
/// Propose a sensitive action to be taken.
fn second(aux: &T::PublicAux, proposal: PropIndex) {
if let Some(mut deposit) = Self::deposit_of(proposal) {
ensure!(<staking::Module<T>>::deduct_unbonded(aux.ref_into(), deposit.0));
deposit.1.push(aux.ref_into().clone());
<DepositOf<T>>::insert(proposal, deposit);
} else {
fail!("can only second an existing proposal");
}
fn second(aux: &T::PublicAux, proposal: PropIndex) -> Result {
let mut deposit = Self::deposit_of(proposal)
.ok_or("can only second an existing proposal")?;
<staking::Module<T>>::deduct_unbonded(aux.ref_into(), deposit.0)
.map_err(|_| "seconder's balance too low")?;
deposit.1.push(aux.ref_into().clone());
<DepositOf<T>>::insert(proposal, deposit);
Ok(())
}
/// 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..
fn vote(aux: &T::PublicAux, ref_index: ReferendumIndex, approve_proposal: bool) {
if !Self::is_active_referendum(ref_index) {
fail!("vote given for invalid referendum.")
}
if <staking::Module<T>>::balance(aux.ref_into()).is_zero() {
fail!("transactor must have balance to signal approval.");
}
fn vote(aux: &T::PublicAux, ref_index: ReferendumIndex, approve_proposal: bool) -> Result {
ensure!(Self::is_active_referendum(ref_index), "vote given for invalid referendum.");
ensure!(!<staking::Module<T>>::balance(aux.ref_into()).is_zero(),
"transactor must have balance to signal approval.");
if !<VoteOf<T>>::exists(&(ref_index, aux.ref_into().clone())) {
let mut voters = Self::voters_for(ref_index);
voters.push(aux.ref_into().clone());
<VotersFor<T>>::insert(ref_index, voters);
}
<VoteOf<T>>::insert(&(ref_index, aux.ref_into().clone()), approve_proposal);
Ok(())
}
/// Start a referendum.
fn start_referendum(proposal: Box<T::Proposal>, vote_threshold: VoteThreshold) {
let _ = Self::inject_referendum(<system::Module<T>>::block_number() + Self::voting_period(), *proposal, vote_threshold);
fn start_referendum(proposal: Box<T::Proposal>, vote_threshold: VoteThreshold) -> Result {
Self::inject_referendum(
<system::Module<T>>::block_number() + Self::voting_period(),
*proposal,
vote_threshold
).map(|_| ())
}
/// Remove a referendum.
fn cancel_referendum(ref_index: ReferendumIndex) {
fn cancel_referendum(ref_index: ReferendumIndex) -> Result {
Self::clear_referendum(ref_index);
Ok(())
}
// exposed mutables.
/// Start a referendum. Can be called directly by the council.
pub fn internal_start_referendum(proposal: T::Proposal, vote_threshold: VoteThreshold) {
let _ = <Module<T>>::inject_referendum(<system::Module<T>>::block_number() + <Module<T>>::voting_period(), proposal, vote_threshold);
pub fn internal_start_referendum(proposal: T::Proposal, vote_threshold: VoteThreshold) -> result::Result<ReferendumIndex, &'static str> {
<Module<T>>::inject_referendum(<system::Module<T>>::block_number() + <Module<T>>::voting_period(), proposal, vote_threshold)
}
/// Remove a referendum. Can be called directly by the council.
@@ -213,7 +220,7 @@ impl<T: Trait> Module<T> {
end: T::BlockNumber,
proposal: T::Proposal,
vote_threshold: VoteThreshold
) -> Result<ReferendumIndex, &'static str> {
) -> result::Result<ReferendumIndex, &'static str> {
let ref_index = Self::referendum_count();
if ref_index > 0 && Self::referendum_info(ref_index - 1).map(|i| i.0 > end).unwrap_or(false) {
Err("Cannot inject a referendum that ends earlier than preceeding referendum")?
@@ -234,7 +241,7 @@ impl<T: Trait> Module<T> {
}
/// Current era is ending; we should finish up any proposals.
fn end_block(now: T::BlockNumber) -> Result<(), &'static str> {
fn end_block(now: T::BlockNumber) -> Result {
// pick out another public referendum if it's time.
if (now % Self::launch_period()).is_zero() {
let mut public_props = Self::public_props();
@@ -251,7 +258,7 @@ impl<T: Trait> Module<T> {
<PublicProps<T>>::put(public_props);
Self::inject_referendum(now + Self::voting_period(), proposal, VoteThreshold::SuperMajorityApprove)?;
} else {
Err("depositors always exist for current proposals")?
return Err("depositors always exist for current proposals")
}
}
}
@@ -262,7 +269,7 @@ impl<T: Trait> Module<T> {
let total_stake = <staking::Module<T>>::total_stake();
Self::clear_referendum(index);
if vote_threshold.approved(approve, against, total_stake) {
proposal.dispatch();
proposal.dispatch()?;
}
<NextTally<T>>::put(index + 1);
}
@@ -423,17 +430,17 @@ mod tests {
});
}
fn propose_sessions_per_era(who: u64, value: u64, locked: u64) {
Democracy::propose(&who, Box::new(Proposal::Staking(staking::PrivCall::set_sessions_per_era(value))), locked);
fn propose_sessions_per_era(who: u64, value: u64, locked: u64) -> super::Result {
Democracy::propose(&who, Box::new(Proposal::Staking(staking::PrivCall::set_sessions_per_era(value))), locked)
}
#[test]
fn locked_for_should_work() {
with_externalities(&mut new_test_ext(), || {
System::set_block_number(1);
propose_sessions_per_era(1, 2, 2);
propose_sessions_per_era(1, 4, 4);
propose_sessions_per_era(1, 3, 3);
assert_ok!(propose_sessions_per_era(1, 2, 2));
assert_ok!(propose_sessions_per_era(1, 4, 4));
assert_ok!(propose_sessions_per_era(1, 3, 3));
assert_eq!(Democracy::locked_for(0), Some(2));
assert_eq!(Democracy::locked_for(1), Some(4));
assert_eq!(Democracy::locked_for(2), Some(3));
@@ -444,12 +451,12 @@ mod tests {
fn single_proposal_should_work() {
with_externalities(&mut new_test_ext(), || {
System::set_block_number(1);
propose_sessions_per_era(1, 2, 1);
assert_ok!(propose_sessions_per_era(1, 2, 1));
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
System::set_block_number(2);
let r = 0;
Democracy::vote(&1, r, true);
assert_ok!(Democracy::vote(&1, r, true));
assert_eq!(Democracy::referendum_count(), 1);
assert_eq!(Democracy::voters_for(r), vec![1]);
@@ -467,11 +474,11 @@ mod tests {
fn deposit_for_proposals_should_be_taken() {
with_externalities(&mut new_test_ext(), || {
System::set_block_number(1);
propose_sessions_per_era(1, 2, 5);
Democracy::second(&2, 0);
Democracy::second(&5, 0);
Democracy::second(&5, 0);
Democracy::second(&5, 0);
assert_ok!(propose_sessions_per_era(1, 2, 5));
assert_ok!(Democracy::second(&2, 0));
assert_ok!(Democracy::second(&5, 0));
assert_ok!(Democracy::second(&5, 0));
assert_ok!(Democracy::second(&5, 0));
assert_eq!(Staking::balance(&1), 5);
assert_eq!(Staking::balance(&2), 15);
assert_eq!(Staking::balance(&5), 35);
@@ -482,11 +489,11 @@ mod tests {
fn deposit_for_proposals_should_be_returned() {
with_externalities(&mut new_test_ext(), || {
System::set_block_number(1);
propose_sessions_per_era(1, 2, 5);
Democracy::second(&2, 0);
Democracy::second(&5, 0);
Democracy::second(&5, 0);
Democracy::second(&5, 0);
assert_ok!(propose_sessions_per_era(1, 2, 5));
assert_ok!(Democracy::second(&2, 0));
assert_ok!(Democracy::second(&5, 0));
assert_ok!(Democracy::second(&5, 0));
assert_ok!(Democracy::second(&5, 0));
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
assert_eq!(Staking::balance(&1), 10);
assert_eq!(Staking::balance(&2), 20);
@@ -495,60 +502,57 @@ mod tests {
}
#[test]
fn proposal_with_deposit_below_minimum_should_panic() {
fn proposal_with_deposit_below_minimum_should_not_work() {
with_externalities(&mut new_test_ext(), || {
System::set_block_number(1);
propose_sessions_per_era(1, 2, 0);
assert_eq!(Democracy::locked_for(0), None);
assert_noop!(propose_sessions_per_era(1, 2, 0), "value too low");
});
}
#[test]
fn poor_proposer_should_panic() {
fn poor_proposer_should_not_work() {
with_externalities(&mut new_test_ext(), || {
System::set_block_number(1);
propose_sessions_per_era(1, 2, 11);
assert_eq!(Democracy::locked_for(0), None);
assert_noop!(propose_sessions_per_era(1, 2, 11), "proposer\'s balance too low");
});
}
#[test]
fn poor_seconder_should_panic() {
fn poor_seconder_should_not_work() {
with_externalities(&mut new_test_ext(), || {
System::set_block_number(1);
propose_sessions_per_era(2, 2, 11);
Democracy::second(&1, 0);
assert_eq!(Democracy::locked_for(0), Some(11));
assert_ok!(propose_sessions_per_era(2, 2, 11));
assert_noop!(Democracy::second(&1, 0), "seconder\'s balance too low");
});
}
fn propose_bonding_duration(who: u64, value: u64, locked: u64) {
Democracy::propose(&who, Box::new(Proposal::Staking(staking::PrivCall::set_bonding_duration(value))), locked);
fn propose_bonding_duration(who: u64, value: u64, locked: u64) -> super::Result {
Democracy::propose(&who, Box::new(Proposal::Staking(staking::PrivCall::set_bonding_duration(value))), locked)
}
#[test]
fn runners_up_should_come_after() {
with_externalities(&mut new_test_ext(), || {
System::set_block_number(0);
propose_bonding_duration(1, 2, 2);
propose_bonding_duration(1, 4, 4);
propose_bonding_duration(1, 3, 3);
assert_ok!(propose_bonding_duration(1, 2, 2));
assert_ok!(propose_bonding_duration(1, 4, 4));
assert_ok!(propose_bonding_duration(1, 3, 3));
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
System::set_block_number(1);
Democracy::vote(&1, 0, true);
assert_ok!(Democracy::vote(&1, 0, true));
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
Staking::check_new_era();
assert_eq!(Staking::bonding_duration(), 4);
System::set_block_number(2);
Democracy::vote(&1, 1, true);
assert_ok!(Democracy::vote(&1, 1, true));
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
Staking::check_new_era();
assert_eq!(Staking::bonding_duration(), 3);
System::set_block_number(3);
Democracy::vote(&1, 2, true);
assert_ok!(Democracy::vote(&1, 2, true));
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
Staking::check_new_era();
assert_eq!(Staking::bonding_duration(), 2);
@@ -564,7 +568,7 @@ mod tests {
with_externalities(&mut new_test_ext(), || {
System::set_block_number(1);
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
Democracy::vote(&1, r, true);
assert_ok!(Democracy::vote(&1, r, true));
assert_eq!(Democracy::voters_for(r), vec![1]);
assert_eq!(Democracy::vote_of((r, 1)), Some(true));
@@ -582,8 +586,8 @@ mod tests {
with_externalities(&mut new_test_ext(), || {
System::set_block_number(1);
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
Democracy::vote(&1, r, true);
Democracy::cancel_referendum(r);
assert_ok!(Democracy::vote(&1, r, true));
assert_ok!(Democracy::cancel_referendum(r));
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
Staking::check_new_era();
@@ -597,7 +601,7 @@ mod tests {
with_externalities(&mut new_test_ext(), || {
System::set_block_number(1);
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
Democracy::vote(&1, r, false);
assert_ok!(Democracy::vote(&1, r, false));
assert_eq!(Democracy::voters_for(r), vec![1]);
assert_eq!(Democracy::vote_of((r, 1)), Some(false));
@@ -615,12 +619,12 @@ mod tests {
with_externalities(&mut new_test_ext(), || {
System::set_block_number(1);
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
Democracy::vote(&1, r, true);
Democracy::vote(&2, r, false);
Democracy::vote(&3, r, false);
Democracy::vote(&4, r, true);
Democracy::vote(&5, r, false);
Democracy::vote(&6, r, true);
assert_ok!(Democracy::vote(&1, r, true));
assert_ok!(Democracy::vote(&2, r, false));
assert_ok!(Democracy::vote(&3, r, false));
assert_ok!(Democracy::vote(&4, r, true));
assert_ok!(Democracy::vote(&5, r, false));
assert_ok!(Democracy::vote(&6, r, true));
assert_eq!(Democracy::tally(r), (110, 100));
@@ -636,8 +640,8 @@ mod tests {
with_externalities(&mut new_test_ext(), || {
System::set_block_number(1);
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
Democracy::vote(&5, r, false);
Democracy::vote(&6, r, true);
assert_ok!(Democracy::vote(&5, r, false));
assert_ok!(Democracy::vote(&6, r, true));
assert_eq!(Democracy::tally(r), (60, 50));
@@ -656,9 +660,9 @@ mod tests {
System::set_block_number(1);
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
Democracy::vote(&4, r, true);
Democracy::vote(&5, r, false);
Democracy::vote(&6, r, true);
assert_ok!(Democracy::vote(&4, r, true));
assert_ok!(Democracy::vote(&5, r, false));
assert_ok!(Democracy::vote(&6, r, true));
assert_eq!(Democracy::tally(r), (100, 50));
+15 -11
View File
@@ -43,6 +43,7 @@ extern crate substrate_runtime_system as system;
use rstd::prelude::*;
use primitives::traits::{Zero, One, RefInto, Executable, Convert};
use runtime_support::{StorageValue, StorageMap};
use runtime_support::dispatch::Result;
pub trait Trait: consensus::Trait {
type ConvertAccountIdToSessionKey: Convert<Self::AccountId, Self::SessionKey>;
@@ -51,11 +52,11 @@ pub trait Trait: consensus::Trait {
decl_module! {
pub struct Module<T: Trait>;
pub enum Call where aux: T::PublicAux {
fn set_key(aux, key: T::SessionKey) = 0;
fn set_key(aux, key: T::SessionKey) -> Result = 0;
}
pub enum PrivCall {
fn set_length(new: T::BlockNumber) = 0;
fn force_new_session() = 1;
fn set_length(new: T::BlockNumber) -> Result = 0;
fn force_new_session() -> Result = 1;
}
}
decl_storage! {
@@ -89,19 +90,22 @@ impl<T: Trait> Module<T> {
/// Sets the session key of `_validator` to `_key`. This doesn't take effect until the next
/// session.
fn set_key(aux: &T::PublicAux, key: T::SessionKey) {
fn set_key(aux: &T::PublicAux, key: T::SessionKey) -> Result {
// set new value for next session
<NextKeyFor<T>>::insert(aux.ref_into(), key)
<NextKeyFor<T>>::insert(aux.ref_into(), key);
Ok(())
}
/// Set a new era length. Won't kick in until the next era change (at current length).
fn set_length(new: T::BlockNumber) {
fn set_length(new: T::BlockNumber) -> Result {
<NextSessionLength<T>>::put(new);
Ok(())
}
/// Forces a new session.
fn force_new_session() {
fn force_new_session() -> Result {
Self::rotate_session();
Ok(())
}
// INTERNAL API (available to other runtime modules)
@@ -248,14 +252,14 @@ mod tests {
with_externalities(&mut new_test_ext(), || {
// Block 1: Change to length 3; no visible change.
System::set_block_number(1);
Session::set_length(3);
assert_ok!(Session::set_length(3));
Session::check_rotate_session();
assert_eq!(Session::length(), 2);
assert_eq!(Session::current_index(), 0);
// Block 2: Length now changed to 3. Index incremented.
System::set_block_number(2);
Session::set_length(3);
assert_ok!(Session::set_length(3));
Session::check_rotate_session();
assert_eq!(Session::length(), 3);
assert_eq!(Session::current_index(), 1);
@@ -268,7 +272,7 @@ mod tests {
// Block 4: Change to length 2; no visible change.
System::set_block_number(4);
Session::set_length(2);
assert_ok!(Session::set_length(2));
Session::check_rotate_session();
assert_eq!(Session::length(), 3);
assert_eq!(Session::current_index(), 1);
@@ -308,7 +312,7 @@ mod tests {
// Block 3: Set new key for validator 2; no visible change.
System::set_block_number(3);
Session::set_key(&2, 5);
assert_ok!(Session::set_key(&2, 5));
assert_eq!(Consensus::authorities(), vec![1, 2, 3]);
Session::check_rotate_session();
@@ -551,7 +551,7 @@ mod tests {
<CodeOf<Test>>::insert(1, code_transfer.to_vec());
Staking::transfer(&0, 1, 11);
assert_ok!(Staking::transfer(&0, 1, 11));
assert_eq!(Staking::balance(&0), 100);
assert_eq!(Staking::balance(&1), 5);
@@ -616,7 +616,7 @@ r#"
<CodeOf<Test>>::insert(1, code_create.to_vec());
// When invoked, the contract at address `1` must create a contract with 'transfer' code.
Staking::transfer(&0, 1, 11);
assert_ok!(Staking::transfer(&0, 1, 11));
let derived_address =
<Test as Trait>::DetermineContractAddress::contract_address_for(&code_transfer, &1);
@@ -674,8 +674,8 @@ r#"
<FreeBalance<Test>>::insert(1, 0);
<CodeOf<Test>>::insert(1, code_adder);
Staking::transfer(&0, 1, 1);
Staking::transfer(&0, 1, 1);
assert_ok!(Staking::transfer(&0, 1, 1));
assert_ok!(Staking::transfer(&0, 1, 1));
let storage_addr = [0x01u8; 32];
let value =
@@ -735,7 +735,7 @@ r#"
// Transfer some balance from 0 to 1. This will trigger execution
// of the smart-contract code at address 1.
Staking::transfer(&0, 1, 11);
assert_ok!(Staking::transfer(&0, 1, 11));
// The balance should remain unchanged since we are expecting
// out-of-gas error which will revert transfer.
@@ -767,7 +767,7 @@ r#"
<CodeOf<Test>>::insert(1, code_mem.to_vec());
// Transfer some balance from 0 to 1.
Staking::transfer(&0, 1, 11);
assert_ok!(Staking::transfer(&0, 1, 11));
// The balance should remain unchanged since we are expecting
// validation error caused by internal memory declaration.
+87 -74
View File
@@ -47,11 +47,12 @@ extern crate parity_wasm;
#[cfg(test)] use std::fmt::Debug;
use rstd::prelude::*;
use rstd::cmp;
use rstd::{cmp, result};
use rstd::cell::RefCell;
use rstd::collections::btree_map::{BTreeMap, Entry};
use codec::Slicable;
use runtime_support::{StorageValue, StorageMap, Parameter};
use runtime_support::dispatch::Result;
use primitives::traits::{Zero, One, Bounded, RefInto, SimpleArithmetic, Executable, MakePayment, As};
mod contract;
@@ -99,15 +100,15 @@ pub trait Trait: system::Trait + session::Trait {
decl_module! {
pub struct Module<T: Trait>;
pub enum Call where aux: T::PublicAux {
fn transfer(aux, dest: T::AccountId, value: T::Balance) = 0;
fn stake(aux) = 1;
fn unstake(aux) = 2;
fn transfer(aux, dest: T::AccountId, value: T::Balance) -> Result = 0;
fn stake(aux) -> Result = 1;
fn unstake(aux) -> Result = 2;
}
pub enum PrivCall {
fn set_sessions_per_era(new: T::BlockNumber) = 0;
fn set_bonding_duration(new: T::BlockNumber) = 1;
fn set_validator_count(new: u32) = 2;
fn force_new_era() = 3;
fn set_sessions_per_era(new: T::BlockNumber) -> Result = 0;
fn set_bonding_duration(new: T::BlockNumber) -> Result = 1;
fn set_validator_count(new: u32) -> Result = 2;
fn force_new_era() -> Result = 3;
}
}
@@ -193,85 +194,90 @@ impl<T: Trait> Module<T> {
}
/// Create a smart-contract account.
pub fn create(aux: &T::PublicAux, code: &[u8], value: T::Balance) {
pub fn create(aux: &T::PublicAux, code: &[u8], value: T::Balance) -> Result {
// commit anything that made it this far to storage
if let Ok(Some(commit)) = Self::effect_create(aux.ref_into(), code, value, &DirectAccountDb) {
if let Some(commit) = Self::effect_create(aux.ref_into(), code, value, &DirectAccountDb)? {
<AccountDb<T>>::merge(&mut DirectAccountDb, commit);
}
Ok(())
}
// PUBLIC DISPATCH
/// Transfer some unlocked staking balance to another staker.
/// TODO: probably want to state gas-limit and gas-price.
fn transfer(aux: &T::PublicAux, dest: T::AccountId, value: T::Balance) {
fn transfer(aux: &T::PublicAux, dest: T::AccountId, value: T::Balance) -> Result {
// commit anything that made it this far to storage
if let Ok(Some(commit)) = Self::effect_transfer(aux.ref_into(), &dest, value, &DirectAccountDb) {
if let Some(commit) = Self::effect_transfer(aux.ref_into(), &dest, value, &DirectAccountDb)? {
<AccountDb<T>>::merge(&mut DirectAccountDb, commit);
}
Ok(())
}
/// Declare the desire to stake for the transactor.
///
/// Effects will be felt at the beginning of the next era.
fn stake(aux: &T::PublicAux) {
fn stake(aux: &T::PublicAux) -> Result {
let mut intentions = <Intentions<T>>::get();
// can't be in the list twice.
ensure!(intentions.iter().find(|&t| t == aux.ref_into()).is_none(), "Cannot stake if already staked.");
intentions.push(aux.ref_into().clone());
<Intentions<T>>::put(intentions);
<Bondage<T>>::insert(aux.ref_into(), T::BlockNumber::max_value());
Ok(())
}
/// Retract the desire to stake for the transactor.
///
/// Effects will be felt at the beginning of the next era.
fn unstake(aux: &T::PublicAux) {
fn unstake(aux: &T::PublicAux) -> Result {
let mut intentions = <Intentions<T>>::get();
if let Some(position) = intentions.iter().position(|t| t == aux.ref_into()) {
intentions.swap_remove(position);
<Intentions<T>>::put(intentions);
<Bondage<T>>::insert(aux.ref_into(), Self::current_era() + Self::bonding_duration());
} else {
fail!("Cannot unstake if not already staked.");
}
let position = intentions.iter().position(|t| t == aux.ref_into()).ok_or("Cannot unstake if not already staked.")?;
intentions.swap_remove(position);
<Intentions<T>>::put(intentions);
<Bondage<T>>::insert(aux.ref_into(), Self::current_era() + Self::bonding_duration());
Ok(())
}
// PRIV DISPATCH
/// Set the number of sessions in an era.
fn set_sessions_per_era(new: T::BlockNumber) {
fn set_sessions_per_era(new: T::BlockNumber) -> Result {
<NextSessionsPerEra<T>>::put(&new);
Ok(())
}
/// The length of the bonding duration in eras.
fn set_bonding_duration(new: T::BlockNumber) {
fn set_bonding_duration(new: T::BlockNumber) -> Result {
<BondingDuration<T>>::put(&new);
Ok(())
}
/// The length of a staking era in sessions.
fn set_validator_count(new: u32) {
fn set_validator_count(new: u32) -> Result {
<ValidatorCount<T>>::put(&new);
Ok(())
}
/// Force there to be a new era. This also forces a new session immediately after.
fn force_new_era() {
fn force_new_era() -> Result {
Self::new_era();
<session::Module<T>>::rotate_session();
Ok(())
}
// PUBLIC MUTABLES (DANGEROUS)
/// Deduct from an unbonded balance. true if it happened.
pub fn deduct_unbonded(who: &T::AccountId, value: T::Balance) -> bool {
pub fn deduct_unbonded(who: &T::AccountId, value: T::Balance) -> Result {
if let LockStatus::Liquid = Self::unlock_block(who) {
let b = Self::free_balance(who);
if b >= value {
<FreeBalance<T>>::insert(who, b - value);
return true;
return Ok(())
}
}
false
Err("not enough liquid funds")
}
/// Refund some balance.
@@ -280,26 +286,27 @@ impl<T: Trait> Module<T> {
}
/// Will slash any balance, but prefer free over reserved.
pub fn slash(who: &T::AccountId, value: T::Balance) -> bool {
pub fn slash(who: &T::AccountId, value: T::Balance) -> Result {
let free_balance = Self::free_balance(who);
let free_slash = cmp::min(free_balance, value);
<FreeBalance<T>>::insert(who, &(free_balance - free_slash));
if free_slash < value {
Self::slash_reserved(who, value - free_slash)
.map_err(|_| "not enough funds")
} else {
true
Ok(())
}
}
/// Moves `value` from balance to reserved balance.
pub fn reserve_balance(who: &T::AccountId, value: T::Balance) -> bool {
pub fn reserve_balance(who: &T::AccountId, value: T::Balance) -> Result {
let b = Self::free_balance(who);
if b < value {
return false;
return Err("not enough free funds")
}
<FreeBalance<T>>::insert(who, b - value);
<ReservedBalance<T>>::insert(who, Self::reserved_balance(who) + value);
true
Ok(())
}
/// Moves `value` from reserved balance to balance.
@@ -311,20 +318,28 @@ impl<T: Trait> Module<T> {
}
/// Moves `value` from reserved balance to balance.
pub fn slash_reserved(who: &T::AccountId, value: T::Balance) -> bool {
pub fn slash_reserved(who: &T::AccountId, value: T::Balance) -> Result {
let b = Self::reserved_balance(who);
let slash = cmp::min(b, value);
<ReservedBalance<T>>::insert(who, b - slash);
value == slash
if value == slash {
Ok(())
} else {
Err("not enough (reserved) funds")
}
}
/// Moves `value` from reserved balance to balance.
pub fn transfer_reserved_balance(slashed: &T::AccountId, beneficiary: &T::AccountId, value: T::Balance) -> bool {
pub fn transfer_reserved_balance(slashed: &T::AccountId, beneficiary: &T::AccountId, value: T::Balance) -> Result {
let b = Self::reserved_balance(slashed);
let slash = cmp::min(b, value);
<ReservedBalance<T>>::insert(slashed, b - slash);
<FreeBalance<T>>::insert(beneficiary, Self::free_balance(beneficiary) + slash);
slash == value
if value == slash {
Ok(())
} else {
Err("not enough (reserved) funds")
}
}
/// Hook to be called after to transaction processing.
@@ -551,7 +566,7 @@ impl<T: Trait> Module<T> {
code: &[u8],
value: T::Balance,
account_db: &DB,
) -> Result<Option<State<T>>, &'static str> {
) -> result::Result<Option<State<T>>, &'static str> {
let from_balance = account_db.get_balance(transactor);
// TODO: a fee.
if from_balance < value {
@@ -580,7 +595,7 @@ impl<T: Trait> Module<T> {
dest: &T::AccountId,
value: T::Balance,
account_db: &DB,
) -> Result<Option<State<T>>, &'static str> {
) -> result::Result<Option<State<T>>, &'static str> {
let from_balance = account_db.get_balance(transactor);
if from_balance < value {
return Err("balance too low to send value");
@@ -756,9 +771,9 @@ mod tests {
// Block 1: Add three validators. No obvious change.
System::set_block_number(1);
Staking::stake(&1);
Staking::stake(&2);
Staking::stake(&4);
assert_ok!(Staking::stake(&1));
assert_ok!(Staking::stake(&2));
assert_ok!(Staking::stake(&4));
Staking::check_new_era();
assert_eq!(Session::validators(), vec![10, 20]);
@@ -769,8 +784,8 @@ mod tests {
// Block 3: Unstake highest, introduce another staker. No change yet.
System::set_block_number(3);
Staking::stake(&3);
Staking::unstake(&4);
assert_ok!(Staking::stake(&3));
assert_ok!(Staking::unstake(&4));
Staking::check_new_era();
// Block 4: New era - validators change.
@@ -780,7 +795,7 @@ mod tests {
// Block 5: Transfer stake from highest to lowest. No change yet.
System::set_block_number(5);
Staking::transfer(&4, 1, 40);
assert_ok!(Staking::transfer(&4, 1, 40));
Staking::check_new_era();
// Block 6: Lowest now validator.
@@ -790,7 +805,7 @@ mod tests {
// Block 7: Unstake three. No change yet.
System::set_block_number(7);
Staking::unstake(&3);
assert_ok!(Staking::unstake(&3));
Staking::check_new_era();
assert_eq!(Session::validators(), vec![1, 3]);
@@ -825,7 +840,7 @@ mod tests {
// Block 3: Schedule an era length change; no visible changes.
System::set_block_number(3);
Staking::set_sessions_per_era(3);
assert_ok!(Staking::set_sessions_per_era(3));
Staking::check_new_era();
assert_eq!(Staking::sessions_per_era(), 2);
assert_eq!(Staking::last_era_length_change(), 0);
@@ -878,19 +893,18 @@ mod tests {
fn staking_balance_transfer_works() {
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
<FreeBalance<Test>>::insert(1, 111);
Staking::transfer(&1, 2, 69);
assert_ok!(Staking::transfer(&1, 2, 69));
assert_eq!(Staking::balance(&1), 42);
assert_eq!(Staking::balance(&2), 69);
});
}
#[test]
fn staking_balance_transfer_when_bonded_panics() {
fn staking_balance_transfer_when_bonded_should_not_work() {
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
<FreeBalance<Test>>::insert(1, 111);
Staking::stake(&1);
Staking::transfer(&1, 2, 69);
assert_eq!(Staking::balance(&1), 111);
assert_ok!(Staking::stake(&1));
assert_noop!(Staking::transfer(&1, 2, 69), "bondage too high to send value");
});
}
@@ -903,7 +917,7 @@ mod tests {
assert_eq!(Staking::free_balance(&1), 111);
assert_eq!(Staking::reserved_balance(&1), 0);
Staking::reserve_balance(&1, 69);
assert_ok!(Staking::reserve_balance(&1, 69));
assert_eq!(Staking::balance(&1), 111);
assert_eq!(Staking::free_balance(&1), 42);
@@ -911,13 +925,12 @@ mod tests {
});
}
#[should_panic]
fn staking_balance_transfer_when_reserved_panics() {
#[test]
fn staking_balance_transfer_when_reserved_should_not_work() {
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
<FreeBalance<Test>>::insert(1, 111);
Staking::reserve_balance(&1, 69);
Staking::transfer(&1, 2, 69);
assert_eq!(Staking::balance(&1), 111);
assert_ok!(Staking::reserve_balance(&1, 69));
assert_noop!(Staking::transfer(&1, 2, 69), "balance too low to send value");
});
}
@@ -925,19 +938,19 @@ mod tests {
fn deducting_balance_should_work() {
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
<FreeBalance<Test>>::insert(1, 111);
assert!(Staking::deduct_unbonded(&1, 69));
assert_ok!(Staking::deduct_unbonded(&1, 69));
assert_eq!(Staking::free_balance(&1), 42);
});
}
#[test]
fn deducting_balance_should_fail_when_bonded() {
fn deducting_balance_when_bonded_should_not_work() {
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
<FreeBalance<Test>>::insert(1, 111);
<Bondage<Test>>::insert(1, 2);
System::set_block_number(1);
assert_eq!(Staking::unlock_block(&1), LockStatus::LockedUntil(2));
assert!(!Staking::deduct_unbonded(&1, 69));
assert_noop!(Staking::deduct_unbonded(&1, 69), "not enough liquid funds");
});
}
@@ -954,8 +967,8 @@ mod tests {
fn slashing_balance_should_work() {
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
<FreeBalance<Test>>::insert(1, 111);
Staking::reserve_balance(&1, 69);
assert!(Staking::slash(&1, 69));
assert_ok!(Staking::reserve_balance(&1, 69));
assert_ok!(Staking::slash(&1, 69));
assert_eq!(Staking::free_balance(&1), 0);
assert_eq!(Staking::reserved_balance(&1), 42);
});
@@ -965,8 +978,8 @@ mod tests {
fn slashing_incomplete_balance_should_work() {
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
<FreeBalance<Test>>::insert(1, 42);
Staking::reserve_balance(&1, 21);
assert!(!Staking::slash(&1, 69));
assert_ok!(Staking::reserve_balance(&1, 21));
assert_err!(Staking::slash(&1, 69), "not enough funds");
assert_eq!(Staking::free_balance(&1), 0);
assert_eq!(Staking::reserved_balance(&1), 0);
});
@@ -976,7 +989,7 @@ mod tests {
fn unreserving_balance_should_work() {
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
<FreeBalance<Test>>::insert(1, 111);
Staking::reserve_balance(&1, 111);
assert_ok!(Staking::reserve_balance(&1, 111));
Staking::unreserve_balance(&1, 42);
assert_eq!(Staking::reserved_balance(&1), 69);
assert_eq!(Staking::free_balance(&1), 42);
@@ -987,8 +1000,8 @@ mod tests {
fn slashing_reserved_balance_should_work() {
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
<FreeBalance<Test>>::insert(1, 111);
Staking::reserve_balance(&1, 111);
assert!(Staking::slash_reserved(&1, 42));
assert_ok!(Staking::reserve_balance(&1, 111));
assert_ok!(Staking::slash_reserved(&1, 42));
assert_eq!(Staking::reserved_balance(&1), 69);
assert_eq!(Staking::free_balance(&1), 0);
});
@@ -998,8 +1011,8 @@ mod tests {
fn slashing_incomplete_reserved_balance_should_work() {
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
<FreeBalance<Test>>::insert(1, 111);
Staking::reserve_balance(&1, 42);
assert!(!Staking::slash_reserved(&1, 69));
assert_ok!(Staking::reserve_balance(&1, 42));
assert_err!(Staking::slash_reserved(&1, 69), "not enough (reserved) funds");
assert_eq!(Staking::free_balance(&1), 69);
assert_eq!(Staking::reserved_balance(&1), 0);
});
@@ -1009,8 +1022,8 @@ mod tests {
fn transferring_reserved_balance_should_work() {
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
<FreeBalance<Test>>::insert(1, 111);
Staking::reserve_balance(&1, 111);
assert!(Staking::transfer_reserved_balance(&1, &2, 42));
assert_ok!(Staking::reserve_balance(&1, 111));
assert_ok!(Staking::transfer_reserved_balance(&1, &2, 42));
assert_eq!(Staking::reserved_balance(&1), 69);
assert_eq!(Staking::free_balance(&1), 0);
assert_eq!(Staking::reserved_balance(&2), 0);
@@ -1022,8 +1035,8 @@ mod tests {
fn transferring_incomplete_reserved_balance_should_work() {
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
<FreeBalance<Test>>::insert(1, 111);
Staking::reserve_balance(&1, 42);
assert!(!Staking::transfer_reserved_balance(&1, &2, 69));
assert_ok!(Staking::reserve_balance(&1, 42));
assert_err!(Staking::transfer_reserved_balance(&1, &2, 69), "not enough (reserved) funds");
assert_eq!(Staking::reserved_balance(&1), 0);
assert_eq!(Staking::free_balance(&1), 69);
assert_eq!(Staking::reserved_balance(&2), 0);
@@ -34,6 +34,7 @@ extern crate substrate_runtime_system as system;
extern crate substrate_codec as codec;
use runtime_support::{StorageValue, Parameter};
use runtime_support::dispatch::Result;
use runtime_primitives::traits::{HasPublicAux, Executable, MaybeEmpty};
pub trait Trait: HasPublicAux + system::Trait {
@@ -46,7 +47,7 @@ pub trait Trait: HasPublicAux + system::Trait {
decl_module! {
pub struct Module<T: Trait>;
pub enum Call where aux: T::PublicAux {
fn set(aux, now: T::Value) = 0;
fn set(aux, now: T::Value) -> Result = 0;
}
}
@@ -64,7 +65,7 @@ impl<T: Trait> Module<T> {
}
/// Set the current time.
fn set(aux: &T::PublicAux, now: T::Value) {
fn set(aux: &T::PublicAux, now: T::Value) -> Result {
assert!(aux.is_empty());
assert!(!<Self as Store>::DidUpdate::exists(), "Timestamp must be updated only once in the block");
assert!(
@@ -74,6 +75,7 @@ impl<T: Trait> Module<T> {
);
<Self as Store>::Now::put(now);
<Self as Store>::DidUpdate::put(true);
Ok(())
}
}
@@ -138,7 +140,7 @@ mod tests {
with_externalities(&mut t, || {
assert_eq!(<Timestamp as Store>::Now::get(), 42);
Timestamp::aux_dispatch(Call::set(69), &0);
assert_ok!(Timestamp::aux_dispatch(Call::set(69), &0));
assert_eq!(Timestamp::now(), 69);
});
}