mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 14:31:02 +00:00
Public extrinsic apply doesn't panic (#178)
* 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. * utilise hygene
This commit is contained in:
@@ -115,7 +115,8 @@ pub fn run<I, T>(args: I) -> error::Result<()> where
|
|||||||
staking: Some(StakingConfig {
|
staking: Some(StakingConfig {
|
||||||
current_era: 0,
|
current_era: 0,
|
||||||
intentions: vec![],
|
intentions: vec![],
|
||||||
transaction_fee: 100,
|
transaction_base_fee: 100,
|
||||||
|
transaction_byte_fee: 1,
|
||||||
balances: vec![(god_key.clone(), 1u64 << 63)].into_iter().collect(),
|
balances: vec![(god_key.clone(), 1u64 << 63)].into_iter().collect(),
|
||||||
validator_count: 12,
|
validator_count: 12,
|
||||||
sessions_per_era: 24, // 24 hours per era.
|
sessions_per_era: 24, // 24 hours per era.
|
||||||
|
|||||||
@@ -82,8 +82,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn panic_execution_with_foreign_code_gives_error() {
|
fn panic_execution_with_foreign_code_gives_error() {
|
||||||
let mut t: TestExternalities = map![
|
let mut t: TestExternalities = map![
|
||||||
twox_128(&<staking::FreeBalance<Concrete>>::key_for(*Alice)).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0],
|
twox_128(&<staking::FreeBalance<Concrete>>::key_for(*Alice)).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0],
|
||||||
twox_128(<staking::TransactionFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
twox_128(<staking::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![70u8; 8],
|
||||||
|
twox_128(<staking::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||||
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -96,8 +97,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn panic_execution_with_native_equivalent_code_gives_error() {
|
fn panic_execution_with_native_equivalent_code_gives_error() {
|
||||||
let mut t: TestExternalities = map![
|
let mut t: TestExternalities = map![
|
||||||
twox_128(&<staking::FreeBalance<Concrete>>::key_for(*Alice)).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0],
|
twox_128(&<staking::FreeBalance<Concrete>>::key_for(*Alice)).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0],
|
||||||
twox_128(<staking::TransactionFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
twox_128(<staking::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![70u8; 8],
|
||||||
|
twox_128(<staking::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||||
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -111,7 +113,8 @@ mod tests {
|
|||||||
fn successful_execution_with_native_equivalent_code_gives_ok() {
|
fn successful_execution_with_native_equivalent_code_gives_ok() {
|
||||||
let mut t: TestExternalities = map![
|
let mut t: TestExternalities = map![
|
||||||
twox_128(&<staking::FreeBalance<Concrete>>::key_for(*Alice)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
twox_128(&<staking::FreeBalance<Concrete>>::key_for(*Alice)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||||
twox_128(<staking::TransactionFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
twox_128(<staking::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||||
|
twox_128(<staking::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||||
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -130,7 +133,8 @@ mod tests {
|
|||||||
fn successful_execution_with_foreign_code_gives_ok() {
|
fn successful_execution_with_foreign_code_gives_ok() {
|
||||||
let mut t: TestExternalities = map![
|
let mut t: TestExternalities = map![
|
||||||
twox_128(&<staking::FreeBalance<Concrete>>::key_for(*Alice)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
twox_128(&<staking::FreeBalance<Concrete>>::key_for(*Alice)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||||
twox_128(<staking::TransactionFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
twox_128(<staking::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||||
|
twox_128(<staking::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||||
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -162,7 +166,8 @@ mod tests {
|
|||||||
intentions: vec![Alice.into(), Bob.into(), Charlie.into()],
|
intentions: vec![Alice.into(), Bob.into(), Charlie.into()],
|
||||||
validator_count: 3,
|
validator_count: 3,
|
||||||
bonding_duration: 0,
|
bonding_duration: 0,
|
||||||
transaction_fee: 1,
|
transaction_base_fee: 1,
|
||||||
|
transaction_byte_fee: 0,
|
||||||
}),
|
}),
|
||||||
democracy: Some(Default::default()),
|
democracy: Some(Default::default()),
|
||||||
council: Some(Default::default()),
|
council: Some(Default::default()),
|
||||||
@@ -197,7 +202,7 @@ mod tests {
|
|||||||
construct_block(
|
construct_block(
|
||||||
1,
|
1,
|
||||||
[69u8; 32].into(),
|
[69u8; 32].into(),
|
||||||
hex!("a63d59c6a7347cd7a1dc1ec139723b531f0ac450e39b1c532d5ca69ff74ad811").into(),
|
hex!("76b0393b4958d3cb98bb51d9f4edb316af48485142b8721e94c3b52c75ec3243").into(),
|
||||||
vec![Extrinsic {
|
vec![Extrinsic {
|
||||||
signed: Alice.into(),
|
signed: Alice.into(),
|
||||||
index: 0,
|
index: 0,
|
||||||
@@ -210,7 +215,7 @@ mod tests {
|
|||||||
construct_block(
|
construct_block(
|
||||||
2,
|
2,
|
||||||
block1().1,
|
block1().1,
|
||||||
hex!("1c3623b2e3f7e43752debb9015bace4f6931593579b5af34457b931315f5e2ab").into(),
|
hex!("8ae9828a5988459d35fb428086170dead660176ee0766e89bc1a4b48153d4e88").into(),
|
||||||
vec![
|
vec![
|
||||||
Extrinsic {
|
Extrinsic {
|
||||||
signed: Bob.into(),
|
signed: Bob.into(),
|
||||||
@@ -267,8 +272,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn panic_execution_gives_error() {
|
fn panic_execution_gives_error() {
|
||||||
let mut t: TestExternalities = map![
|
let mut t: TestExternalities = map![
|
||||||
twox_128(&<staking::FreeBalance<Concrete>>::key_for(*Alice)).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0],
|
twox_128(&<staking::FreeBalance<Concrete>>::key_for(*Alice)).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0],
|
||||||
twox_128(<staking::TransactionFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
twox_128(<staking::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![70u8; 8],
|
||||||
|
twox_128(<staking::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||||
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -283,7 +289,8 @@ mod tests {
|
|||||||
fn successful_execution_gives_ok() {
|
fn successful_execution_gives_ok() {
|
||||||
let mut t: TestExternalities = map![
|
let mut t: TestExternalities = map![
|
||||||
twox_128(&<staking::FreeBalance<Concrete>>::key_for(*Alice)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
twox_128(&<staking::FreeBalance<Concrete>>::key_for(*Alice)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||||
twox_128(<staking::TransactionFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
twox_128(<staking::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||||
|
twox_128(<staking::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||||
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
BIN
Binary file not shown.
Binary file not shown.
@@ -65,6 +65,6 @@ args:
|
|||||||
- chain:
|
- chain:
|
||||||
long: chain
|
long: chain
|
||||||
value_name: CHAIN_SPEC
|
value_name: CHAIN_SPEC
|
||||||
help: Specify the chain specification (one of dev, local or poc-1)
|
help: Specify the chain specification (one of dev, local or poc-2)
|
||||||
takes_value: true
|
takes_value: true
|
||||||
subcommands:
|
subcommands:
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ impl substrate_rpc::system::SystemApi for Configuration {
|
|||||||
Ok(match self.0.chain_spec {
|
Ok(match self.0.chain_spec {
|
||||||
ChainSpec::Development => "dev",
|
ChainSpec::Development => "dev",
|
||||||
ChainSpec::LocalTestnet => "local",
|
ChainSpec::LocalTestnet => "local",
|
||||||
ChainSpec::PoC1Testnet => "poc-1",
|
ChainSpec::PoC2Testnet => "poc-2",
|
||||||
}.into())
|
}.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,14 +174,14 @@ pub fn run<I, T>(args: I) -> error::Result<()> where
|
|||||||
match matches.value_of("chain") {
|
match matches.value_of("chain") {
|
||||||
Some("dev") => config.chain_spec = ChainSpec::Development,
|
Some("dev") => config.chain_spec = ChainSpec::Development,
|
||||||
Some("local") => config.chain_spec = ChainSpec::LocalTestnet,
|
Some("local") => config.chain_spec = ChainSpec::LocalTestnet,
|
||||||
Some("poc-1") => config.chain_spec = ChainSpec::PoC1Testnet,
|
Some("poc-2") => config.chain_spec = ChainSpec::PoC2Testnet,
|
||||||
None => (),
|
None => (),
|
||||||
Some(unknown) => panic!("Invalid chain name: {}", unknown),
|
Some(unknown) => panic!("Invalid chain name: {}", unknown),
|
||||||
}
|
}
|
||||||
info!("Chain specification: {}", match config.chain_spec {
|
info!("Chain specification: {}", match config.chain_spec {
|
||||||
ChainSpec::Development => "Development",
|
ChainSpec::Development => "Development",
|
||||||
ChainSpec::LocalTestnet => "Local Testnet",
|
ChainSpec::LocalTestnet => "Local Testnet",
|
||||||
ChainSpec::PoC1Testnet => "PoC-1 Testnet",
|
ChainSpec::PoC2Testnet => "PoC-2 Testnet",
|
||||||
});
|
});
|
||||||
|
|
||||||
config.roles = role;
|
config.roles = role;
|
||||||
|
|||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -28,8 +28,8 @@ pub enum ChainSpec {
|
|||||||
Development,
|
Development,
|
||||||
/// Whatever the current runtime is, with simple Alice/Bob auths.
|
/// Whatever the current runtime is, with simple Alice/Bob auths.
|
||||||
LocalTestnet,
|
LocalTestnet,
|
||||||
/// The PoC-1 testnet.
|
/// The PoC-2 testnet.
|
||||||
PoC1Testnet,
|
PoC2Testnet,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Service configuration.
|
/// Service configuration.
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ pub struct ChainConfig {
|
|||||||
boot_nodes: Vec<String>,
|
boot_nodes: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poc_1_testnet_config() -> ChainConfig {
|
fn poc_2_testnet_config() -> ChainConfig {
|
||||||
let initial_authorities = vec![
|
let initial_authorities = vec![
|
||||||
hex!["82c39b31a2b79a90f8e66e7a77fdb85a4ed5517f2ae39f6a80565e8ecae85cf5"].into(),
|
hex!["82c39b31a2b79a90f8e66e7a77fdb85a4ed5517f2ae39f6a80565e8ecae85cf5"].into(),
|
||||||
hex!["4de37a07567ebcbf8c64568428a835269a566723687058e017b6d69db00a77e7"].into(),
|
hex!["4de37a07567ebcbf8c64568428a835269a566723687058e017b6d69db00a77e7"].into(),
|
||||||
@@ -164,7 +164,8 @@ fn poc_1_testnet_config() -> ChainConfig {
|
|||||||
staking: Some(StakingConfig {
|
staking: Some(StakingConfig {
|
||||||
current_era: 0,
|
current_era: 0,
|
||||||
intentions: initial_authorities.clone(),
|
intentions: initial_authorities.clone(),
|
||||||
transaction_fee: 100,
|
transaction_base_fee: 100,
|
||||||
|
transaction_byte_fee: 1,
|
||||||
balances: endowed_accounts.iter().map(|&k|(k, 1u128 << 60)).collect(),
|
balances: endowed_accounts.iter().map(|&k|(k, 1u128 << 60)).collect(),
|
||||||
validator_count: 12,
|
validator_count: 12,
|
||||||
sessions_per_era: 24, // 24 hours per era.
|
sessions_per_era: 24, // 24 hours per era.
|
||||||
@@ -222,7 +223,8 @@ fn testnet_config(initial_authorities: Vec<AuthorityId>) -> ChainConfig {
|
|||||||
staking: Some(StakingConfig {
|
staking: Some(StakingConfig {
|
||||||
current_era: 0,
|
current_era: 0,
|
||||||
intentions: initial_authorities.clone(),
|
intentions: initial_authorities.clone(),
|
||||||
transaction_fee: 1,
|
transaction_base_fee: 1,
|
||||||
|
transaction_byte_fee: 0,
|
||||||
balances: endowed_accounts.iter().map(|&k|(k, (1u128 << 60))).collect(),
|
balances: endowed_accounts.iter().map(|&k|(k, (1u128 << 60))).collect(),
|
||||||
validator_count: 2,
|
validator_count: 2,
|
||||||
sessions_per_era: 5,
|
sessions_per_era: 5,
|
||||||
@@ -364,7 +366,7 @@ impl<B, E> Service<B, E>
|
|||||||
let ChainConfig { genesis_config, boot_nodes } = match config.chain_spec {
|
let ChainConfig { genesis_config, boot_nodes } = match config.chain_spec {
|
||||||
ChainSpec::Development => development_config(),
|
ChainSpec::Development => development_config(),
|
||||||
ChainSpec::LocalTestnet => local_testnet_config(),
|
ChainSpec::LocalTestnet => local_testnet_config(),
|
||||||
ChainSpec::PoC1Testnet => poc_1_testnet_config(),
|
ChainSpec::PoC2Testnet => poc_2_testnet_config(),
|
||||||
};
|
};
|
||||||
config.network.boot_nodes.extend(boot_nodes);
|
config.network.boot_nodes.extend(boot_nodes);
|
||||||
|
|
||||||
|
|||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -44,3 +44,62 @@ mod hashable;
|
|||||||
pub use self::storage::{StorageVec, StorageList, StorageValue, StorageMap};
|
pub use self::storage::{StorageVec, StorageList, StorageValue, StorageMap};
|
||||||
pub use self::hashable::Hashable;
|
pub use self::hashable::Hashable;
|
||||||
pub use self::dispatch::{Parameter, Dispatchable, Callable, AuxDispatchable, AuxCallable, IsSubType, IsAuxSubType};
|
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;
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! ensure {
|
||||||
|
( $x:expr, $y:expr ) => {{
|
||||||
|
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 )* ) => {
|
||||||
|
let h = runtime_io::storage_root();
|
||||||
|
{
|
||||||
|
$( $x )*
|
||||||
|
}
|
||||||
|
assert_eq!(h, runtime_io::storage_root());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -224,8 +224,8 @@ impl<T: Trait> Module<T> {
|
|||||||
/// Set candidate approvals. Approval slots stay valid as long as candidates in those slots
|
/// Set candidate approvals. Approval slots stay valid as long as candidates in those slots
|
||||||
/// are registered.
|
/// are registered.
|
||||||
fn set_approvals(aux: &T::PublicAux, votes: Vec<bool>, index: VoteIndex) {
|
fn set_approvals(aux: &T::PublicAux, votes: Vec<bool>, index: VoteIndex) {
|
||||||
assert!(!Self::presentation_active());
|
ensure!(!Self::presentation_active());
|
||||||
assert_eq!(index, Self::vote_index());
|
ensure!(index == Self::vote_index());
|
||||||
if !<LastActiveOf<T>>::exists(aux.ref_into()) {
|
if !<LastActiveOf<T>>::exists(aux.ref_into()) {
|
||||||
// not yet a voter - deduct bond.
|
// not yet a voter - deduct bond.
|
||||||
<staking::Module<T>>::reserve_balance(aux.ref_into(), Self::voting_bond());
|
<staking::Module<T>>::reserve_balance(aux.ref_into(), Self::voting_bond());
|
||||||
@@ -245,16 +245,16 @@ impl<T: Trait> Module<T> {
|
|||||||
///
|
///
|
||||||
/// May be called by anyone. Returns the voter deposit to `signed`.
|
/// May be called by anyone. Returns the voter deposit to `signed`.
|
||||||
fn reap_inactive_voter(aux: &T::PublicAux, signed_index: u32, who: T::AccountId, who_index: u32, assumed_vote_index: VoteIndex) {
|
fn reap_inactive_voter(aux: &T::PublicAux, signed_index: u32, who: T::AccountId, who_index: u32, assumed_vote_index: VoteIndex) {
|
||||||
assert!(!Self::presentation_active(), "cannot reap during presentation period");
|
ensure!(!Self::presentation_active(), "cannot reap during presentation period");
|
||||||
assert!(Self::voter_last_active(aux.ref_into()).is_some(), "reaper must be a voter");
|
ensure!(Self::voter_last_active(aux.ref_into()).is_some(), "reaper must be a voter");
|
||||||
let last_active = Self::voter_last_active(&who).expect("target for inactivity cleanup must be active");
|
let last_active = ensure_unwrap!(Self::voter_last_active(&who), "target for inactivity cleanup must be active");
|
||||||
assert!(assumed_vote_index == Self::vote_index(), "vote index not current");
|
ensure!(assumed_vote_index == Self::vote_index(), "vote index not current");
|
||||||
assert!(last_active < assumed_vote_index - Self::inactivity_grace_period(), "cannot reap during grace perid");
|
ensure!(last_active < assumed_vote_index - Self::inactivity_grace_period(), "cannot reap during grace perid");
|
||||||
let voters = Self::voters();
|
let voters = Self::voters();
|
||||||
let signed_index = signed_index as usize;
|
let signed_index = signed_index as usize;
|
||||||
let who_index = who_index as usize;
|
let who_index = who_index as usize;
|
||||||
assert!(signed_index < voters.len() && &voters[signed_index] == aux.ref_into(), "bad reporter index");
|
ensure!(signed_index < voters.len() && &voters[signed_index] == aux.ref_into(), "bad reporter index");
|
||||||
assert!(who_index < voters.len() && voters[who_index] == who, "bad target index");
|
ensure!(who_index < voters.len() && voters[who_index] == who, "bad target index");
|
||||||
|
|
||||||
// will definitely kill one of signed or who now.
|
// will definitely kill one of signed or who now.
|
||||||
|
|
||||||
@@ -263,8 +263,8 @@ impl<T: Trait> Module<T> {
|
|||||||
.any(|(&appr, addr)|
|
.any(|(&appr, addr)|
|
||||||
appr &&
|
appr &&
|
||||||
*addr != T::AccountId::default() &&
|
*addr != T::AccountId::default() &&
|
||||||
Self::candidate_reg_info(addr)
|
Self::candidate_reg_info(addr).map_or(false, |x| x.0 <= last_active)/*defensive only: all items in candidates list are registered*/
|
||||||
.expect("all items in candidates list are registered").0 <= last_active);
|
);
|
||||||
|
|
||||||
Self::remove_voter(
|
Self::remove_voter(
|
||||||
if valid { &who } else { aux.ref_into() },
|
if valid { &who } else { aux.ref_into() },
|
||||||
@@ -280,12 +280,12 @@ impl<T: Trait> Module<T> {
|
|||||||
|
|
||||||
/// Remove a voter. All votes are cancelled and the voter deposit is returned.
|
/// Remove a voter. All votes are cancelled and the voter deposit is returned.
|
||||||
fn retract_voter(aux: &T::PublicAux, index: u32) {
|
fn retract_voter(aux: &T::PublicAux, index: u32) {
|
||||||
assert!(!Self::presentation_active(), "cannot retract when presenting");
|
ensure!(!Self::presentation_active(), "cannot retract when presenting");
|
||||||
assert!(<LastActiveOf<T>>::exists(aux.ref_into()), "cannot retract non-voter");
|
ensure!(<LastActiveOf<T>>::exists(aux.ref_into()), "cannot retract non-voter");
|
||||||
let voters = Self::voters();
|
let voters = Self::voters();
|
||||||
let index = index as usize;
|
let index = index as usize;
|
||||||
assert!(index < voters.len(), "retraction index invalid");
|
ensure!(index < voters.len(), "retraction index invalid");
|
||||||
assert!(&voters[index] == aux.ref_into(), "retraction index mismatch");
|
ensure!(&voters[index] == aux.ref_into(), "retraction index mismatch");
|
||||||
Self::remove_voter(aux.ref_into(), index, voters);
|
Self::remove_voter(aux.ref_into(), index, voters);
|
||||||
<staking::Module<T>>::unreserve_balance(aux.ref_into(), Self::voting_bond());
|
<staking::Module<T>>::unreserve_balance(aux.ref_into(), Self::voting_bond());
|
||||||
}
|
}
|
||||||
@@ -294,18 +294,19 @@ impl<T: Trait> Module<T> {
|
|||||||
///
|
///
|
||||||
/// Account must have enough transferrable funds in it to pay the bond.
|
/// Account must have enough transferrable funds in it to pay the bond.
|
||||||
fn submit_candidacy(aux: &T::PublicAux, slot: u32) {
|
fn submit_candidacy(aux: &T::PublicAux, slot: u32) {
|
||||||
assert!(!Self::is_a_candidate(aux.ref_into()), "duplicate candidate submission");
|
ensure!(!Self::is_a_candidate(aux.ref_into()), "duplicate candidate submission");
|
||||||
assert!(<staking::Module<T>>::deduct_unbonded(aux.ref_into(), Self::candidacy_bond()), "candidate has not enough funds");
|
ensure!(<staking::Module<T>>::can_deduct_unbonded(aux.ref_into(), Self::candidacy_bond()), "candidate has not enough funds");
|
||||||
|
|
||||||
let slot = slot as usize;
|
let slot = slot as usize;
|
||||||
let count = Self::candidate_count() as usize;
|
let count = Self::candidate_count() as usize;
|
||||||
let candidates = Self::candidates();
|
let candidates = Self::candidates();
|
||||||
assert!(
|
ensure!(
|
||||||
(slot == count && count == candidates.len()) ||
|
(slot == count && count == candidates.len()) ||
|
||||||
(slot < candidates.len() && candidates[slot] == T::AccountId::default()),
|
(slot < candidates.len() && candidates[slot] == T::AccountId::default()),
|
||||||
"invalid candidate slot"
|
"invalid candidate slot"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
<staking::Module<T>>::deduct_unbonded(aux.ref_into(), Self::candidacy_bond());
|
||||||
let mut candidates = candidates;
|
let mut candidates = candidates;
|
||||||
if slot == candidates.len() {
|
if slot == candidates.len() {
|
||||||
candidates.push(aux.ref_into().clone());
|
candidates.push(aux.ref_into().clone());
|
||||||
@@ -321,23 +322,22 @@ impl<T: Trait> Module<T> {
|
|||||||
/// Only works if the `block_number >= current_vote().0` and `< current_vote().0 + presentation_duration()``
|
/// Only works if the `block_number >= current_vote().0` and `< current_vote().0 + presentation_duration()``
|
||||||
/// `signed` should have at least
|
/// `signed` should have at least
|
||||||
fn present_winner(aux: &T::PublicAux, candidate: T::AccountId, total: T::Balance, index: VoteIndex) {
|
fn present_winner(aux: &T::PublicAux, candidate: T::AccountId, total: T::Balance, index: VoteIndex) {
|
||||||
assert_eq!(index, Self::vote_index(), "index not current");
|
ensure!(index == Self::vote_index(), "index not current");
|
||||||
let (_, _, expiring) = Self::next_finalise()
|
let (_, _, expiring) = ensure_unwrap!(Self::next_finalise(), "cannot present outside of presentation period");
|
||||||
.expect("cannot present outside of presentation period");
|
|
||||||
let stakes = Self::snapshoted_stakes();
|
let stakes = Self::snapshoted_stakes();
|
||||||
let voters = Self::voters();
|
let voters = Self::voters();
|
||||||
let bad_presentation_punishment = Self::present_slash_per_voter() * T::Balance::sa(voters.len());
|
let bad_presentation_punishment = Self::present_slash_per_voter() * T::Balance::sa(voters.len());
|
||||||
assert!(<staking::Module<T>>::can_slash(aux.ref_into(), bad_presentation_punishment), "presenter must have sufficient slashable funds");
|
ensure!(<staking::Module<T>>::can_slash(aux.ref_into(), bad_presentation_punishment), "presenter must have sufficient slashable funds");
|
||||||
|
|
||||||
let mut leaderboard = Self::leaderboard().expect("leaderboard must exist while present phase active");
|
let mut leaderboard = ensure_unwrap!(Self::leaderboard(), "leaderboard must exist while present phase active");
|
||||||
assert!(total > leaderboard[0].0, "candidate not worthy of leaderboard");
|
ensure!(total > leaderboard[0].0, "candidate not worthy of leaderboard");
|
||||||
|
|
||||||
if let Some(p) = Self::active_council().iter().position(|&(ref c, _)| c == &candidate) {
|
if let Some(p) = Self::active_council().iter().position(|&(ref c, _)| c == &candidate) {
|
||||||
assert!(p < expiring.len(), "candidate must not form a duplicated member if elected");
|
ensure!(p < expiring.len(), "candidate must not form a duplicated member if elected");
|
||||||
}
|
}
|
||||||
|
|
||||||
let (registered_since, candidate_index): (VoteIndex, u32) =
|
let (registered_since, candidate_index): (VoteIndex, u32) =
|
||||||
Self::candidate_reg_info(&candidate).expect("presented candidate must be current");
|
ensure_unwrap!(Self::candidate_reg_info(&candidate), "presented candidate must be current");
|
||||||
let actual_total = voters.iter()
|
let actual_total = voters.iter()
|
||||||
.zip(stakes.iter())
|
.zip(stakes.iter())
|
||||||
.filter_map(|(voter, stake)|
|
.filter_map(|(voter, stake)|
|
||||||
@@ -440,8 +440,8 @@ impl<T: Trait> Module<T> {
|
|||||||
/// Clears all presented candidates, returning the bond of the elected ones.
|
/// Clears all presented candidates, returning the bond of the elected ones.
|
||||||
fn finalise_tally() {
|
fn finalise_tally() {
|
||||||
<SnapshotedStakes<T>>::kill();
|
<SnapshotedStakes<T>>::kill();
|
||||||
let (_, coming, expiring): (T::BlockNumber, u32, Vec<T::AccountId>) = <NextFinalise<T>>::take()
|
let (_, coming, expiring): (T::BlockNumber, u32, Vec<T::AccountId>) =
|
||||||
.expect("finalise can only be called after a tally is started.");
|
ensure_unwrap!(<NextFinalise<T>>::take(), "finalise can only be called after a tally is started.");
|
||||||
let leaderboard: Vec<(T::Balance, T::AccountId)> = <Leaderboard<T>>::take().unwrap_or_default();
|
let leaderboard: Vec<(T::Balance, T::AccountId)> = <Leaderboard<T>>::take().unwrap_or_default();
|
||||||
let new_expiry = <system::Module<T>>::block_number() + Self::term_duration();
|
let new_expiry = <system::Module<T>>::block_number() + Self::term_duration();
|
||||||
|
|
||||||
@@ -476,7 +476,7 @@ impl<T: Trait> Module<T> {
|
|||||||
.rev()
|
.rev()
|
||||||
.take_while(|&(b, _)| !b.is_zero())
|
.take_while(|&(b, _)| !b.is_zero())
|
||||||
.skip(coming as usize)
|
.skip(coming as usize)
|
||||||
.map(|(_, a)| { let i = Self::candidate_reg_info(&a).expect("runner up must be registered").1; (a, i) });
|
.filter_map(|(_, a)| Self::candidate_reg_info(&a).map(|i| (a, i.1)));
|
||||||
let mut count = 0u32;
|
let mut count = 0u32;
|
||||||
for (address, slot) in runners_up {
|
for (address, slot) in runners_up {
|
||||||
new_candidates[slot as usize] = address;
|
new_candidates[slot as usize] = address;
|
||||||
@@ -626,7 +626,8 @@ mod tests {
|
|||||||
intentions: vec![],
|
intentions: vec![],
|
||||||
validator_count: 2,
|
validator_count: 2,
|
||||||
bonding_duration: 0,
|
bonding_duration: 0,
|
||||||
transaction_fee: 0,
|
transaction_base_fee: 0,
|
||||||
|
transaction_byte_fee: 0,
|
||||||
}.build_externalities());
|
}.build_externalities());
|
||||||
t.extend(democracy::GenesisConfig::<Test>{
|
t.extend(democracy::GenesisConfig::<Test>{
|
||||||
launch_period: 1,
|
launch_period: 1,
|
||||||
@@ -760,55 +761,50 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "invalid candidate slot")]
|
fn candidate_submission_not_using_free_slot_should_not_work() {
|
||||||
fn candidate_submission_not_using_free_slot_should_panic() {
|
with_externalities(&mut new_test_ext_with_candidate_holes(), || {
|
||||||
let mut t = new_test_ext_with_candidate_holes();
|
|
||||||
|
|
||||||
with_externalities(&mut t, || {
|
|
||||||
System::set_block_number(1);
|
System::set_block_number(1);
|
||||||
Council::submit_candidacy(&4, 3);
|
assert_noop!{Council::submit_candidacy(&4, 3)}; // gives "invalid candidate slot"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "invalid candidate slot")]
|
fn bad_candidate_slot_submission_should_not_work() {
|
||||||
fn bad_candidate_slot_submission_should_panic() {
|
|
||||||
with_externalities(&mut new_test_ext(false), || {
|
with_externalities(&mut new_test_ext(false), || {
|
||||||
System::set_block_number(1);
|
System::set_block_number(1);
|
||||||
assert_eq!(Council::candidates(), Vec::<u64>::new());
|
assert_eq!(Council::candidates(), Vec::<u64>::new());
|
||||||
Council::submit_candidacy(&1, 1);
|
assert_noop!{Council::submit_candidacy(&1, 1)}; // gives "invalid candidate slot"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "invalid candidate slot")]
|
fn non_free_candidate_slot_submission_should_not_work() {
|
||||||
fn non_free_candidate_slot_submission_should_panic() {
|
|
||||||
with_externalities(&mut new_test_ext(false), || {
|
with_externalities(&mut new_test_ext(false), || {
|
||||||
System::set_block_number(1);
|
System::set_block_number(1);
|
||||||
assert_eq!(Council::candidates(), Vec::<u64>::new());
|
assert_eq!(Council::candidates(), Vec::<u64>::new());
|
||||||
Council::submit_candidacy(&1, 0);
|
Council::submit_candidacy(&1, 0);
|
||||||
Council::submit_candidacy(&2, 0);
|
assert_eq!(Council::candidates(), vec![1]);
|
||||||
|
assert_noop!{Council::submit_candidacy(&2, 0)}; // gives "invalid candidate slot"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "duplicate candidate submission")]
|
fn dupe_candidate_submission_should_not_work() {
|
||||||
fn dupe_candidate_submission_should_panic() {
|
|
||||||
with_externalities(&mut new_test_ext(false), || {
|
with_externalities(&mut new_test_ext(false), || {
|
||||||
System::set_block_number(1);
|
System::set_block_number(1);
|
||||||
assert_eq!(Council::candidates(), Vec::<u64>::new());
|
assert_eq!(Council::candidates(), Vec::<u64>::new());
|
||||||
Council::submit_candidacy(&1, 0);
|
Council::submit_candidacy(&1, 0);
|
||||||
Council::submit_candidacy(&1, 1);
|
assert_eq!(Council::candidates(), vec![1]);
|
||||||
|
assert_noop!{Council::submit_candidacy(&1, 1)}; // gives "duplicate candidate submission"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "candidate has not enough funds")]
|
fn poor_candidate_submission_should_not_work() {
|
||||||
fn poor_candidate_submission_should_panic() {
|
|
||||||
with_externalities(&mut new_test_ext(false), || {
|
with_externalities(&mut new_test_ext(false), || {
|
||||||
System::set_block_number(1);
|
System::set_block_number(1);
|
||||||
assert_eq!(Council::candidates(), Vec::<u64>::new());
|
assert_eq!(Council::candidates(), Vec::<u64>::new());
|
||||||
Council::submit_candidacy(&7, 0);
|
assert_noop!{Council::submit_candidacy(&7, 0)}; // gives "candidate has not enough funds"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -906,36 +902,34 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "retraction index mismatch")]
|
fn invalid_retraction_index_should_not_work() {
|
||||||
fn invalid_retraction_index_should_panic() {
|
|
||||||
with_externalities(&mut new_test_ext(false), || {
|
with_externalities(&mut new_test_ext(false), || {
|
||||||
System::set_block_number(1);
|
System::set_block_number(1);
|
||||||
Council::submit_candidacy(&3, 0);
|
Council::submit_candidacy(&3, 0);
|
||||||
Council::set_approvals(&1, vec![true], 0);
|
Council::set_approvals(&1, vec![true], 0);
|
||||||
Council::set_approvals(&2, vec![true], 0);
|
Council::set_approvals(&2, vec![true], 0);
|
||||||
Council::retract_voter(&1, 1);
|
assert_eq!(Council::voters(), vec![1, 2]);
|
||||||
|
assert_noop!{Council::retract_voter(&1, 1)}; // gives "retraction index mismatch"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "retraction index invalid")]
|
fn overflow_retraction_index_should_not_work() {
|
||||||
fn overflow_retraction_index_should_panic() {
|
|
||||||
with_externalities(&mut new_test_ext(false), || {
|
with_externalities(&mut new_test_ext(false), || {
|
||||||
System::set_block_number(1);
|
System::set_block_number(1);
|
||||||
Council::submit_candidacy(&3, 0);
|
Council::submit_candidacy(&3, 0);
|
||||||
Council::set_approvals(&1, vec![true], 0);
|
Council::set_approvals(&1, vec![true], 0);
|
||||||
Council::retract_voter(&1, 1);
|
assert_noop!{Council::retract_voter(&1, 1)}; // gives "retraction index invalid"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "cannot retract non-voter")]
|
fn non_voter_retraction_should_not_work() {
|
||||||
fn non_voter_retraction_should_panic() {
|
|
||||||
with_externalities(&mut new_test_ext(false), || {
|
with_externalities(&mut new_test_ext(false), || {
|
||||||
System::set_block_number(1);
|
System::set_block_number(1);
|
||||||
Council::submit_candidacy(&3, 0);
|
Council::submit_candidacy(&3, 0);
|
||||||
Council::set_approvals(&1, vec![true], 0);
|
Council::set_approvals(&1, vec![true], 0);
|
||||||
Council::retract_voter(&2, 0);
|
assert_noop!{Council::retract_voter(&2, 0)}; // gives "cannot retract non-voter"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1028,8 +1022,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "candidate must not form a duplicated member if elected")]
|
fn presenting_for_double_election_should_not_work() {
|
||||||
fn presenting_for_double_election_should_panic() {
|
|
||||||
with_externalities(&mut new_test_ext(false), || {
|
with_externalities(&mut new_test_ext(false), || {
|
||||||
System::set_block_number(4);
|
System::set_block_number(4);
|
||||||
Council::submit_candidacy(&2, 0);
|
Council::submit_candidacy(&2, 0);
|
||||||
@@ -1046,7 +1039,7 @@ mod tests {
|
|||||||
Council::end_block(System::block_number());
|
Council::end_block(System::block_number());
|
||||||
|
|
||||||
System::set_block_number(10);
|
System::set_block_number(10);
|
||||||
Council::present_winner(&4, 2, 11, 1);
|
assert_noop!{Council::present_winner(&4, 2, 11, 1)}; // gives "candidate must not form a duplicated member if elected"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1088,8 +1081,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "bad reporter index")]
|
fn retracting_inactive_voter_with_bad_reporter_index_should_not_work() {
|
||||||
fn retracting_inactive_voter_with_bad_reporter_index_should_panic() {
|
|
||||||
with_externalities(&mut new_test_ext(false), || {
|
with_externalities(&mut new_test_ext(false), || {
|
||||||
System::set_block_number(4);
|
System::set_block_number(4);
|
||||||
Council::submit_candidacy(&2, 0);
|
Council::submit_candidacy(&2, 0);
|
||||||
@@ -1109,17 +1101,16 @@ mod tests {
|
|||||||
Council::present_winner(&4, 5, 38, 1);
|
Council::present_winner(&4, 5, 38, 1);
|
||||||
Council::end_block(System::block_number());
|
Council::end_block(System::block_number());
|
||||||
|
|
||||||
Council::reap_inactive_voter(&2,
|
assert_noop!{Council::reap_inactive_voter(&2,
|
||||||
42,
|
42,
|
||||||
2, Council::voters().iter().position(|&i| i == 2).unwrap() as u32,
|
2, Council::voters().iter().position(|&i| i == 2).unwrap() as u32,
|
||||||
2
|
2
|
||||||
);
|
)}; // given "bad reporter index"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "bad target index")]
|
fn retracting_inactive_voter_with_bad_target_index_should_not_work() {
|
||||||
fn retracting_inactive_voter_with_bad_target_index_should_panic() {
|
|
||||||
with_externalities(&mut new_test_ext(false), || {
|
with_externalities(&mut new_test_ext(false), || {
|
||||||
System::set_block_number(4);
|
System::set_block_number(4);
|
||||||
Council::submit_candidacy(&2, 0);
|
Council::submit_candidacy(&2, 0);
|
||||||
@@ -1139,11 +1130,11 @@ mod tests {
|
|||||||
Council::present_winner(&4, 5, 38, 1);
|
Council::present_winner(&4, 5, 38, 1);
|
||||||
Council::end_block(System::block_number());
|
Council::end_block(System::block_number());
|
||||||
|
|
||||||
Council::reap_inactive_voter(&2,
|
assert_noop!{Council::reap_inactive_voter(&2,
|
||||||
Council::voters().iter().position(|&i| i == 2).unwrap() as u32,
|
Council::voters().iter().position(|&i| i == 2).unwrap() as u32,
|
||||||
2, 42,
|
2, 42,
|
||||||
2
|
2
|
||||||
);
|
)}; // gives "bad target index"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1190,8 +1181,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "reaper must be a voter")]
|
fn attempting_to_retract_inactive_voter_by_nonvoter_should_not_work() {
|
||||||
fn attempting_to_retract_inactive_voter_by_nonvoter_should_panic() {
|
|
||||||
with_externalities(&mut new_test_ext(false), || {
|
with_externalities(&mut new_test_ext(false), || {
|
||||||
System::set_block_number(4);
|
System::set_block_number(4);
|
||||||
Council::submit_candidacy(&2, 0);
|
Council::submit_candidacy(&2, 0);
|
||||||
@@ -1211,17 +1201,16 @@ mod tests {
|
|||||||
Council::present_winner(&4, 5, 41, 1);
|
Council::present_winner(&4, 5, 41, 1);
|
||||||
Council::end_block(System::block_number());
|
Council::end_block(System::block_number());
|
||||||
|
|
||||||
Council::reap_inactive_voter(&4,
|
assert_noop!{Council::reap_inactive_voter(&4,
|
||||||
0,
|
0,
|
||||||
2, Council::voters().iter().position(|&i| i == 2).unwrap() as u32,
|
2, Council::voters().iter().position(|&i| i == 2).unwrap() as u32,
|
||||||
2
|
2
|
||||||
);
|
)}; // gives "reaper must be a voter"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "candidate not worthy of leaderboard")]
|
fn presenting_loser_should_not_work() {
|
||||||
fn presenting_loser_should_panic() {
|
|
||||||
with_externalities(&mut new_test_ext(false), || {
|
with_externalities(&mut new_test_ext(false), || {
|
||||||
System::set_block_number(4);
|
System::set_block_number(4);
|
||||||
Council::submit_candidacy(&1, 0);
|
Council::submit_candidacy(&1, 0);
|
||||||
@@ -1241,7 +1230,7 @@ mod tests {
|
|||||||
Council::present_winner(&4, 3, 21, 0);
|
Council::present_winner(&4, 3, 21, 0);
|
||||||
Council::present_winner(&4, 4, 31, 0);
|
Council::present_winner(&4, 4, 31, 0);
|
||||||
Council::present_winner(&4, 5, 41, 0);
|
Council::present_winner(&4, 5, 41, 0);
|
||||||
Council::present_winner(&4, 2, 11, 0);
|
assert_noop!{Council::present_winner(&4, 2, 11, 0)}; // gives "candidate not worthy of leaderboard"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1278,18 +1267,16 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "cannot present outside of presentation period")]
|
fn present_outside_of_presentation_period_should_not_work() {
|
||||||
fn present_panics_outside_of_presentation_period() {
|
|
||||||
with_externalities(&mut new_test_ext(false), || {
|
with_externalities(&mut new_test_ext(false), || {
|
||||||
System::set_block_number(4);
|
System::set_block_number(4);
|
||||||
assert!(!Council::presentation_active());
|
assert!(!Council::presentation_active());
|
||||||
Council::present_winner(&5, 5, 1, 0);
|
assert_noop!{Council::present_winner(&5, 5, 1, 0)}; // gives "cannot present outside of presentation period"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "index not current")]
|
fn present_with_invalid_vote_index_should_not_work() {
|
||||||
fn present_panics_with_invalid_vote_index() {
|
|
||||||
with_externalities(&mut new_test_ext(false), || {
|
with_externalities(&mut new_test_ext(false), || {
|
||||||
System::set_block_number(4);
|
System::set_block_number(4);
|
||||||
Council::submit_candidacy(&2, 0);
|
Council::submit_candidacy(&2, 0);
|
||||||
@@ -1299,13 +1286,12 @@ mod tests {
|
|||||||
Council::end_block(System::block_number());
|
Council::end_block(System::block_number());
|
||||||
|
|
||||||
System::set_block_number(6);
|
System::set_block_number(6);
|
||||||
Council::present_winner(&4, 2, 11, 1);
|
assert_noop!{Council::present_winner(&4, 2, 11, 1)}; // gives "index not current"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "presenter must have sufficient slashable funds")]
|
fn present_when_presenter_is_poor_should_not_work() {
|
||||||
fn present_panics_when_presenter_is_poor() {
|
|
||||||
with_externalities(&mut new_test_ext(false), || {
|
with_externalities(&mut new_test_ext(false), || {
|
||||||
System::set_block_number(4);
|
System::set_block_number(4);
|
||||||
assert!(!Council::presentation_active());
|
assert!(!Council::presentation_active());
|
||||||
@@ -1318,7 +1304,7 @@ mod tests {
|
|||||||
|
|
||||||
System::set_block_number(6);
|
System::set_block_number(6);
|
||||||
assert_eq!(Staking::balance(&1), 1);
|
assert_eq!(Staking::balance(&1), 1);
|
||||||
Council::present_winner(&1, 1, 30, 0);
|
assert_noop!{Council::present_winner(&1, 1, 30, 0)}; // gives "presenter must have sufficient slashable funds"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,12 +75,12 @@ impl<T: Trait> Module<T> {
|
|||||||
// Dispatch
|
// Dispatch
|
||||||
fn propose(aux: &T::PublicAux, proposal: Box<T::Proposal>) {
|
fn propose(aux: &T::PublicAux, proposal: Box<T::Proposal>) {
|
||||||
let expiry = <system::Module<T>>::block_number() + Self::voting_period();
|
let expiry = <system::Module<T>>::block_number() + Self::voting_period();
|
||||||
assert!(Self::will_still_be_councillor_at(aux.ref_into(), expiry));
|
ensure!(Self::will_still_be_councillor_at(aux.ref_into(), expiry));
|
||||||
|
|
||||||
let proposal_hash = T::Hashing::hash_of(&proposal);
|
let proposal_hash = T::Hashing::hash_of(&proposal);
|
||||||
|
|
||||||
assert!(!<ProposalOf<T>>::exists(proposal_hash), "No duplicate proposals allowed");
|
ensure!(!<ProposalOf<T>>::exists(proposal_hash), "No duplicate proposals allowed");
|
||||||
assert!(!Self::is_vetoed(&proposal_hash));
|
ensure!(!Self::is_vetoed(&proposal_hash));
|
||||||
|
|
||||||
let mut proposals = Self::proposals();
|
let mut proposals = Self::proposals();
|
||||||
proposals.push((expiry, proposal_hash));
|
proposals.push((expiry, proposal_hash));
|
||||||
@@ -102,14 +102,14 @@ impl<T: Trait> Module<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn veto(aux: &T::PublicAux, proposal_hash: T::Hash) {
|
fn veto(aux: &T::PublicAux, proposal_hash: T::Hash) {
|
||||||
assert!(Self::is_councillor(aux.ref_into()), "only councillors may veto council proposals");
|
ensure!(Self::is_councillor(aux.ref_into()), "only councillors may veto council proposals");
|
||||||
assert!(<ProposalVoters<T>>::exists(&proposal_hash), "proposal must exist to be vetoed");
|
ensure!(<ProposalVoters<T>>::exists(&proposal_hash), "proposal must exist to be vetoed");
|
||||||
|
|
||||||
let mut existing_vetoers = Self::veto_of(&proposal_hash)
|
let mut existing_vetoers = Self::veto_of(&proposal_hash)
|
||||||
.map(|pair| pair.1)
|
.map(|pair| pair.1)
|
||||||
.unwrap_or_else(Vec::new);
|
.unwrap_or_else(Vec::new);
|
||||||
let insert_position = existing_vetoers.binary_search(aux.ref_into())
|
let insert_position = ensure_unwrap_err!(existing_vetoers.binary_search(aux.ref_into()),
|
||||||
.expect_err("a councillor may not veto a proposal twice");
|
"a councillor may not veto a proposal twice");
|
||||||
existing_vetoers.insert(insert_position, aux.ref_into().clone());
|
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);
|
Self::set_veto_of(&proposal_hash, <system::Module<T>>::block_number() + Self::cooloff_period(), existing_vetoers);
|
||||||
|
|
||||||
@@ -163,8 +163,7 @@ impl<T: Trait> Module<T> {
|
|||||||
Some(&(expiry, hash)) if expiry == n => {
|
Some(&(expiry, hash)) if expiry == n => {
|
||||||
// yes this is horrible, but fixing it will need substantial work in storage.
|
// yes this is horrible, but fixing it will need substantial work in storage.
|
||||||
Self::set_proposals(&proposals[1..].to_vec());
|
Self::set_proposals(&proposals[1..].to_vec());
|
||||||
let proposal = <ProposalOf<T>>::take(hash).expect("all queued proposal hashes must have associated proposals");
|
<ProposalOf<T>>::take(hash).map(|p| (p, hash)) /* defensive only: all queued proposal hashes must have associated proposals*/
|
||||||
Some((proposal, hash))
|
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@@ -311,8 +310,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
fn double_veto_should_not_work() {
|
||||||
fn double_veto_should_panic() {
|
|
||||||
with_externalities(&mut new_test_ext(true), || {
|
with_externalities(&mut new_test_ext(true), || {
|
||||||
System::set_block_number(1);
|
System::set_block_number(1);
|
||||||
let proposal = bonding_duration_proposal(42);
|
let proposal = bonding_duration_proposal(42);
|
||||||
@@ -322,13 +320,12 @@ mod tests {
|
|||||||
|
|
||||||
System::set_block_number(3);
|
System::set_block_number(3);
|
||||||
CouncilVoting::propose(&1, Box::new(proposal.clone()));
|
CouncilVoting::propose(&1, Box::new(proposal.clone()));
|
||||||
CouncilVoting::veto(&2, hash);
|
assert_noop!{CouncilVoting::veto(&2, hash)};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
fn retry_in_cooloff_should_not_work() {
|
||||||
fn retry_in_cooloff_should_panic() {
|
|
||||||
with_externalities(&mut new_test_ext(true), || {
|
with_externalities(&mut new_test_ext(true), || {
|
||||||
System::set_block_number(1);
|
System::set_block_number(1);
|
||||||
let proposal = bonding_duration_proposal(42);
|
let proposal = bonding_duration_proposal(42);
|
||||||
@@ -337,7 +334,7 @@ mod tests {
|
|||||||
CouncilVoting::veto(&2, hash);
|
CouncilVoting::veto(&2, hash);
|
||||||
|
|
||||||
System::set_block_number(2);
|
System::set_block_number(2);
|
||||||
CouncilVoting::propose(&1, Box::new(proposal.clone()));
|
assert_noop!{CouncilVoting::propose(&1, Box::new(proposal.clone()))};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,12 +444,11 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
fn propose_by_public_should_not_work() {
|
||||||
fn propose_by_public_should_panic() {
|
|
||||||
with_externalities(&mut new_test_ext(true), || {
|
with_externalities(&mut new_test_ext(true), || {
|
||||||
System::set_block_number(1);
|
System::set_block_number(1);
|
||||||
let proposal = bonding_duration_proposal(42);
|
let proposal = bonding_duration_proposal(42);
|
||||||
CouncilVoting::propose(&4, Box::new(proposal));
|
assert_noop!{CouncilVoting::propose(&4, Box::new(proposal))};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ impl<T: Trait> Module<T> {
|
|||||||
|
|
||||||
// exposed immutables.
|
// exposed immutables.
|
||||||
|
|
||||||
/// Get the amount locked in support of `proposal`; false if proposal isn't a valid proposal
|
/// Get the amount locked in support of `proposal`; `None` if proposal isn't a valid proposal
|
||||||
/// index.
|
/// index.
|
||||||
pub fn locked_for(proposal: PropIndex) -> Option<T::Balance> {
|
pub fn locked_for(proposal: PropIndex) -> Option<T::Balance> {
|
||||||
Self::deposit_of(proposal).map(|(d, l)| d * T::Balance::sa(l.len()))
|
Self::deposit_of(proposal).map(|(d, l)| d * T::Balance::sa(l.len()))
|
||||||
@@ -135,7 +135,7 @@ impl<T: Trait> Module<T> {
|
|||||||
/// Get the voters for the current proposal.
|
/// Get the voters for the current proposal.
|
||||||
pub fn tally(ref_index: ReferendumIndex) -> (T::Balance, T::Balance) {
|
pub fn tally(ref_index: ReferendumIndex) -> (T::Balance, T::Balance) {
|
||||||
Self::voters_for(ref_index).iter()
|
Self::voters_for(ref_index).iter()
|
||||||
.map(|a| (<staking::Module<T>>::balance(a), Self::vote_of((ref_index, a.clone())).expect("all items come from `voters`; for an item to be in `voters` there must be a vote registered; qed")))
|
.map(|a| (<staking::Module<T>>::balance(a), Self::vote_of((ref_index, a.clone())).unwrap_or(false)/*defensive only: all items come from `voters`; for an item to be in `voters` there must be a vote registered; qed*/))
|
||||||
.map(|(bal, vote)| if vote { (bal, Zero::zero()) } else { (Zero::zero(), bal) })
|
.map(|(bal, vote)| if vote { (bal, Zero::zero()) } else { (Zero::zero(), bal) })
|
||||||
.fold((Zero::zero(), Zero::zero()), |(a, b), (c, d)| (a + c, b + d))
|
.fold((Zero::zero(), Zero::zero()), |(a, b), (c, d)| (a + c, b + d))
|
||||||
}
|
}
|
||||||
@@ -144,8 +144,8 @@ impl<T: Trait> Module<T> {
|
|||||||
|
|
||||||
/// Propose a sensitive action to be taken.
|
/// Propose a sensitive action to be taken.
|
||||||
fn propose(aux: &T::PublicAux, proposal: Box<T::Proposal>, value: T::Balance) {
|
fn propose(aux: &T::PublicAux, proposal: Box<T::Proposal>, value: T::Balance) {
|
||||||
assert!(value >= Self::minimum_deposit());
|
ensure!(value >= Self::minimum_deposit());
|
||||||
assert!(<staking::Module<T>>::deduct_unbonded(aux.ref_into(), value));
|
ensure!(<staking::Module<T>>::deduct_unbonded(aux.ref_into(), value));
|
||||||
|
|
||||||
let index = Self::public_prop_count();
|
let index = Self::public_prop_count();
|
||||||
<PublicPropCount<T>>::put(index + 1);
|
<PublicPropCount<T>>::put(index + 1);
|
||||||
@@ -158,21 +158,23 @@ impl<T: Trait> Module<T> {
|
|||||||
|
|
||||||
/// Propose a sensitive action to be taken.
|
/// Propose a sensitive action to be taken.
|
||||||
fn second(aux: &T::PublicAux, proposal: PropIndex) {
|
fn second(aux: &T::PublicAux, proposal: PropIndex) {
|
||||||
let mut deposit = Self::deposit_of(proposal).expect("can only second an existing proposal");
|
if let Some(mut deposit) = Self::deposit_of(proposal) {
|
||||||
assert!(<staking::Module<T>>::deduct_unbonded(aux.ref_into(), deposit.0));
|
ensure!(<staking::Module<T>>::deduct_unbonded(aux.ref_into(), deposit.0));
|
||||||
|
deposit.1.push(aux.ref_into().clone());
|
||||||
deposit.1.push(aux.ref_into().clone());
|
<DepositOf<T>>::insert(proposal, deposit);
|
||||||
<DepositOf<T>>::insert(proposal, deposit);
|
} else {
|
||||||
|
fail!("can only second an existing proposal");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Vote in a referendum. If `approve_proposal` is true, the vote is to enact the proposal;
|
/// 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..
|
/// false would be a vote to keep the status quo..
|
||||||
fn vote(aux: &T::PublicAux, ref_index: ReferendumIndex, approve_proposal: bool) {
|
fn vote(aux: &T::PublicAux, ref_index: ReferendumIndex, approve_proposal: bool) {
|
||||||
if !Self::is_active_referendum(ref_index) {
|
if !Self::is_active_referendum(ref_index) {
|
||||||
panic!("vote given for invalid referendum.")
|
fail!("vote given for invalid referendum.")
|
||||||
}
|
}
|
||||||
if <staking::Module<T>>::balance(aux.ref_into()).is_zero() {
|
if <staking::Module<T>>::balance(aux.ref_into()).is_zero() {
|
||||||
panic!("transactor must have balance to signal approval.");
|
fail!("transactor must have balance to signal approval.");
|
||||||
}
|
}
|
||||||
if !<VoteOf<T>>::exists(&(ref_index, aux.ref_into().clone())) {
|
if !<VoteOf<T>>::exists(&(ref_index, aux.ref_into().clone())) {
|
||||||
let mut voters = Self::voters_for(ref_index);
|
let mut voters = Self::voters_for(ref_index);
|
||||||
@@ -184,7 +186,7 @@ impl<T: Trait> Module<T> {
|
|||||||
|
|
||||||
/// Start a referendum.
|
/// Start a referendum.
|
||||||
fn start_referendum(proposal: Box<T::Proposal>, vote_threshold: VoteThreshold) {
|
fn start_referendum(proposal: Box<T::Proposal>, vote_threshold: VoteThreshold) {
|
||||||
Self::inject_referendum(<system::Module<T>>::block_number() + Self::voting_period(), *proposal, vote_threshold);
|
let _ = Self::inject_referendum(<system::Module<T>>::block_number() + Self::voting_period(), *proposal, vote_threshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove a referendum.
|
/// Remove a referendum.
|
||||||
@@ -196,7 +198,7 @@ impl<T: Trait> Module<T> {
|
|||||||
|
|
||||||
/// Start a referendum. Can be called directly by the council.
|
/// Start a referendum. Can be called directly by the council.
|
||||||
pub fn internal_start_referendum(proposal: T::Proposal, vote_threshold: VoteThreshold) {
|
pub fn internal_start_referendum(proposal: T::Proposal, vote_threshold: VoteThreshold) {
|
||||||
<Module<T>>::inject_referendum(<system::Module<T>>::block_number() + <Module<T>>::voting_period(), proposal, vote_threshold);
|
let _ = <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.
|
/// Remove a referendum. Can be called directly by the council.
|
||||||
@@ -211,15 +213,15 @@ impl<T: Trait> Module<T> {
|
|||||||
end: T::BlockNumber,
|
end: T::BlockNumber,
|
||||||
proposal: T::Proposal,
|
proposal: T::Proposal,
|
||||||
vote_threshold: VoteThreshold
|
vote_threshold: VoteThreshold
|
||||||
) -> ReferendumIndex {
|
) -> Result<ReferendumIndex, &'static str> {
|
||||||
let ref_index = Self::referendum_count();
|
let ref_index = Self::referendum_count();
|
||||||
if ref_index > 0 && Self::referendum_info(ref_index - 1).map(|i| i.0 > end).unwrap_or(false) {
|
if ref_index > 0 && Self::referendum_info(ref_index - 1).map(|i| i.0 > end).unwrap_or(false) {
|
||||||
panic!("Cannot inject a referendum that ends earlier than preceeding referendum");
|
Err("Cannot inject a referendum that ends earlier than preceeding referendum")?
|
||||||
}
|
}
|
||||||
|
|
||||||
<ReferendumCount<T>>::put(ref_index + 1);
|
<ReferendumCount<T>>::put(ref_index + 1);
|
||||||
<ReferendumInfoOf<T>>::insert(ref_index, (end, proposal, vote_threshold));
|
<ReferendumInfoOf<T>>::insert(ref_index, (end, proposal, vote_threshold));
|
||||||
ref_index
|
Ok(ref_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove all info on a referendum.
|
/// Remove all info on a referendum.
|
||||||
@@ -232,23 +234,25 @@ impl<T: Trait> Module<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Current era is ending; we should finish up any proposals.
|
/// Current era is ending; we should finish up any proposals.
|
||||||
fn end_block(now: T::BlockNumber) {
|
fn end_block(now: T::BlockNumber) -> Result<(), &'static str> {
|
||||||
// pick out another public referendum if it's time.
|
// pick out another public referendum if it's time.
|
||||||
if (now % Self::launch_period()).is_zero() {
|
if (now % Self::launch_period()).is_zero() {
|
||||||
let mut public_props = Self::public_props();
|
let mut public_props = Self::public_props();
|
||||||
if let Some((winner_index, _)) = public_props.iter()
|
if let Some((winner_index, _)) = public_props.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.max_by_key(|x| Self::locked_for((x.1).0).expect("All current public proposals have an amount locked"))
|
.max_by_key(|x| Self::locked_for((x.1).0).unwrap_or_else(Zero::zero)/*defensive only: All current public proposals have an amount locked*/)
|
||||||
{
|
{
|
||||||
let (prop_index, proposal, _) = public_props.swap_remove(winner_index);
|
let (prop_index, proposal, _) = public_props.swap_remove(winner_index);
|
||||||
let (deposit, depositors): (T::Balance, Vec<T::AccountId>) =
|
if let Some((deposit, depositors)) = <DepositOf<T>>::take(prop_index) {//: (T::Balance, Vec<T::AccountId>) =
|
||||||
<DepositOf<T>>::take(prop_index).expect("depositors always exist for current proposals");
|
// refund depositors
|
||||||
// refund depositors
|
for d in &depositors {
|
||||||
for d in &depositors {
|
<staking::Module<T>>::refund(d, deposit);
|
||||||
<staking::Module<T>>::refund(d, deposit);
|
}
|
||||||
|
<PublicProps<T>>::put(public_props);
|
||||||
|
Self::inject_referendum(now + Self::voting_period(), proposal, VoteThreshold::SuperMajorityApprove)?;
|
||||||
|
} else {
|
||||||
|
Err("depositors always exist for current proposals")?
|
||||||
}
|
}
|
||||||
<PublicProps<T>>::put(public_props);
|
|
||||||
Self::inject_referendum(now + Self::voting_period(), proposal, VoteThreshold::SuperMajorityApprove);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,12 +266,15 @@ impl<T: Trait> Module<T> {
|
|||||||
}
|
}
|
||||||
<NextTally<T>>::put(index + 1);
|
<NextTally<T>>::put(index + 1);
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Trait> Executable for Module<T> {
|
impl<T: Trait> Executable for Module<T> {
|
||||||
fn execute() {
|
fn execute() {
|
||||||
Self::end_block(<system::Module<T>>::block_number());
|
if let Err(e) = Self::end_block(<system::Module<T>>::block_number()) {
|
||||||
|
runtime_io::print(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,7 +395,8 @@ mod tests {
|
|||||||
intentions: vec![],
|
intentions: vec![],
|
||||||
validator_count: 2,
|
validator_count: 2,
|
||||||
bonding_duration: 3,
|
bonding_duration: 3,
|
||||||
transaction_fee: 0,
|
transaction_base_fee: 0,
|
||||||
|
transaction_byte_fee: 0,
|
||||||
}.build_externalities());
|
}.build_externalities());
|
||||||
t.extend(GenesisConfig::<Test>{
|
t.extend(GenesisConfig::<Test>{
|
||||||
launch_period: 1,
|
launch_period: 1,
|
||||||
@@ -437,7 +445,7 @@ mod tests {
|
|||||||
with_externalities(&mut new_test_ext(), || {
|
with_externalities(&mut new_test_ext(), || {
|
||||||
System::set_block_number(1);
|
System::set_block_number(1);
|
||||||
propose_sessions_per_era(1, 2, 1);
|
propose_sessions_per_era(1, 2, 1);
|
||||||
Democracy::end_block(System::block_number());
|
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||||
|
|
||||||
System::set_block_number(2);
|
System::set_block_number(2);
|
||||||
let r = 0;
|
let r = 0;
|
||||||
@@ -448,7 +456,7 @@ mod tests {
|
|||||||
assert_eq!(Democracy::vote_of((r, 1)), Some(true));
|
assert_eq!(Democracy::vote_of((r, 1)), Some(true));
|
||||||
assert_eq!(Democracy::tally(r), (10, 0));
|
assert_eq!(Democracy::tally(r), (10, 0));
|
||||||
|
|
||||||
Democracy::end_block(System::block_number());
|
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||||
Staking::check_new_era();
|
Staking::check_new_era();
|
||||||
|
|
||||||
assert_eq!(Staking::era_length(), 2);
|
assert_eq!(Staking::era_length(), 2);
|
||||||
@@ -479,7 +487,7 @@ mod tests {
|
|||||||
Democracy::second(&5, 0);
|
Democracy::second(&5, 0);
|
||||||
Democracy::second(&5, 0);
|
Democracy::second(&5, 0);
|
||||||
Democracy::second(&5, 0);
|
Democracy::second(&5, 0);
|
||||||
Democracy::end_block(System::block_number());
|
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||||
assert_eq!(Staking::balance(&1), 10);
|
assert_eq!(Staking::balance(&1), 10);
|
||||||
assert_eq!(Staking::balance(&2), 20);
|
assert_eq!(Staking::balance(&2), 20);
|
||||||
assert_eq!(Staking::balance(&5), 50);
|
assert_eq!(Staking::balance(&5), 50);
|
||||||
@@ -487,35 +495,35 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
|
||||||
fn proposal_with_deposit_below_minimum_should_panic() {
|
fn proposal_with_deposit_below_minimum_should_panic() {
|
||||||
with_externalities(&mut new_test_ext(), || {
|
with_externalities(&mut new_test_ext(), || {
|
||||||
System::set_block_number(1);
|
System::set_block_number(1);
|
||||||
propose_sessions_per_era(1, 2, 0);
|
propose_sessions_per_era(1, 2, 0);
|
||||||
|
assert_eq!(Democracy::locked_for(0), None);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
|
||||||
fn poor_proposer_should_panic() {
|
fn poor_proposer_should_panic() {
|
||||||
with_externalities(&mut new_test_ext(), || {
|
with_externalities(&mut new_test_ext(), || {
|
||||||
System::set_block_number(1);
|
System::set_block_number(1);
|
||||||
propose_sessions_per_era(1, 2, 11);
|
propose_sessions_per_era(1, 2, 11);
|
||||||
|
assert_eq!(Democracy::locked_for(0), None);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
|
||||||
fn poor_seconder_should_panic() {
|
fn poor_seconder_should_panic() {
|
||||||
with_externalities(&mut new_test_ext(), || {
|
with_externalities(&mut new_test_ext(), || {
|
||||||
System::set_block_number(1);
|
System::set_block_number(1);
|
||||||
propose_sessions_per_era(2, 2, 11);
|
propose_sessions_per_era(2, 2, 11);
|
||||||
Democracy::second(&1, 0);
|
Democracy::second(&1, 0);
|
||||||
|
assert_eq!(Democracy::locked_for(0), Some(11));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn propose_bonding_duration(who: u64, value: u64, locked: u64) {
|
fn propose_bonding_duration(who: u64, value: u64, locked: u64) {
|
||||||
Democracy::propose(&who, Box::new(Proposal::Staking(staking::PrivCall::set_bonding_duration(value))), locked);
|
Democracy::propose(&who, Box::new(Proposal::Staking(staking::PrivCall::set_bonding_duration(value))), locked);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -525,23 +533,23 @@ mod tests {
|
|||||||
propose_bonding_duration(1, 2, 2);
|
propose_bonding_duration(1, 2, 2);
|
||||||
propose_bonding_duration(1, 4, 4);
|
propose_bonding_duration(1, 4, 4);
|
||||||
propose_bonding_duration(1, 3, 3);
|
propose_bonding_duration(1, 3, 3);
|
||||||
Democracy::end_block(System::block_number());
|
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||||
|
|
||||||
System::set_block_number(1);
|
System::set_block_number(1);
|
||||||
Democracy::vote(&1, 0, true);
|
Democracy::vote(&1, 0, true);
|
||||||
Democracy::end_block(System::block_number());
|
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||||
Staking::check_new_era();
|
Staking::check_new_era();
|
||||||
assert_eq!(Staking::bonding_duration(), 4);
|
assert_eq!(Staking::bonding_duration(), 4);
|
||||||
|
|
||||||
System::set_block_number(2);
|
System::set_block_number(2);
|
||||||
Democracy::vote(&1, 1, true);
|
Democracy::vote(&1, 1, true);
|
||||||
Democracy::end_block(System::block_number());
|
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||||
Staking::check_new_era();
|
Staking::check_new_era();
|
||||||
assert_eq!(Staking::bonding_duration(), 3);
|
assert_eq!(Staking::bonding_duration(), 3);
|
||||||
|
|
||||||
System::set_block_number(3);
|
System::set_block_number(3);
|
||||||
Democracy::vote(&1, 2, true);
|
Democracy::vote(&1, 2, true);
|
||||||
Democracy::end_block(System::block_number());
|
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||||
Staking::check_new_era();
|
Staking::check_new_era();
|
||||||
assert_eq!(Staking::bonding_duration(), 2);
|
assert_eq!(Staking::bonding_duration(), 2);
|
||||||
});
|
});
|
||||||
@@ -555,14 +563,14 @@ mod tests {
|
|||||||
fn simple_passing_should_work() {
|
fn simple_passing_should_work() {
|
||||||
with_externalities(&mut new_test_ext(), || {
|
with_externalities(&mut new_test_ext(), || {
|
||||||
System::set_block_number(1);
|
System::set_block_number(1);
|
||||||
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove);
|
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||||
Democracy::vote(&1, r, true);
|
Democracy::vote(&1, r, true);
|
||||||
|
|
||||||
assert_eq!(Democracy::voters_for(r), vec![1]);
|
assert_eq!(Democracy::voters_for(r), vec![1]);
|
||||||
assert_eq!(Democracy::vote_of((r, 1)), Some(true));
|
assert_eq!(Democracy::vote_of((r, 1)), Some(true));
|
||||||
assert_eq!(Democracy::tally(r), (10, 0));
|
assert_eq!(Democracy::tally(r), (10, 0));
|
||||||
|
|
||||||
Democracy::end_block(System::block_number());
|
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||||
Staking::check_new_era();
|
Staking::check_new_era();
|
||||||
|
|
||||||
assert_eq!(Staking::era_length(), 2);
|
assert_eq!(Staking::era_length(), 2);
|
||||||
@@ -573,11 +581,11 @@ mod tests {
|
|||||||
fn cancel_referendum_should_work() {
|
fn cancel_referendum_should_work() {
|
||||||
with_externalities(&mut new_test_ext(), || {
|
with_externalities(&mut new_test_ext(), || {
|
||||||
System::set_block_number(1);
|
System::set_block_number(1);
|
||||||
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove);
|
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||||
Democracy::vote(&1, r, true);
|
Democracy::vote(&1, r, true);
|
||||||
Democracy::cancel_referendum(r);
|
Democracy::cancel_referendum(r);
|
||||||
|
|
||||||
Democracy::end_block(System::block_number());
|
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||||
Staking::check_new_era();
|
Staking::check_new_era();
|
||||||
|
|
||||||
assert_eq!(Staking::era_length(), 1);
|
assert_eq!(Staking::era_length(), 1);
|
||||||
@@ -588,14 +596,14 @@ mod tests {
|
|||||||
fn simple_failing_should_work() {
|
fn simple_failing_should_work() {
|
||||||
with_externalities(&mut new_test_ext(), || {
|
with_externalities(&mut new_test_ext(), || {
|
||||||
System::set_block_number(1);
|
System::set_block_number(1);
|
||||||
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove);
|
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||||
Democracy::vote(&1, r, false);
|
Democracy::vote(&1, r, false);
|
||||||
|
|
||||||
assert_eq!(Democracy::voters_for(r), vec![1]);
|
assert_eq!(Democracy::voters_for(r), vec![1]);
|
||||||
assert_eq!(Democracy::vote_of((r, 1)), Some(false));
|
assert_eq!(Democracy::vote_of((r, 1)), Some(false));
|
||||||
assert_eq!(Democracy::tally(r), (0, 10));
|
assert_eq!(Democracy::tally(r), (0, 10));
|
||||||
|
|
||||||
Democracy::end_block(System::block_number());
|
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||||
Staking::check_new_era();
|
Staking::check_new_era();
|
||||||
|
|
||||||
assert_eq!(Staking::era_length(), 1);
|
assert_eq!(Staking::era_length(), 1);
|
||||||
@@ -606,7 +614,7 @@ mod tests {
|
|||||||
fn controversial_voting_should_work() {
|
fn controversial_voting_should_work() {
|
||||||
with_externalities(&mut new_test_ext(), || {
|
with_externalities(&mut new_test_ext(), || {
|
||||||
System::set_block_number(1);
|
System::set_block_number(1);
|
||||||
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove);
|
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||||
Democracy::vote(&1, r, true);
|
Democracy::vote(&1, r, true);
|
||||||
Democracy::vote(&2, r, false);
|
Democracy::vote(&2, r, false);
|
||||||
Democracy::vote(&3, r, false);
|
Democracy::vote(&3, r, false);
|
||||||
@@ -616,7 +624,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(Democracy::tally(r), (110, 100));
|
assert_eq!(Democracy::tally(r), (110, 100));
|
||||||
|
|
||||||
Democracy::end_block(System::block_number());
|
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||||
Staking::check_new_era();
|
Staking::check_new_era();
|
||||||
|
|
||||||
assert_eq!(Staking::era_length(), 2);
|
assert_eq!(Staking::era_length(), 2);
|
||||||
@@ -627,13 +635,13 @@ mod tests {
|
|||||||
fn controversial_low_turnout_voting_should_work() {
|
fn controversial_low_turnout_voting_should_work() {
|
||||||
with_externalities(&mut new_test_ext(), || {
|
with_externalities(&mut new_test_ext(), || {
|
||||||
System::set_block_number(1);
|
System::set_block_number(1);
|
||||||
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove);
|
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||||
Democracy::vote(&5, r, false);
|
Democracy::vote(&5, r, false);
|
||||||
Democracy::vote(&6, r, true);
|
Democracy::vote(&6, r, true);
|
||||||
|
|
||||||
assert_eq!(Democracy::tally(r), (60, 50));
|
assert_eq!(Democracy::tally(r), (60, 50));
|
||||||
|
|
||||||
Democracy::end_block(System::block_number());
|
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||||
Staking::check_new_era();
|
Staking::check_new_era();
|
||||||
|
|
||||||
assert_eq!(Staking::era_length(), 1);
|
assert_eq!(Staking::era_length(), 1);
|
||||||
@@ -647,14 +655,14 @@ mod tests {
|
|||||||
assert_eq!(Staking::total_stake(), 210);
|
assert_eq!(Staking::total_stake(), 210);
|
||||||
|
|
||||||
System::set_block_number(1);
|
System::set_block_number(1);
|
||||||
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove);
|
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||||
Democracy::vote(&4, r, true);
|
Democracy::vote(&4, r, true);
|
||||||
Democracy::vote(&5, r, false);
|
Democracy::vote(&5, r, false);
|
||||||
Democracy::vote(&6, r, true);
|
Democracy::vote(&6, r, true);
|
||||||
|
|
||||||
assert_eq!(Democracy::tally(r), (100, 50));
|
assert_eq!(Democracy::tally(r), (100, 50));
|
||||||
|
|
||||||
Democracy::end_block(System::block_number());
|
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||||
Staking::check_new_era();
|
Staking::check_new_era();
|
||||||
|
|
||||||
assert_eq!(Staking::era_length(), 2);
|
assert_eq!(Staking::era_length(), 2);
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ impl<
|
|||||||
|
|
||||||
// execute transactions
|
// execute transactions
|
||||||
let (header, extrinsics) = block.deconstruct();
|
let (header, extrinsics) = block.deconstruct();
|
||||||
extrinsics.into_iter().for_each(Self::apply_extrinsic_inner);
|
extrinsics.into_iter().for_each(Self::apply_extrinsic_no_note);
|
||||||
|
|
||||||
// post-transactional book-keeping.
|
// post-transactional book-keeping.
|
||||||
Finalisation::execute();
|
Finalisation::execute();
|
||||||
@@ -116,33 +116,42 @@ impl<
|
|||||||
<system::Module<System>>::finalise()
|
<system::Module<System>>::finalise()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply outside of the block execution function.
|
/// Apply extrinsic outside of the block execution function.
|
||||||
/// This doesn't attempt to validate anything regarding the block, but it builds a list of uxt
|
/// This doesn't attempt to validate anything regarding the block, but it builds a list of uxt
|
||||||
/// hashes.
|
/// hashes.
|
||||||
pub fn apply_extrinsic(uxt: Block::Extrinsic) {
|
pub fn apply_extrinsic(uxt: Block::Extrinsic) {
|
||||||
<system::Module<System>>::note_extrinsic(uxt.encode());
|
let encoded = uxt.encode();
|
||||||
Self::apply_extrinsic_inner(uxt);
|
let encoded_len = encoded.len();
|
||||||
|
<system::Module<System>>::note_extrinsic(encoded);
|
||||||
|
Self::apply_extrinsic_no_note_with_len(uxt, encoded_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply outside of the block execution function.
|
/// Apply an extrinsic inside the block execution function.
|
||||||
/// This doesn't attempt to validate anything regarding the block.
|
fn apply_extrinsic_no_note(uxt: Block::Extrinsic) {
|
||||||
fn apply_extrinsic_inner(uxt: Block::Extrinsic) {
|
let l = uxt.encode().len();
|
||||||
|
Self::apply_extrinsic_no_note_with_len(uxt, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Actually apply an extrinsic given its `encoded_len`; this doesn't note its hash.
|
||||||
|
fn apply_extrinsic_no_note_with_len(uxt: Block::Extrinsic, encoded_len: usize) {
|
||||||
// Verify the signature is good.
|
// Verify the signature is good.
|
||||||
let xt = match uxt.check() {
|
let xt = match uxt.check() {
|
||||||
Ok(xt) => xt,
|
Ok(xt) => xt,
|
||||||
Err(_) => panic!("All transactions should be properly signed"),
|
Err(_) => panic!("All extrinsics should be properly signed"),
|
||||||
};
|
};
|
||||||
|
|
||||||
if xt.sender() != &Default::default() {
|
if xt.sender() != &Default::default() {
|
||||||
// check index
|
// check index
|
||||||
let expected_index = <system::Module<System>>::account_index(xt.sender());
|
let expected_index = <system::Module<System>>::account_index(xt.sender());
|
||||||
assert!(xt.index() == &expected_index, "All transactions should have the correct nonce");
|
assert!(xt.index() == &expected_index, "All extrinsics should have the correct nonce");
|
||||||
|
|
||||||
|
// pay any fees.
|
||||||
|
assert!(Payment::make_payment(xt.sender(), encoded_len), "All extrinsics should have sender able to pay their fees");
|
||||||
|
|
||||||
|
// AUDIT: Under no circumstances may this function panic from here onwards.
|
||||||
|
|
||||||
// increment nonce in storage
|
// increment nonce in storage
|
||||||
<system::Module<System>>::inc_account_index(xt.sender());
|
<system::Module<System>>::inc_account_index(xt.sender());
|
||||||
|
|
||||||
// pay any fees.
|
|
||||||
Payment::make_payment(xt.sender());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// decode parameters and dispatch
|
// decode parameters and dispatch
|
||||||
@@ -213,7 +222,8 @@ mod tests {
|
|||||||
intentions: vec![],
|
intentions: vec![],
|
||||||
validator_count: 0,
|
validator_count: 0,
|
||||||
bonding_duration: 0,
|
bonding_duration: 0,
|
||||||
transaction_fee: 10,
|
transaction_base_fee: 10,
|
||||||
|
transaction_byte_fee: 0,
|
||||||
}.build_externalities());
|
}.build_externalities());
|
||||||
let xt = primitives::testing::TestXt((1, 0, Call::transfer(2, 69)));
|
let xt = primitives::testing::TestXt((1, 0, Call::transfer(2, 69)));
|
||||||
with_externalities(&mut t, || {
|
with_externalities(&mut t, || {
|
||||||
@@ -239,7 +249,7 @@ mod tests {
|
|||||||
header: Header {
|
header: Header {
|
||||||
parent_hash: [69u8; 32].into(),
|
parent_hash: [69u8; 32].into(),
|
||||||
number: 1,
|
number: 1,
|
||||||
state_root: hex!("aa0cff04242e55fc780861b890aa8deba555f6ed95bd8fa575dfd80864f3b93e").into(),
|
state_root: hex!("1d43ef0fcabb78d925093fe22e50cc9ca5d182d189a3407c778e5fca714177dd").into(),
|
||||||
extrinsics_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(),
|
extrinsics_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(),
|
||||||
digest: Digest { logs: vec![], },
|
digest: Digest { logs: vec![], },
|
||||||
},
|
},
|
||||||
@@ -273,7 +283,7 @@ mod tests {
|
|||||||
header: Header {
|
header: Header {
|
||||||
parent_hash: [69u8; 32].into(),
|
parent_hash: [69u8; 32].into(),
|
||||||
number: 1,
|
number: 1,
|
||||||
state_root: hex!("aa0cff04242e55fc780861b890aa8deba555f6ed95bd8fa575dfd80864f3b93e").into(),
|
state_root: hex!("1d43ef0fcabb78d925093fe22e50cc9ca5d182d189a3407c778e5fca714177dd").into(),
|
||||||
extrinsics_root: [0u8; 32].into(),
|
extrinsics_root: [0u8; 32].into(),
|
||||||
digest: Digest { logs: vec![], },
|
digest: Digest { logs: vec![], },
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -35,12 +35,13 @@ pub trait Verify {
|
|||||||
|
|
||||||
/// Simple payment making trait, operating on a single generic `AccountId` type.
|
/// Simple payment making trait, operating on a single generic `AccountId` type.
|
||||||
pub trait MakePayment<AccountId> {
|
pub trait MakePayment<AccountId> {
|
||||||
/// Make some sort of payment concerning `who`.
|
/// Make some sort of payment concerning `who` for an extrinsic (transaction) of encoded length
|
||||||
fn make_payment(who: &AccountId);
|
/// `encoded_len` bytes. Return true iff the payment was successful.
|
||||||
|
fn make_payment(who: &AccountId, encoded_len: usize) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> MakePayment<T> for () {
|
impl<T> MakePayment<T> for () {
|
||||||
fn make_payment(_: &T) {}
|
fn make_payment(_: &T, _: usize) -> bool { true }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extensible conversion trait. Generic over both source and destination types.
|
/// Extensible conversion trait. Generic over both source and destination types.
|
||||||
|
|||||||
@@ -205,6 +205,7 @@ pub(crate) fn execute<'a, 'b: 'a, T: Trait>(
|
|||||||
let account = e.account().clone();
|
let account = e.account().clone();
|
||||||
if let Some(commit_state) =
|
if let Some(commit_state) =
|
||||||
Module::<T>::effect_transfer(&account, &transfer_to, value, e.account_db())
|
Module::<T>::effect_transfer(&account, &transfer_to, value, e.account_db())
|
||||||
|
.map_err(|_| sandbox::Error::Execution)?
|
||||||
{
|
{
|
||||||
e.account_db_mut().merge(commit_state);
|
e.account_db_mut().merge(commit_state);
|
||||||
}
|
}
|
||||||
@@ -231,6 +232,7 @@ pub(crate) fn execute<'a, 'b: 'a, T: Trait>(
|
|||||||
let account = e.account().clone();
|
let account = e.account().clone();
|
||||||
if let Some(commit_state) =
|
if let Some(commit_state) =
|
||||||
Module::<T>::effect_create(&account, &code, value, e.account_db())
|
Module::<T>::effect_create(&account, &code, value, e.account_db())
|
||||||
|
.map_err(|_| sandbox::Error::Execution)?
|
||||||
{
|
{
|
||||||
e.account_db_mut().merge(commit_state);
|
e.account_db_mut().merge(commit_state);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ use rstd::cell::RefCell;
|
|||||||
use rstd::collections::btree_map::{BTreeMap, Entry};
|
use rstd::collections::btree_map::{BTreeMap, Entry};
|
||||||
use codec::Slicable;
|
use codec::Slicable;
|
||||||
use runtime_support::{StorageValue, StorageMap, Parameter};
|
use runtime_support::{StorageValue, StorageMap, Parameter};
|
||||||
use primitives::traits::{Zero, One, Bounded, RefInto, SimpleArithmetic, Executable, MakePayment};
|
use primitives::traits::{Zero, One, Bounded, RefInto, SimpleArithmetic, Executable, MakePayment, As};
|
||||||
|
|
||||||
mod contract;
|
mod contract;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -122,8 +122,10 @@ decl_storage! {
|
|||||||
pub SessionsPerEra get(sessions_per_era): b"sta:spe" => required T::BlockNumber;
|
pub SessionsPerEra get(sessions_per_era): b"sta:spe" => required T::BlockNumber;
|
||||||
// The total amount of stake on the system.
|
// The total amount of stake on the system.
|
||||||
pub TotalStake get(total_stake): b"sta:tot" => required T::Balance;
|
pub TotalStake get(total_stake): b"sta:tot" => required T::Balance;
|
||||||
// The fee to be paid for making a transaction.
|
// The fee to be paid for making a transaction; the base.
|
||||||
pub TransactionFee get(transaction_fee): b"sta:fee" => required T::Balance;
|
pub TransactionBaseFee get(transaction_base_fee): b"sta:basefee" => required T::Balance;
|
||||||
|
// The fee to be paid for making a transaction; the per-byte portion.
|
||||||
|
pub TransactionByteFee get(transaction_byte_fee): b"sta:bytefee" => required T::Balance;
|
||||||
|
|
||||||
// The current era index.
|
// The current era index.
|
||||||
pub CurrentEra get(current_era): b"sta:era" => required T::BlockNumber;
|
pub CurrentEra get(current_era): b"sta:era" => required T::BlockNumber;
|
||||||
@@ -165,12 +167,22 @@ impl<T: Trait> Module<T> {
|
|||||||
Self::free_balance(who) + Self::reserved_balance(who)
|
Self::free_balance(who) + Self::reserved_balance(who)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Some result as `slash(who, value)` (but without the side-effects) asuming there are no
|
/// Some result as `slash(who, value)` (but without the side-effects) assuming there are no
|
||||||
/// balance changes in the meantime.
|
/// balance changes in the meantime.
|
||||||
pub fn can_slash(who: &T::AccountId, value: T::Balance) -> bool {
|
pub fn can_slash(who: &T::AccountId, value: T::Balance) -> bool {
|
||||||
Self::balance(who) >= value
|
Self::balance(who) >= value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Same result as `deduct_unbonded(who, value)` (but without the side-effects) assuming there
|
||||||
|
/// are no balance changes in the meantime.
|
||||||
|
pub fn can_deduct_unbonded(who: &T::AccountId, value: T::Balance) -> bool {
|
||||||
|
if let LockStatus::Liquid = Self::unlock_block(who) {
|
||||||
|
Self::free_balance(who) >= value
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The block at which the `who`'s funds become entirely liquid.
|
/// The block at which the `who`'s funds become entirely liquid.
|
||||||
pub fn unlock_block(who: &T::AccountId) -> LockStatus<T::BlockNumber> {
|
pub fn unlock_block(who: &T::AccountId) -> LockStatus<T::BlockNumber> {
|
||||||
match Self::bondage(who) {
|
match Self::bondage(who) {
|
||||||
@@ -183,7 +195,7 @@ impl<T: Trait> Module<T> {
|
|||||||
/// Create a smart-contract account.
|
/// 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) {
|
||||||
// commit anything that made it this far to storage
|
// commit anything that made it this far to storage
|
||||||
if let Some(commit) = Self::effect_create(aux.ref_into(), code, value, &DirectAccountDb) {
|
if let Ok(Some(commit)) = Self::effect_create(aux.ref_into(), code, value, &DirectAccountDb) {
|
||||||
<AccountDb<T>>::merge(&mut DirectAccountDb, commit);
|
<AccountDb<T>>::merge(&mut DirectAccountDb, commit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,7 +206,7 @@ impl<T: Trait> Module<T> {
|
|||||||
/// TODO: probably want to state gas-limit and gas-price.
|
/// 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) {
|
||||||
// commit anything that made it this far to storage
|
// commit anything that made it this far to storage
|
||||||
if let Some(commit) = Self::effect_transfer(aux.ref_into(), &dest, value, &DirectAccountDb) {
|
if let Ok(Some(commit)) = Self::effect_transfer(aux.ref_into(), &dest, value, &DirectAccountDb) {
|
||||||
<AccountDb<T>>::merge(&mut DirectAccountDb, commit);
|
<AccountDb<T>>::merge(&mut DirectAccountDb, commit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -205,7 +217,7 @@ impl<T: Trait> Module<T> {
|
|||||||
fn stake(aux: &T::PublicAux) {
|
fn stake(aux: &T::PublicAux) {
|
||||||
let mut intentions = <Intentions<T>>::get();
|
let mut intentions = <Intentions<T>>::get();
|
||||||
// can't be in the list twice.
|
// can't be in the list twice.
|
||||||
assert!(intentions.iter().find(|&t| t == aux.ref_into()).is_none(), "Cannot stake if already staked.");
|
ensure!(intentions.iter().find(|&t| t == aux.ref_into()).is_none(), "Cannot stake if already staked.");
|
||||||
intentions.push(aux.ref_into().clone());
|
intentions.push(aux.ref_into().clone());
|
||||||
<Intentions<T>>::put(intentions);
|
<Intentions<T>>::put(intentions);
|
||||||
<Bondage<T>>::insert(aux.ref_into(), T::BlockNumber::max_value());
|
<Bondage<T>>::insert(aux.ref_into(), T::BlockNumber::max_value());
|
||||||
@@ -218,11 +230,11 @@ impl<T: Trait> Module<T> {
|
|||||||
let mut intentions = <Intentions<T>>::get();
|
let mut intentions = <Intentions<T>>::get();
|
||||||
if let Some(position) = intentions.iter().position(|t| t == aux.ref_into()) {
|
if let Some(position) = intentions.iter().position(|t| t == aux.ref_into()) {
|
||||||
intentions.swap_remove(position);
|
intentions.swap_remove(position);
|
||||||
|
<Intentions<T>>::put(intentions);
|
||||||
|
<Bondage<T>>::insert(aux.ref_into(), Self::current_era() + Self::bonding_duration());
|
||||||
} else {
|
} else {
|
||||||
panic!("Cannot unstake if not already staked.");
|
fail!("Cannot unstake if not already staked.");
|
||||||
}
|
}
|
||||||
<Intentions<T>>::put(intentions);
|
|
||||||
<Bondage<T>>::insert(aux.ref_into(), Self::current_era() + Self::bonding_duration());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PRIV DISPATCH
|
// PRIV DISPATCH
|
||||||
@@ -280,11 +292,14 @@ impl<T: Trait> Module<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Moves `value` from balance to reserved balance.
|
/// Moves `value` from balance to reserved balance.
|
||||||
pub fn reserve_balance(who: &T::AccountId, value: T::Balance) {
|
pub fn reserve_balance(who: &T::AccountId, value: T::Balance) -> bool {
|
||||||
let b = Self::free_balance(who);
|
let b = Self::free_balance(who);
|
||||||
assert!(b >= value);
|
if b < value {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
<FreeBalance<T>>::insert(who, b - value);
|
<FreeBalance<T>>::insert(who, b - value);
|
||||||
<ReservedBalance<T>>::insert(who, Self::reserved_balance(who) + value);
|
<ReservedBalance<T>>::insert(who, Self::reserved_balance(who) + value);
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Moves `value` from reserved balance to balance.
|
/// Moves `value` from reserved balance to balance.
|
||||||
@@ -536,26 +551,28 @@ impl<T: Trait> Module<T> {
|
|||||||
code: &[u8],
|
code: &[u8],
|
||||||
value: T::Balance,
|
value: T::Balance,
|
||||||
account_db: &DB,
|
account_db: &DB,
|
||||||
) -> Option<State<T>> {
|
) -> Result<Option<State<T>>, &'static str> {
|
||||||
let from_balance = account_db.get_balance(transactor);
|
let from_balance = account_db.get_balance(transactor);
|
||||||
// TODO: a fee.
|
// TODO: a fee.
|
||||||
assert!(from_balance >= value);
|
if from_balance < value {
|
||||||
|
return Err("balance too low to send value");
|
||||||
|
}
|
||||||
|
|
||||||
let dest = T::DetermineContractAddress::contract_address_for(code, transactor);
|
let dest = T::DetermineContractAddress::contract_address_for(code, transactor);
|
||||||
|
|
||||||
// early-out if degenerate.
|
// early-out if degenerate.
|
||||||
if &dest == transactor {
|
if &dest == transactor {
|
||||||
return None;
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut local = BTreeMap::new();
|
let mut local = BTreeMap::new();
|
||||||
|
|
||||||
// two inserts are safe
|
// two inserts are safe
|
||||||
assert!(&dest != transactor);
|
// note that we now know that `&dest != transactor` due to early-out before.
|
||||||
local.insert(dest, ChangeEntry { balance: Some(value), code: Some(code.to_vec()), storage: Default::default() });
|
local.insert(dest, ChangeEntry { balance: Some(value), code: Some(code.to_vec()), storage: Default::default() });
|
||||||
local.insert(transactor.clone(), ChangeEntry::balance_changed(from_balance - value));
|
local.insert(transactor.clone(), ChangeEntry::balance_changed(from_balance - value));
|
||||||
|
|
||||||
Some(local)
|
Ok(Some(local))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn effect_transfer<DB: AccountDb<T>>(
|
fn effect_transfer<DB: AccountDb<T>>(
|
||||||
@@ -563,13 +580,19 @@ impl<T: Trait> Module<T> {
|
|||||||
dest: &T::AccountId,
|
dest: &T::AccountId,
|
||||||
value: T::Balance,
|
value: T::Balance,
|
||||||
account_db: &DB,
|
account_db: &DB,
|
||||||
) -> Option<State<T>> {
|
) -> Result<Option<State<T>>, &'static str> {
|
||||||
let from_balance = account_db.get_balance(transactor);
|
let from_balance = account_db.get_balance(transactor);
|
||||||
assert!(from_balance >= value);
|
if from_balance < value {
|
||||||
|
return Err("balance too low to send value");
|
||||||
|
}
|
||||||
|
|
||||||
let to_balance = account_db.get_balance(dest);
|
let to_balance = account_db.get_balance(dest);
|
||||||
assert!(<Bondage<T>>::get(transactor) <= <Bondage<T>>::get(dest));
|
if <Bondage<T>>::get(transactor) > <Bondage<T>>::get(dest) {
|
||||||
assert!(to_balance + value > to_balance); // no overflow
|
return Err("bondage too high to send value");
|
||||||
|
}
|
||||||
|
if to_balance + value <= to_balance {
|
||||||
|
return Err("destination balance too high to receive value");
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: a fee, based upon gaslimit/gasprice.
|
// TODO: a fee, based upon gaslimit/gasprice.
|
||||||
let gas_limit = 100_000;
|
let gas_limit = 100_000;
|
||||||
@@ -594,20 +617,23 @@ impl<T: Trait> Module<T> {
|
|||||||
contract::execute(&dest_code, dest, &mut overlay, gas_limit).is_ok()
|
contract::execute(&dest_code, dest, &mut overlay, gas_limit).is_ok()
|
||||||
};
|
};
|
||||||
|
|
||||||
if should_commit {
|
Ok(if should_commit {
|
||||||
Some(overlay.into_state())
|
Some(overlay.into_state())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Trait> MakePayment<T::AccountId> for Module<T> {
|
impl<T: Trait> MakePayment<T::AccountId> for Module<T> {
|
||||||
fn make_payment(transactor: &T::AccountId) {
|
fn make_payment(transactor: &T::AccountId, encoded_len: usize) -> bool {
|
||||||
let b = Self::free_balance(transactor);
|
let b = Self::free_balance(transactor);
|
||||||
let transaction_fee = Self::transaction_fee();
|
let transaction_fee = Self::transaction_base_fee() + Self::transaction_byte_fee() * <T::Balance as As<usize>>::sa(encoded_len);
|
||||||
assert!(b >= transaction_fee, "attempt to transact without enough funds to pay fee");
|
if b < transaction_fee {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
<FreeBalance<T>>::insert(transactor, b - transaction_fee);
|
<FreeBalance<T>>::insert(transactor, b - transaction_fee);
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -628,7 +654,8 @@ pub struct GenesisConfig<T: Trait> {
|
|||||||
pub intentions: Vec<T::AccountId>,
|
pub intentions: Vec<T::AccountId>,
|
||||||
pub validator_count: u64,
|
pub validator_count: u64,
|
||||||
pub bonding_duration: T::BlockNumber,
|
pub bonding_duration: T::BlockNumber,
|
||||||
pub transaction_fee: T::Balance,
|
pub transaction_base_fee: T::Balance,
|
||||||
|
pub transaction_byte_fee: T::Balance,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "std", test))]
|
#[cfg(any(feature = "std", test))]
|
||||||
@@ -642,7 +669,8 @@ impl<T: Trait> GenesisConfig<T> where T::AccountId: From<u64> {
|
|||||||
intentions: vec![T::AccountId::from(1), T::AccountId::from(2), T::AccountId::from(3)],
|
intentions: vec![T::AccountId::from(1), T::AccountId::from(2), T::AccountId::from(3)],
|
||||||
validator_count: 3,
|
validator_count: 3,
|
||||||
bonding_duration: T::BlockNumber::sa(0),
|
bonding_duration: T::BlockNumber::sa(0),
|
||||||
transaction_fee: T::Balance::sa(0),
|
transaction_base_fee: T::Balance::sa(0),
|
||||||
|
transaction_byte_fee: T::Balance::sa(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -663,7 +691,8 @@ impl<T: Trait> GenesisConfig<T> where T::AccountId: From<u64> {
|
|||||||
intentions: vec![T::AccountId::from(1), T::AccountId::from(2), T::AccountId::from(3)],
|
intentions: vec![T::AccountId::from(1), T::AccountId::from(2), T::AccountId::from(3)],
|
||||||
validator_count: 3,
|
validator_count: 3,
|
||||||
bonding_duration: T::BlockNumber::sa(0),
|
bonding_duration: T::BlockNumber::sa(0),
|
||||||
transaction_fee: T::Balance::sa(1),
|
transaction_base_fee: T::Balance::sa(1),
|
||||||
|
transaction_byte_fee: T::Balance::sa(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -679,7 +708,8 @@ impl<T: Trait> Default for GenesisConfig<T> {
|
|||||||
intentions: vec![],
|
intentions: vec![],
|
||||||
validator_count: 0,
|
validator_count: 0,
|
||||||
bonding_duration: T::BlockNumber::sa(1000),
|
bonding_duration: T::BlockNumber::sa(1000),
|
||||||
transaction_fee: T::Balance::sa(0),
|
transaction_base_fee: T::Balance::sa(0),
|
||||||
|
transaction_byte_fee: T::Balance::sa(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -697,7 +727,8 @@ impl<T: Trait> primitives::BuildExternalities for GenesisConfig<T> {
|
|||||||
twox_128(<SessionsPerEra<T>>::key()).to_vec() => self.sessions_per_era.encode(),
|
twox_128(<SessionsPerEra<T>>::key()).to_vec() => self.sessions_per_era.encode(),
|
||||||
twox_128(<ValidatorCount<T>>::key()).to_vec() => self.validator_count.encode(),
|
twox_128(<ValidatorCount<T>>::key()).to_vec() => self.validator_count.encode(),
|
||||||
twox_128(<BondingDuration<T>>::key()).to_vec() => self.bonding_duration.encode(),
|
twox_128(<BondingDuration<T>>::key()).to_vec() => self.bonding_duration.encode(),
|
||||||
twox_128(<TransactionFee<T>>::key()).to_vec() => self.transaction_fee.encode(),
|
twox_128(<TransactionBaseFee<T>>::key()).to_vec() => self.transaction_base_fee.encode(),
|
||||||
|
twox_128(<TransactionByteFee<T>>::key()).to_vec() => self.transaction_byte_fee.encode(),
|
||||||
twox_128(<CurrentEra<T>>::key()).to_vec() => self.current_era.encode(),
|
twox_128(<CurrentEra<T>>::key()).to_vec() => self.current_era.encode(),
|
||||||
twox_128(<TotalStake<T>>::key()).to_vec() => total_stake.encode()
|
twox_128(<TotalStake<T>>::key()).to_vec() => total_stake.encode()
|
||||||
];
|
];
|
||||||
@@ -854,12 +885,12 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
|
||||||
fn staking_balance_transfer_when_bonded_panics() {
|
fn staking_balance_transfer_when_bonded_panics() {
|
||||||
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
|
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
|
||||||
<FreeBalance<Test>>::insert(1, 111);
|
<FreeBalance<Test>>::insert(1, 111);
|
||||||
Staking::stake(&1);
|
Staking::stake(&1);
|
||||||
Staking::transfer(&1, 2, 69);
|
Staking::transfer(&1, 2, 69);
|
||||||
|
assert_eq!(Staking::balance(&1), 111);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -880,13 +911,13 @@ mod tests {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn staking_balance_transfer_when_reserved_panics() {
|
fn staking_balance_transfer_when_reserved_panics() {
|
||||||
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
|
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
|
||||||
<FreeBalance<Test>>::insert(1, 111);
|
<FreeBalance<Test>>::insert(1, 111);
|
||||||
Staking::reserve_balance(&1, 69);
|
Staking::reserve_balance(&1, 69);
|
||||||
Staking::transfer(&1, 2, 69);
|
Staking::transfer(&1, 2, 69);
|
||||||
|
assert_eq!(Staking::balance(&1), 111);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,8 @@ pub fn new_test_ext(session_length: u64, sessions_per_era: u64, current_era: u64
|
|||||||
intentions: vec![],
|
intentions: vec![],
|
||||||
validator_count: 2,
|
validator_count: 2,
|
||||||
bonding_duration: 3,
|
bonding_duration: 3,
|
||||||
transaction_fee: 0,
|
transaction_base_fee: 0,
|
||||||
|
transaction_byte_fee: 0,
|
||||||
}.build_externalities());
|
}.build_externalities());
|
||||||
t
|
t
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
Reference in New Issue
Block a user