mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-01 12:27:56 +00:00
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:
@@ -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(),
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user