mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 03:01:07 +00:00
Separate out staking module into balances and payment (#629)
* Initial commit. * Split out balances module * Minimise Balances trait requirements * Fix up balances, remove balances stuff from staking * Split off and fix up staking module * Fix executive tests * Fix up democracy module * make council work again * Remove unneeded cruft from democracy * Fix up contract module * Fix up rest of tests * Fix minor TODOs * Fix tests * Remove superfluous code * Move offline inherents to consensus module. Fixes #630 * Version needs Decode. * Move Decode back * Fix nits * Refactor to allow custom message
This commit is contained in:
Generated
+26
-10
@@ -451,6 +451,7 @@ dependencies = [
|
||||
"substrate-executor 0.1.0",
|
||||
"substrate-keyring 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-balances 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
@@ -491,6 +492,7 @@ dependencies = [
|
||||
"substrate-codec-derive 0.1.0",
|
||||
"substrate-keyring 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-balances 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-council 0.1.0",
|
||||
"substrate-runtime-democracy 0.1.0",
|
||||
@@ -2655,6 +2657,25 @@ dependencies = [
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-balances"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-codec-derive 0.1.0",
|
||||
"substrate-keyring 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-consensus"
|
||||
version = "0.1.0"
|
||||
@@ -2682,16 +2703,13 @@ dependencies = [
|
||||
"serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-balances 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-sandbox 0.1.0",
|
||||
"substrate-runtime-session 0.1.0",
|
||||
"substrate-runtime-staking 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
"substrate-runtime-timestamp 0.1.0",
|
||||
"wabt 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -2707,16 +2725,14 @@ dependencies = [
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-keyring 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-balances 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-democracy 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-session 0.1.0",
|
||||
"substrate-runtime-staking 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
"substrate-runtime-timestamp 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2730,15 +2746,13 @@ dependencies = [
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-codec-derive 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-balances 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-session 0.1.0",
|
||||
"substrate-runtime-staking 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
"substrate-runtime-timestamp 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2751,6 +2765,7 @@ dependencies = [
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-codec-derive 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-balances 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
@@ -2841,6 +2856,7 @@ dependencies = [
|
||||
"substrate-codec-derive 0.1.0",
|
||||
"substrate-keyring 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-balances 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
|
||||
@@ -19,6 +19,7 @@ members = [
|
||||
"substrate/runtime-sandbox",
|
||||
"substrate/runtime-std",
|
||||
"substrate/runtime-support",
|
||||
"substrate/runtime/balances",
|
||||
"substrate/runtime/consensus",
|
||||
"substrate/runtime/contract",
|
||||
"substrate/runtime/council",
|
||||
|
||||
@@ -50,8 +50,8 @@ pub mod error;
|
||||
use std::sync::Arc;
|
||||
use demo_primitives::{AccountId, Hash};
|
||||
use demo_runtime::{Block, BlockId, GenesisConfig,
|
||||
ConsensusConfig, CouncilConfig, DemocracyConfig, SessionConfig, StakingConfig,
|
||||
TimestampConfig};
|
||||
BalancesConfig, ConsensusConfig, CouncilConfig, DemocracyConfig, SessionConfig,
|
||||
StakingConfig, TimestampConfig};
|
||||
use futures::{Future, Sink, Stream};
|
||||
use tokio::runtime::Runtime;
|
||||
use demo_executor::NativeExecutor;
|
||||
@@ -164,13 +164,7 @@ pub fn run<I, T>(args: I) -> error::Result<()> where
|
||||
authorities: vec![god_key.clone().into()],
|
||||
}),
|
||||
system: None,
|
||||
session: Some(SessionConfig {
|
||||
validators: vec![god_key.clone().into()],
|
||||
session_length: 720, // that's 1 hour per session.
|
||||
}),
|
||||
staking: Some(StakingConfig {
|
||||
current_era: 0,
|
||||
intentions: vec![],
|
||||
balances: Some(BalancesConfig {
|
||||
transaction_base_fee: 100,
|
||||
transaction_byte_fee: 1,
|
||||
transfer_fee: 0,
|
||||
@@ -178,10 +172,18 @@ pub fn run<I, T>(args: I) -> error::Result<()> where
|
||||
reclaim_rebate: 0,
|
||||
existential_deposit: 500,
|
||||
balances: vec![(god_key.clone().into(), 1u64 << 63)].into_iter().collect(),
|
||||
}),
|
||||
session: Some(SessionConfig {
|
||||
validators: vec![god_key.clone().into()],
|
||||
session_length: 720, // that's 1 hour per session.
|
||||
}),
|
||||
staking: Some(StakingConfig {
|
||||
current_era: 0,
|
||||
intentions: vec![],
|
||||
validator_count: 12,
|
||||
minimum_validator_count: 4,
|
||||
sessions_per_era: 24, // 24 hours per era.
|
||||
bonding_duration: 90, // 90 days per bond.
|
||||
bonding_duration: 90 * 24 * 720, // 90 days per bond.
|
||||
early_era_slash: 10000,
|
||||
session_reward: 100,
|
||||
offline_slash_grace: 0,
|
||||
|
||||
@@ -20,6 +20,7 @@ demo-runtime = { path = "../runtime" }
|
||||
[dev-dependencies]
|
||||
substrate-keyring = { path = "../../substrate/keyring" }
|
||||
substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" }
|
||||
substrate-runtime-balances = { path = "../../substrate/runtime/balances" }
|
||||
substrate-runtime-session = { path = "../../substrate/runtime/session" }
|
||||
substrate-runtime-staking = { path = "../../substrate/runtime/staking" }
|
||||
substrate-runtime-system = { path = "../../substrate/runtime/system" }
|
||||
|
||||
@@ -30,6 +30,7 @@ extern crate triehash;
|
||||
#[cfg(test)] extern crate substrate_keyring as keyring;
|
||||
#[cfg(test)] extern crate substrate_runtime_primitives as runtime_primitives;
|
||||
#[cfg(test)] extern crate substrate_runtime_support as runtime_support;
|
||||
#[cfg(test)] extern crate substrate_runtime_balances as balances;
|
||||
#[cfg(test)] extern crate substrate_runtime_session as session;
|
||||
#[cfg(test)] extern crate substrate_runtime_staking as staking;
|
||||
#[cfg(test)] extern crate substrate_runtime_system as system;
|
||||
@@ -52,10 +53,10 @@ mod tests {
|
||||
use demo_primitives::{Hash, BlockNumber, AccountId};
|
||||
use runtime_primitives::traits::Header as HeaderT;
|
||||
use runtime_primitives::{ApplyOutcome, ApplyError, ApplyResult, MaybeUnsigned};
|
||||
use {staking, session, system, consensus};
|
||||
use {balances, staking, session, system, consensus};
|
||||
use system::{EventRecord, Phase};
|
||||
use demo_runtime::{Header, Block, UncheckedExtrinsic, Extrinsic, Call, Concrete, Staking,
|
||||
BuildStorage, GenesisConfig, SessionConfig, StakingConfig, BareExtrinsic, System, Event};
|
||||
use demo_runtime::{Header, Block, UncheckedExtrinsic, Extrinsic, Call, Concrete, Balances,
|
||||
BuildStorage, GenesisConfig, BalancesConfig, SessionConfig, StakingConfig, BareExtrinsic, System, Event};
|
||||
use ed25519::{Public, Pair};
|
||||
|
||||
const BLOATY_CODE: &[u8] = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm");
|
||||
@@ -80,7 +81,7 @@ mod tests {
|
||||
let extrinsic = BareExtrinsic {
|
||||
signed: alice(),
|
||||
index: 0,
|
||||
function: Call::Staking(staking::Call::transfer::<Concrete>(bob().into(), 69)),
|
||||
function: Call::Balances(balances::Call::transfer::<Concrete>(bob().into(), 69)),
|
||||
};
|
||||
let signature = MaybeUnsigned(Keyring::from_raw_public(extrinsic.signed.0.clone()).unwrap()
|
||||
.sign(&extrinsic.encode()).into());
|
||||
@@ -103,13 +104,13 @@ mod tests {
|
||||
#[test]
|
||||
fn panic_execution_with_foreign_code_gives_error() {
|
||||
let mut t: TestExternalities<KeccakHasher> = map![
|
||||
twox_128(&<staking::FreeBalance<Concrete>>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TotalStake<Concrete>>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![70u8; 8],
|
||||
twox_128(<staking::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::ExistentialDeposit<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::TransferFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::NextEnumSet<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<balances::FreeBalance<Concrete>>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<balances::TotalIssuance<Concrete>>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<balances::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![70u8; 8],
|
||||
twox_128(<balances::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<balances::ExistentialDeposit<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<balances::TransferFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<balances::NextEnumSet<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
||||
];
|
||||
|
||||
@@ -123,13 +124,13 @@ mod tests {
|
||||
#[test]
|
||||
fn bad_extrinsic_with_native_equivalent_code_gives_error() {
|
||||
let mut t: TestExternalities<KeccakHasher> = map![
|
||||
twox_128(&<staking::FreeBalance<Concrete>>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TotalStake<Concrete>>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![70u8; 8],
|
||||
twox_128(<staking::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::ExistentialDeposit<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::TransferFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::NextEnumSet<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<balances::FreeBalance<Concrete>>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<balances::TotalIssuance<Concrete>>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<balances::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![70u8; 8],
|
||||
twox_128(<balances::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<balances::ExistentialDeposit<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<balances::TransferFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<balances::NextEnumSet<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
||||
];
|
||||
|
||||
@@ -143,13 +144,13 @@ mod tests {
|
||||
#[test]
|
||||
fn successful_execution_with_native_equivalent_code_gives_ok() {
|
||||
let mut t: TestExternalities<KeccakHasher> = map![
|
||||
twox_128(&<staking::FreeBalance<Concrete>>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TotalStake<Concrete>>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::ExistentialDeposit<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::TransferFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::NextEnumSet<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<balances::FreeBalance<Concrete>>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<balances::TotalIssuance<Concrete>>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<balances::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<balances::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<balances::ExistentialDeposit<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<balances::TransferFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<balances::NextEnumSet<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
||||
];
|
||||
|
||||
@@ -159,21 +160,21 @@ mod tests {
|
||||
assert!(r.is_ok());
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(Staking::voting_balance(&alice()), 42);
|
||||
assert_eq!(Staking::voting_balance(&bob()), 69);
|
||||
assert_eq!(Balances::total_balance(&alice()), 42);
|
||||
assert_eq!(Balances::total_balance(&bob()), 69);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn successful_execution_with_foreign_code_gives_ok() {
|
||||
let mut t: TestExternalities<KeccakHasher> = map![
|
||||
twox_128(&<staking::FreeBalance<Concrete>>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TotalStake<Concrete>>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::ExistentialDeposit<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::TransferFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::NextEnumSet<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<balances::FreeBalance<Concrete>>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<balances::TotalIssuance<Concrete>>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<balances::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<balances::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<balances::ExistentialDeposit<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<balances::TransferFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<balances::NextEnumSet<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
||||
];
|
||||
|
||||
@@ -183,8 +184,8 @@ mod tests {
|
||||
assert!(r.is_ok());
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(Staking::voting_balance(&alice()), 42);
|
||||
assert_eq!(Staking::voting_balance(&bob()), 69);
|
||||
assert_eq!(Balances::total_balance(&alice()), 42);
|
||||
assert_eq!(Balances::total_balance(&bob()), 69);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -194,6 +195,15 @@ mod tests {
|
||||
GenesisConfig {
|
||||
consensus: Some(Default::default()),
|
||||
system: Some(Default::default()),
|
||||
balances: Some(BalancesConfig {
|
||||
balances: vec![(alice(), 111)],
|
||||
transaction_base_fee: 1,
|
||||
transaction_byte_fee: 0,
|
||||
existential_deposit: 0,
|
||||
transfer_fee: 0,
|
||||
creation_fee: 0,
|
||||
reclaim_rebate: 0,
|
||||
}),
|
||||
session: Some(SessionConfig {
|
||||
session_length: 2,
|
||||
validators: vec![One.to_raw_public().into(), Two.to_raw_public().into(), three],
|
||||
@@ -201,17 +211,10 @@ mod tests {
|
||||
staking: Some(StakingConfig {
|
||||
sessions_per_era: 2,
|
||||
current_era: 0,
|
||||
balances: vec![(alice(), 111)],
|
||||
intentions: vec![alice(), bob(), Charlie.to_raw_public().into()],
|
||||
validator_count: 3,
|
||||
minimum_validator_count: 0,
|
||||
bonding_duration: 0,
|
||||
transaction_base_fee: 1,
|
||||
transaction_byte_fee: 0,
|
||||
existential_deposit: 0,
|
||||
transfer_fee: 0,
|
||||
creation_fee: 0,
|
||||
reclaim_rebate: 0,
|
||||
early_era_slash: 0,
|
||||
session_reward: 0,
|
||||
offline_slash_grace: 0,
|
||||
@@ -257,11 +260,11 @@ mod tests {
|
||||
// Blake
|
||||
// hex!("3437bf4b182ab17bb322af5c67e55f6be487a77084ad2b4e27ddac7242e4ad21").into(),
|
||||
// Keccak
|
||||
hex!("0b401681b95d04e91dbe53835867bdcb5d9e0590b54ae06bd7b347d49f9a737f").into(),
|
||||
hex!("a3f5ce86e303f4001d14124ab690428d10cd9e60d21699b42096358c2422445f").into(),
|
||||
vec![BareExtrinsic {
|
||||
signed: alice(),
|
||||
index: 0,
|
||||
function: Call::Staking(staking::Call::transfer(bob().into(), 69)),
|
||||
function: Call::Balances(balances::Call::transfer(bob().into(), 69)),
|
||||
}]
|
||||
)
|
||||
}
|
||||
@@ -273,17 +276,17 @@ mod tests {
|
||||
// Blake
|
||||
// hex!("741fcb660e6fa9f625fbcd993b49f6c1cc4040f5e0cc8727afdedf11fd3c464b").into(),
|
||||
// Keccak
|
||||
hex!("03f051dc4f588fdc713145772486a129d33c7f178c133b5801fa79c3ecca2dc9").into(),
|
||||
hex!("72dc147d2619a978adc38a38abc85bb77e25b0095ad38b15f97d56ccb66f36e8").into(),
|
||||
vec![
|
||||
BareExtrinsic {
|
||||
signed: bob(),
|
||||
index: 0,
|
||||
function: Call::Staking(staking::Call::transfer(alice().into(), 5)),
|
||||
function: Call::Balances(balances::Call::transfer(alice().into(), 5)),
|
||||
},
|
||||
BareExtrinsic {
|
||||
signed: alice(),
|
||||
index: 1,
|
||||
function: Call::Staking(staking::Call::transfer(bob().into(), 15)),
|
||||
function: Call::Balances(balances::Call::transfer(bob().into(), 15)),
|
||||
}
|
||||
]
|
||||
)
|
||||
@@ -296,7 +299,7 @@ mod tests {
|
||||
// Blake
|
||||
// hex!("2c7231a9c210a7aa4bea169d944bc4aaacd517862b244b8021236ffa7f697991").into(),
|
||||
// Keccak
|
||||
hex!("6e3b6aaf0be927394b520e3ebc0c34a7c26519711bc836e116e371273c3aca44").into(),
|
||||
hex!("7aa14ff631321ca5aa22e6fa53e3569faa732758993fa82e2dbde31a1b720391").into(),
|
||||
vec![BareExtrinsic {
|
||||
signed: alice(),
|
||||
index: 0,
|
||||
@@ -312,12 +315,12 @@ mod tests {
|
||||
executor().call(&mut t, COMPACT_CODE, "execute_block", &block1().0, true).0.unwrap();
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(Staking::voting_balance(&alice()), 41);
|
||||
assert_eq!(Staking::voting_balance(&bob()), 69);
|
||||
assert_eq!(Balances::total_balance(&alice()), 41);
|
||||
assert_eq!(Balances::total_balance(&bob()), 69);
|
||||
assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: Event::staking(staking::RawEvent::NewAccount(bob(), 1, staking::NewAccountOutcome::NoHint))
|
||||
event: Event::balances(balances::RawEvent::NewAccount(bob(), 1, balances::NewAccountOutcome::NoHint))
|
||||
}
|
||||
]);
|
||||
});
|
||||
@@ -325,8 +328,8 @@ mod tests {
|
||||
executor().call(&mut t, COMPACT_CODE, "execute_block", &block2().0, true).0.unwrap();
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(Staking::voting_balance(&alice()), 30);
|
||||
assert_eq!(Staking::voting_balance(&bob()), 78);
|
||||
assert_eq!(Balances::total_balance(&alice()), 30);
|
||||
assert_eq!(Balances::total_balance(&bob()), 78);
|
||||
assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::Finalization,
|
||||
@@ -347,15 +350,15 @@ mod tests {
|
||||
WasmExecutor::new(8).call(&mut t, COMPACT_CODE, "execute_block", &block1().0).unwrap();
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(Staking::voting_balance(&alice()), 41);
|
||||
assert_eq!(Staking::voting_balance(&bob()), 69);
|
||||
assert_eq!(Balances::total_balance(&alice()), 41);
|
||||
assert_eq!(Balances::total_balance(&bob()), 69);
|
||||
});
|
||||
|
||||
WasmExecutor::new(8).call(&mut t, COMPACT_CODE, "execute_block", &block2().0).unwrap();
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(Staking::voting_balance(&alice()), 30);
|
||||
assert_eq!(Staking::voting_balance(&bob()), 78);
|
||||
assert_eq!(Balances::total_balance(&alice()), 30);
|
||||
assert_eq!(Balances::total_balance(&bob()), 78);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -386,13 +389,13 @@ mod tests {
|
||||
#[test]
|
||||
fn panic_execution_gives_error() {
|
||||
let mut t: TestExternalities<KeccakHasher> = map![
|
||||
twox_128(&<staking::FreeBalance<Concrete>>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TotalStake<Concrete>>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![70u8; 8],
|
||||
twox_128(<staking::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::ExistentialDeposit<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::TransferFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::NextEnumSet<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<balances::FreeBalance<Concrete>>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<balances::TotalIssuance<Concrete>>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<balances::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![70u8; 8],
|
||||
twox_128(<balances::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<balances::ExistentialDeposit<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<balances::TransferFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<balances::NextEnumSet<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
||||
];
|
||||
|
||||
@@ -407,13 +410,13 @@ mod tests {
|
||||
#[test]
|
||||
fn successful_execution_gives_ok() {
|
||||
let mut t: TestExternalities<KeccakHasher> = map![
|
||||
twox_128(&<staking::FreeBalance<Concrete>>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TotalStake<Concrete>>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<staking::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::ExistentialDeposit<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::TransferFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<staking::NextEnumSet<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<balances::FreeBalance<Concrete>>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<balances::TotalIssuance<Concrete>>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(<balances::TransactionBaseFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<balances::TransactionByteFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<balances::ExistentialDeposit<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<balances::TransferFee<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(<balances::NextEnumSet<Concrete>>::key()).to_vec() => vec![0u8; 8],
|
||||
twox_128(&<system::BlockHash<Concrete>>::key_for(0)).to_vec() => vec![0u8; 32]
|
||||
];
|
||||
|
||||
@@ -425,8 +428,8 @@ mod tests {
|
||||
assert_eq!(r, Ok(ApplyOutcome::Success));
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(Staking::voting_balance(&alice()), 42);
|
||||
assert_eq!(Staking::voting_balance(&bob()), 69);
|
||||
assert_eq!(Balances::total_balance(&alice()), 42);
|
||||
assert_eq!(Balances::total_balance(&bob()), 69);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ substrate-runtime-io = { path = "../../substrate/runtime-io" }
|
||||
substrate-runtime-support = { path = "../../substrate/runtime-support" }
|
||||
substrate-primitives = { path = "../../substrate/primitives" }
|
||||
substrate-keyring = { path = "../../substrate/keyring" }
|
||||
substrate-runtime-balances = { path = "../../substrate/runtime/balances" }
|
||||
substrate-runtime-consensus = { path = "../../substrate/runtime/consensus" }
|
||||
substrate-runtime-council = { path = "../../substrate/runtime/council" }
|
||||
substrate-runtime-democracy = { path = "../../substrate/runtime/democracy" }
|
||||
@@ -37,6 +38,7 @@ std = [
|
||||
"substrate-runtime-std/std",
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-runtime-support/std",
|
||||
"substrate-runtime-balances/std",
|
||||
"substrate-runtime-consensus/std",
|
||||
"substrate-runtime-council/std",
|
||||
"substrate-runtime-democracy/std",
|
||||
|
||||
@@ -40,6 +40,7 @@ extern crate substrate_codec as codec;
|
||||
extern crate substrate_codec_derive;
|
||||
|
||||
extern crate substrate_runtime_std as rstd;
|
||||
extern crate substrate_runtime_balances as balances;
|
||||
extern crate substrate_runtime_consensus as consensus;
|
||||
extern crate substrate_runtime_council as council;
|
||||
extern crate substrate_runtime_democracy as democracy;
|
||||
@@ -88,6 +89,7 @@ impl HasPublicAux for Concrete {
|
||||
}
|
||||
|
||||
impl system::Trait for Concrete {
|
||||
type PublicAux = <Self as HasPublicAux>::PublicAux;
|
||||
type Index = Index;
|
||||
type BlockNumber = BlockNumber;
|
||||
type Hash = Hash;
|
||||
@@ -101,9 +103,21 @@ impl system::Trait for Concrete {
|
||||
/// System module for this concrete runtime.
|
||||
pub type System = system::Module<Concrete>;
|
||||
|
||||
impl balances::Trait for Concrete {
|
||||
type Balance = Balance;
|
||||
type AccountIndex = AccountIndex;
|
||||
type OnFreeBalanceZero = Staking;
|
||||
type EnsureAccountLiquid = Staking;
|
||||
type Event = Event;
|
||||
}
|
||||
|
||||
/// Staking module for this concrete runtime.
|
||||
pub type Balances = balances::Module<Concrete>;
|
||||
|
||||
impl consensus::Trait for Concrete {
|
||||
type PublicAux = <Self as HasPublicAux>::PublicAux;
|
||||
const NOTE_OFFLINE_POSITION: u32 = 1;
|
||||
type SessionKey = SessionKey;
|
||||
type OnOfflineValidator = Staking;
|
||||
}
|
||||
|
||||
/// Consensus module for this concrete runtime.
|
||||
@@ -136,10 +150,6 @@ impl session::Trait for Concrete {
|
||||
pub type Session = session::Module<Concrete>;
|
||||
|
||||
impl staking::Trait for Concrete {
|
||||
const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1;
|
||||
type Balance = Balance;
|
||||
type AccountIndex = AccountIndex;
|
||||
type OnFreeBalanceZero = ();
|
||||
type Event = Event;
|
||||
}
|
||||
|
||||
@@ -162,7 +172,7 @@ pub type CouncilVoting = council::voting::Module<Concrete>;
|
||||
|
||||
impl_outer_event! {
|
||||
pub enum Event for Concrete {
|
||||
session, staking
|
||||
balances, session, staking
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,9 +181,10 @@ impl_outer_dispatch! {
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
pub enum Call where aux: <Concrete as HasPublicAux>::PublicAux {
|
||||
Consensus = 0,
|
||||
Session = 1,
|
||||
Staking = 2,
|
||||
Timestamp = 3,
|
||||
Balances = 1,
|
||||
Session = 2,
|
||||
Staking = 3,
|
||||
Timestamp = 4,
|
||||
Democracy = 5,
|
||||
Council = 6,
|
||||
CouncilVoting = 7,
|
||||
@@ -183,16 +194,17 @@ impl_outer_dispatch! {
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
pub enum PrivCall {
|
||||
Consensus = 0,
|
||||
Session = 1,
|
||||
Staking = 2,
|
||||
Democracy = 5,
|
||||
Council = 6,
|
||||
CouncilVoting = 7,
|
||||
Balances = 1,
|
||||
Session = 2,
|
||||
Staking = 3,
|
||||
Democracy = 4,
|
||||
Council = 5,
|
||||
CouncilVoting = 6,
|
||||
}
|
||||
}
|
||||
|
||||
/// The address format for describing accounts.
|
||||
pub type Address = staking::Address<Concrete>;
|
||||
pub type Address = balances::Address<Concrete>;
|
||||
/// Block header type as expected by this runtime.
|
||||
pub type Header = generic::Header<BlockNumber, BlakeTwo256, Vec<u8>>;
|
||||
/// Block type as expected by this runtime.
|
||||
@@ -206,13 +218,14 @@ pub type Extrinsic = generic::Extrinsic<Address, Index, Call>;
|
||||
/// Extrinsic type that is signed.
|
||||
pub type BareExtrinsic = generic::Extrinsic<AccountId, Index, Call>;
|
||||
/// Executive: handles dispatch to the various modules.
|
||||
pub type Executive = executive::Executive<Concrete, Block, Staking, Staking,
|
||||
pub type Executive = executive::Executive<Concrete, Block, Balances, Balances,
|
||||
(((((), Council), Democracy), Staking), Session)>;
|
||||
|
||||
impl_outer_config! {
|
||||
pub struct GenesisConfig for Concrete {
|
||||
ConsensusConfig => consensus,
|
||||
SystemConfig => system,
|
||||
BalancesConfig => balances,
|
||||
SessionConfig => session,
|
||||
StakingConfig => staking,
|
||||
DemocracyConfig => democracy,
|
||||
|
||||
Generated
+27
-4
@@ -103,6 +103,7 @@ dependencies = [
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-codec-derive 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-balances 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-council 0.1.0",
|
||||
"substrate-runtime-democracy 0.1.0",
|
||||
@@ -639,6 +640,29 @@ dependencies = [
|
||||
"wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-balances"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-codec-derive 0.1.0",
|
||||
"substrate-keyring 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-sandbox 0.1.0",
|
||||
"substrate-runtime-session 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
"substrate-runtime-timestamp 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-consensus"
|
||||
version = "0.1.0"
|
||||
@@ -667,12 +691,11 @@ dependencies = [
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-keyring 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-balances 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-democracy 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-session 0.1.0",
|
||||
"substrate-runtime-staking 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
@@ -689,11 +712,10 @@ dependencies = [
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-codec-derive 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-balances 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-session 0.1.0",
|
||||
"substrate-runtime-staking 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
@@ -791,6 +813,7 @@ dependencies = [
|
||||
"substrate-codec-derive 0.1.0",
|
||||
"substrate-keyring 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-balances 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
|
||||
@@ -15,6 +15,7 @@ substrate-primitives = { path = "../../../substrate/primitives", default-feature
|
||||
substrate-runtime-std = { path = "../../../substrate/runtime-std", default-features = false }
|
||||
substrate-runtime-io = { path = "../../../substrate/runtime-io", default-features = false }
|
||||
substrate-runtime-support = { path = "../../../substrate/runtime-support", default-features = false }
|
||||
substrate-runtime-balances = { path = "../../../substrate/runtime/balances", default-features = false }
|
||||
substrate-runtime-consensus = { path = "../../../substrate/runtime/consensus", default-features = false }
|
||||
substrate-runtime-council = { path = "../../../substrate/runtime/council", default-features = false }
|
||||
substrate-runtime-democracy = { path = "../../../substrate/runtime/democracy", default-features = false }
|
||||
@@ -36,6 +37,7 @@ std = [
|
||||
"substrate-runtime-std/std",
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-runtime-support/std",
|
||||
"substrate-runtime-balances/std",
|
||||
"substrate-runtime-consensus/std",
|
||||
"substrate-runtime-council/std",
|
||||
"substrate-runtime-democracy/std",
|
||||
|
||||
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -1,18 +1,18 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate Demo is free software: you can redistribute it and/or modify
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate Demo is distributed in the hope that it will be useful,
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Dispatch system. Just dispatches calls.
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
[package]
|
||||
name = "substrate-runtime-balances"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
hex-literal = "0.1.0"
|
||||
serde = { version = "1.0", default_features = false }
|
||||
serde_derive = { version = "1.0", optional = true }
|
||||
safe-mix = { version = "1.0", default_features = false}
|
||||
substrate-keyring = { path = "../../keyring", optional = true }
|
||||
substrate-codec = { path = "../../codec", default_features = false }
|
||||
substrate-codec-derive = { path = "../../codec/derive", default_features = false }
|
||||
substrate-primitives = { path = "../../primitives", default_features = false }
|
||||
substrate-runtime-std = { path = "../../runtime-std", default_features = false }
|
||||
substrate-runtime-io = { path = "../../runtime-io", default_features = false }
|
||||
substrate-runtime-support = { path = "../../runtime-support", default_features = false }
|
||||
substrate-runtime-primitives = { path = "../primitives", default_features = false }
|
||||
substrate-runtime-system = { path = "../system", default_features = false }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"serde/std",
|
||||
"serde_derive",
|
||||
"safe-mix/std",
|
||||
"substrate-keyring",
|
||||
"substrate-codec/std",
|
||||
"substrate-codec-derive/std",
|
||||
"substrate-primitives/std",
|
||||
"substrate-runtime-std/std",
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-runtime-support/std",
|
||||
"substrate-runtime-primitives/std",
|
||||
"substrate-runtime-system/std",
|
||||
]
|
||||
+4
-4
@@ -1,18 +1,18 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate Demo is free software: you can redistribute it and/or modify
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate Demo is distributed in the hope that it will be useful,
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Address type that is union of index and id for an account.
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Build a balances genesis block.
|
||||
|
||||
#![cfg(feature = "std")]
|
||||
|
||||
use std::collections::HashMap;
|
||||
use rstd::prelude::*;
|
||||
use codec::Encode;
|
||||
use runtime_support::{StorageValue, StorageMap};
|
||||
use primitives::traits::{Zero, As};
|
||||
use substrate_primitives::KeccakHasher;
|
||||
use {runtime_io, primitives};
|
||||
use super::{Trait, ENUM_SET_SIZE, EnumSet, NextEnumSet, CreationFee, TransferFee,
|
||||
ReclaimRebate, ExistentialDeposit, TransactionByteFee, TransactionBaseFee, TotalIssuance,
|
||||
FreeBalance};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct GenesisConfig<T: Trait> {
|
||||
pub balances: Vec<(T::AccountId, T::Balance)>,
|
||||
pub transaction_base_fee: T::Balance,
|
||||
pub transaction_byte_fee: T::Balance,
|
||||
pub transfer_fee: T::Balance,
|
||||
pub creation_fee: T::Balance,
|
||||
pub reclaim_rebate: T::Balance,
|
||||
pub existential_deposit: T::Balance,
|
||||
}
|
||||
|
||||
impl<T: Trait> Default for GenesisConfig<T> {
|
||||
fn default() -> Self {
|
||||
GenesisConfig {
|
||||
balances: vec![],
|
||||
transaction_base_fee: T::Balance::sa(0),
|
||||
transaction_byte_fee: T::Balance::sa(0),
|
||||
transfer_fee: T::Balance::sa(0),
|
||||
creation_fee: T::Balance::sa(0),
|
||||
existential_deposit: T::Balance::sa(0),
|
||||
reclaim_rebate: T::Balance::sa(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> primitives::BuildStorage for GenesisConfig<T> {
|
||||
fn build_storage(self) -> ::std::result::Result<HashMap<Vec<u8>, Vec<u8>>, String> {
|
||||
let total_stake: T::Balance = self.balances.iter().fold(Zero::zero(), |acc, &(_, n)| acc + n);
|
||||
|
||||
let mut r: runtime_io::TestExternalities<KeccakHasher> = map![
|
||||
Self::hash(<NextEnumSet<T>>::key()).to_vec() => T::AccountIndex::sa(self.balances.len() / ENUM_SET_SIZE).encode(),
|
||||
Self::hash(<TransactionBaseFee<T>>::key()).to_vec() => self.transaction_base_fee.encode(),
|
||||
Self::hash(<TransactionByteFee<T>>::key()).to_vec() => self.transaction_byte_fee.encode(),
|
||||
Self::hash(<TransferFee<T>>::key()).to_vec() => self.transfer_fee.encode(),
|
||||
Self::hash(<CreationFee<T>>::key()).to_vec() => self.creation_fee.encode(),
|
||||
Self::hash(<ExistentialDeposit<T>>::key()).to_vec() => self.existential_deposit.encode(),
|
||||
Self::hash(<ReclaimRebate<T>>::key()).to_vec() => self.reclaim_rebate.encode(),
|
||||
Self::hash(<TotalIssuance<T>>::key()).to_vec() => total_stake.encode()
|
||||
];
|
||||
|
||||
let ids: Vec<_> = self.balances.iter().map(|x| x.0.clone()).collect();
|
||||
for i in 0..(ids.len() + ENUM_SET_SIZE - 1) / ENUM_SET_SIZE {
|
||||
r.insert(Self::hash(&<EnumSet<T>>::key_for(T::AccountIndex::sa(i))).to_vec(),
|
||||
ids[i * ENUM_SET_SIZE..ids.len().min((i + 1) * ENUM_SET_SIZE)].to_owned().encode());
|
||||
}
|
||||
for (who, value) in self.balances.into_iter() {
|
||||
r.insert(Self::hash(&<FreeBalance<T>>::key_for(who)).to_vec(), value.encode());
|
||||
}
|
||||
Ok(r.into())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,651 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Balances: Handles balances.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
extern crate serde;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[macro_use]
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
|
||||
#[cfg_attr(feature = "std", macro_use)]
|
||||
extern crate substrate_runtime_std as rstd;
|
||||
|
||||
#[macro_use]
|
||||
extern crate substrate_codec_derive;
|
||||
|
||||
extern crate substrate_codec as codec;
|
||||
extern crate substrate_primitives;
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
extern crate substrate_runtime_primitives as primitives;
|
||||
extern crate substrate_runtime_system as system;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::{cmp, result};
|
||||
use codec::{Encode, Decode, Codec, Input, Output};
|
||||
use runtime_support::{StorageValue, StorageMap, Parameter};
|
||||
use runtime_support::dispatch::Result;
|
||||
use primitives::traits::{Zero, One, RefInto, SimpleArithmetic, Executable, MakePayment,
|
||||
As, AuxLookup, Member, CheckedAdd, CheckedSub};
|
||||
use address::Address as RawAddress;
|
||||
|
||||
mod mock;
|
||||
|
||||
pub mod address;
|
||||
mod tests;
|
||||
mod genesis_config;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub use genesis_config::GenesisConfig;
|
||||
|
||||
/// Number of account IDs stored per enum set.
|
||||
const ENUM_SET_SIZE: usize = 64;
|
||||
|
||||
/// The byte to identify intention to reclaim an existing account index.
|
||||
const RECLAIM_INDEX_MAGIC: usize = 0x69;
|
||||
|
||||
pub type Address<T> = RawAddress<<T as system::Trait>::AccountId, <T as Trait>::AccountIndex>;
|
||||
|
||||
pub type Event<T> = RawEvent<
|
||||
<T as system::Trait>::AccountId,
|
||||
<T as Trait>::AccountIndex
|
||||
>;
|
||||
|
||||
/// The account with the given id was killed.
|
||||
pub trait OnFreeBalanceZero<AccountId> {
|
||||
/// The account was the given id was killed.
|
||||
fn on_free_balance_zero(who: &AccountId);
|
||||
}
|
||||
|
||||
impl<AccountId> OnFreeBalanceZero<AccountId> for () {
|
||||
fn on_free_balance_zero(_who: &AccountId) {}
|
||||
}
|
||||
impl<
|
||||
AccountId,
|
||||
X: OnFreeBalanceZero<AccountId>,
|
||||
Y: OnFreeBalanceZero<AccountId>,
|
||||
> OnFreeBalanceZero<AccountId> for (X, Y) {
|
||||
fn on_free_balance_zero(who: &AccountId) {
|
||||
X::on_free_balance_zero(who);
|
||||
Y::on_free_balance_zero(who);
|
||||
}
|
||||
}
|
||||
|
||||
/// Determinator for whether a given account is able to transfer balance.
|
||||
pub trait EnsureAccountLiquid<AccountId> {
|
||||
/// Returns `Ok` iff the account is able to transfer funds normally. `Err(...)`
|
||||
/// with the reason why not otherwise.
|
||||
fn ensure_account_liquid(who: &AccountId) -> Result;
|
||||
}
|
||||
|
||||
impl<AccountId> EnsureAccountLiquid<AccountId> for () {
|
||||
fn ensure_account_liquid(_who: &AccountId) -> Result { Ok(()) }
|
||||
}
|
||||
|
||||
pub trait Trait: system::Trait {
|
||||
/// The balance of an account.
|
||||
type Balance: Parameter + SimpleArithmetic + Codec + Default + Copy + As<Self::AccountIndex> + As<usize> + As<u64>;
|
||||
/// Type used for storing an account's index; implies the maximum number of accounts the system
|
||||
/// can hold.
|
||||
type AccountIndex: Parameter + Member + Codec + SimpleArithmetic + As<u8> + As<u16> + As<u32> + As<u64> + As<usize> + Copy;
|
||||
/// A function which is invoked when the free-balance has fallen below the existential deposit and
|
||||
/// has been reduced to zero.
|
||||
///
|
||||
/// Gives a chance to clean up resources associated with the given account.
|
||||
type OnFreeBalanceZero: OnFreeBalanceZero<Self::AccountId>;
|
||||
|
||||
/// A function that returns true iff a given account can transfer its funds to another account.
|
||||
type EnsureAccountLiquid: EnsureAccountLiquid<Self::AccountId>;
|
||||
|
||||
/// The overarching event type.
|
||||
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
fn transfer(aux, dest: RawAddress<T::AccountId, T::AccountIndex>, value: T::Balance) -> Result = 0;
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum PrivCall {
|
||||
fn set_balance(who: RawAddress<T::AccountId, T::AccountIndex>, free: T::Balance, reserved: T::Balance) -> Result = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// An event in this module.
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[derive(Encode, Decode, PartialEq, Eq, Clone)]
|
||||
pub enum RawEvent<AccountId, AccountIndex> {
|
||||
/// A new account was created.
|
||||
NewAccount(AccountId, AccountIndex, NewAccountOutcome),
|
||||
/// An account was reaped.
|
||||
ReapedAccount(AccountId),
|
||||
}
|
||||
|
||||
impl<A, I> From<RawEvent<A, I>> for () {
|
||||
fn from(_: RawEvent<A, I>) -> () { () }
|
||||
}
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as Balances {
|
||||
// The total amount of stake on the system.
|
||||
pub TotalIssuance get(total_stake): required T::Balance;
|
||||
// The minimum amount allowed to keep an account open.
|
||||
pub ExistentialDeposit get(existential_deposit): required T::Balance;
|
||||
// The amount credited to a destination's account whose index was reclaimed.
|
||||
pub ReclaimRebate get(reclaim_rebate): required T::Balance;
|
||||
// The fee required to make a transfer.
|
||||
pub TransferFee get(transfer_fee): required T::Balance;
|
||||
// The fee required to create an account. At least as big as ReclaimRebate.
|
||||
pub CreationFee get(creation_fee): required T::Balance;
|
||||
|
||||
// The next free enumeration set.
|
||||
pub NextEnumSet get(next_enum_set): required T::AccountIndex;
|
||||
// The enumeration sets.
|
||||
pub EnumSet get(enum_set): default map [ T::AccountIndex => Vec<T::AccountId> ];
|
||||
|
||||
// The "free" balance of a given account.
|
||||
//
|
||||
// This is the only balance that matters in terms of most operations on tokens. It is
|
||||
// alone used to determine the balance when in the contract execution environment. When this
|
||||
// balance falls below the value of `ExistentialDeposit`, then the "current account" is
|
||||
// deleted: specifically `FreeBalance`. Furthermore, `OnFreeBalanceZero` callback
|
||||
// is invoked, giving a chance to external modules to cleanup data associated with
|
||||
// the deleted account.
|
||||
//
|
||||
// `system::AccountNonce` is also deleted if `ReservedBalance` is also zero (it also gets
|
||||
// collapsed to zero if it ever becomes less than `ExistentialDeposit`.
|
||||
pub FreeBalance get(free_balance): default map [ T::AccountId => T::Balance ];
|
||||
|
||||
// The amount of the balance of a given account that is exterally reserved; this can still get
|
||||
// slashed, but gets slashed last of all.
|
||||
//
|
||||
// This balance is a "reserve" balance that other subsystems use in order to set aside tokens
|
||||
// that are still "owned" by the account holder, but which are unspendable. (This is different
|
||||
// and wholly unrelated to the `Bondage` system used in the staking module.)
|
||||
//
|
||||
// When this balance falls below the value of `ExistentialDeposit`, then this "reserve account"
|
||||
// is deleted: specifically, `ReservedBalance`.
|
||||
//
|
||||
// `system::AccountNonce` is also deleted if `FreeBalance` is also zero (it also gets
|
||||
// collapsed to zero if it ever becomes less than `ExistentialDeposit`.
|
||||
pub ReservedBalance get(reserved_balance): default map [ T::AccountId => T::Balance ];
|
||||
|
||||
|
||||
// Payment stuff.
|
||||
|
||||
// The fee to be paid for making a transaction; the base.
|
||||
pub TransactionBaseFee get(transaction_base_fee): required T::Balance;
|
||||
// The fee to be paid for making a transaction; the per-byte portion.
|
||||
pub TransactionByteFee get(transaction_byte_fee): required T::Balance;
|
||||
}
|
||||
}
|
||||
|
||||
/// Whatever happened about the hint given when creating the new account.
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[derive(Encode, Decode, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum NewAccountOutcome {
|
||||
NoHint,
|
||||
GoodHint,
|
||||
BadHint,
|
||||
}
|
||||
|
||||
/// Outcome of a balance update.
|
||||
pub enum UpdateBalanceOutcome {
|
||||
/// Account balance was simply updated.
|
||||
Updated,
|
||||
/// The update has led to killing of the account.
|
||||
AccountKilled,
|
||||
}
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
|
||||
// PUBLIC IMMUTABLES
|
||||
|
||||
/// The combined balance of `who`.
|
||||
pub fn total_balance(who: &T::AccountId) -> T::Balance {
|
||||
Self::free_balance(who) + Self::reserved_balance(who)
|
||||
}
|
||||
|
||||
/// Some result as `slash(who, value)` (but without the side-effects) assuming there are no
|
||||
/// balance changes in the meantime and only the reserved balance is not taken into account.
|
||||
pub fn can_slash(who: &T::AccountId, value: T::Balance) -> bool {
|
||||
Self::free_balance(who) >= value
|
||||
}
|
||||
|
||||
/// Same result as `reserve(who, value)` (but without the side-effects) assuming there
|
||||
/// are no balance changes in the meantime.
|
||||
pub fn can_reserve(who: &T::AccountId, value: T::Balance) -> bool {
|
||||
if T::EnsureAccountLiquid::ensure_account_liquid(who).is_ok() {
|
||||
Self::free_balance(who) >= value
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Lookup an T::AccountIndex to get an Id, if there's one there.
|
||||
pub fn lookup_index(index: T::AccountIndex) -> Option<T::AccountId> {
|
||||
let enum_set_size = Self::enum_set_size();
|
||||
let set = Self::enum_set(index / enum_set_size);
|
||||
let i: usize = (index % enum_set_size).as_();
|
||||
set.get(i).map(|x| x.clone())
|
||||
}
|
||||
|
||||
/// `true` if the account `index` is ready for reclaim.
|
||||
pub fn can_reclaim(try_index: T::AccountIndex) -> bool {
|
||||
let enum_set_size = Self::enum_set_size();
|
||||
let try_set = Self::enum_set(try_index / enum_set_size);
|
||||
let i = (try_index % enum_set_size).as_();
|
||||
i < try_set.len() && Self::total_balance(&try_set[i]).is_zero()
|
||||
}
|
||||
|
||||
/// Lookup an address to get an Id, if there's one there.
|
||||
pub fn lookup_address(a: address::Address<T::AccountId, T::AccountIndex>) -> Option<T::AccountId> {
|
||||
match a {
|
||||
address::Address::Id(i) => Some(i),
|
||||
address::Address::Index(i) => Self::lookup_index(i),
|
||||
}
|
||||
}
|
||||
|
||||
// PUBLIC DISPATCH
|
||||
|
||||
/// Transfer some liquid free balance to another staker.
|
||||
pub fn transfer(aux: &T::PublicAux, dest: Address<T>, value: T::Balance) -> Result {
|
||||
let dest = Self::lookup(dest)?;
|
||||
|
||||
let transactor = aux.ref_into();
|
||||
let from_balance = Self::free_balance(transactor);
|
||||
let would_create = from_balance.is_zero();
|
||||
let fee = if would_create { Self::creation_fee() } else { Self::transfer_fee() };
|
||||
let liability = value + fee;
|
||||
|
||||
let new_from_balance = match from_balance.checked_sub(&liability) {
|
||||
Some(b) => b,
|
||||
None => return Err("balance too low to send value"),
|
||||
};
|
||||
if would_create && value < Self::existential_deposit() {
|
||||
return Err("value too low to create account");
|
||||
}
|
||||
T::EnsureAccountLiquid::ensure_account_liquid(transactor)?;
|
||||
|
||||
let to_balance = Self::free_balance(&dest);
|
||||
// NOTE: total stake being stored in the same type means that this could never overflow
|
||||
// but better to be safe than sorry.
|
||||
let new_to_balance = match to_balance.checked_add(&value) {
|
||||
Some(b) => b,
|
||||
None => return Err("destination balance too high to receive value"),
|
||||
};
|
||||
|
||||
if transactor != &dest {
|
||||
Self::set_free_balance(transactor, new_from_balance);
|
||||
Self::decrease_total_stake_by(fee);
|
||||
Self::set_free_balance_creating(&dest, new_to_balance);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// PRIV DISPATCH
|
||||
|
||||
/// Deposit one of this module's events.
|
||||
fn deposit_event(event: Event<T>) {
|
||||
<system::Module<T>>::deposit_event(<T as Trait>::Event::from(event).into());
|
||||
}
|
||||
|
||||
/// Set the balances of a given account.
|
||||
fn set_balance(who: Address<T>, free: T::Balance, reserved: T::Balance) -> Result {
|
||||
let who = Self::lookup(who)?;
|
||||
Self::set_free_balance(&who, free);
|
||||
Self::set_reserved_balance(&who, reserved);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// PUBLIC MUTABLES (DANGEROUS)
|
||||
|
||||
/// Set the free balance of an account to some new value.
|
||||
///
|
||||
/// Will enforce ExistentialDeposit law, anulling the account as needed.
|
||||
/// In that case it will return `AccountKilled`.
|
||||
pub fn set_reserved_balance(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome {
|
||||
if balance < Self::existential_deposit() {
|
||||
<ReservedBalance<T>>::insert(who, balance);
|
||||
Self::on_reserved_too_low(who);
|
||||
UpdateBalanceOutcome::AccountKilled
|
||||
} else {
|
||||
<ReservedBalance<T>>::insert(who, balance);
|
||||
UpdateBalanceOutcome::Updated
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the free balance of an account to some new value. Will enforce ExistentialDeposit
|
||||
/// law anulling the account as needed.
|
||||
///
|
||||
/// Doesn't do any preparatory work for creating a new account, so should only be used when it
|
||||
/// is known that the account already exists.
|
||||
///
|
||||
/// Returns if the account was successfully updated or update has led to killing of the account.
|
||||
pub fn set_free_balance(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome {
|
||||
// Commented out for no - but consider it instructive.
|
||||
// assert!(!Self::total_balance(who).is_zero());
|
||||
if balance < Self::existential_deposit() {
|
||||
<FreeBalance<T>>::insert(who, balance);
|
||||
Self::on_free_too_low(who);
|
||||
UpdateBalanceOutcome::AccountKilled
|
||||
} else {
|
||||
<FreeBalance<T>>::insert(who, balance);
|
||||
UpdateBalanceOutcome::Updated
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the free balance on an account to some new value.
|
||||
///
|
||||
/// Same as [`set_free_balance`], but will create a new account.
|
||||
///
|
||||
/// Returns if the account was successfully updated or update has led to killing of the account.
|
||||
///
|
||||
/// [`set_free_balance`]: #method.set_free_balance
|
||||
pub fn set_free_balance_creating(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome {
|
||||
let ed = <Module<T>>::existential_deposit();
|
||||
// If the balance is too low, then the account is reaped.
|
||||
// NOTE: There are two balances for every account: `reserved_balance` and
|
||||
// `free_balance`. This contract subsystem only cares about the latter: whenever
|
||||
// the term "balance" is used *here* it should be assumed to mean "free balance"
|
||||
// in the rest of the module.
|
||||
// Free balance can never be less than ED. If that happens, it gets reduced to zero
|
||||
// and the account information relevant to this subsystem is deleted (i.e. the
|
||||
// account is reaped).
|
||||
// NOTE: This is orthogonal to the `Bondage` value that an account has, a high
|
||||
// value of which makes even the `free_balance` unspendable.
|
||||
// TODO: enforce this for the other balance-altering functions.
|
||||
if balance < ed {
|
||||
Self::set_free_balance(who, balance);
|
||||
UpdateBalanceOutcome::AccountKilled
|
||||
} else {
|
||||
if !<FreeBalance<T>>::exists(who) {
|
||||
let outcome = Self::new_account(&who, balance);
|
||||
let credit = match outcome {
|
||||
NewAccountOutcome::GoodHint => balance + <Module<T>>::reclaim_rebate(),
|
||||
_ => balance,
|
||||
};
|
||||
Self::set_free_balance(who, credit);
|
||||
Self::increase_total_stake_by(credit - balance);
|
||||
} else {
|
||||
Self::set_free_balance(who, balance);
|
||||
}
|
||||
|
||||
UpdateBalanceOutcome::Updated
|
||||
}
|
||||
}
|
||||
|
||||
/// Deducts up to `value` from the combined balance of `who`, preferring to deduct from the
|
||||
/// free balance. This function cannot fail.
|
||||
///
|
||||
/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
|
||||
/// then `Some(remaining)` will be returned. Full completion is given by `None`.
|
||||
pub fn slash(who: &T::AccountId, value: T::Balance) -> Option<T::Balance> {
|
||||
let free_balance = Self::free_balance(who);
|
||||
let free_slash = cmp::min(free_balance, value);
|
||||
Self::set_free_balance(who, free_balance - free_slash);
|
||||
Self::decrease_total_stake_by(free_slash);
|
||||
if free_slash < value {
|
||||
Self::slash_reserved(who, value - free_slash)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds up to `value` to the free balance of `who`.
|
||||
///
|
||||
/// If `who` doesn't exist, nothing is done and an Err returned.
|
||||
pub fn reward(who: &T::AccountId, value: T::Balance) -> Result {
|
||||
if Self::total_balance(who).is_zero() {
|
||||
return Err("beneficiary account must pre-exist");
|
||||
}
|
||||
Self::set_free_balance(who, Self::free_balance(who) + value);
|
||||
Self::increase_total_stake_by(value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Moves `value` from balance to reserved balance.
|
||||
///
|
||||
/// If the free balance is lower than `value`, then no funds will be moved and an `Err` will
|
||||
/// be returned to notify of this. This is different behaviour to `unreserve`.
|
||||
pub fn reserve(who: &T::AccountId, value: T::Balance) -> Result {
|
||||
let b = Self::free_balance(who);
|
||||
if b < value {
|
||||
return Err("not enough free funds")
|
||||
}
|
||||
T::EnsureAccountLiquid::ensure_account_liquid(who)?;
|
||||
Self::set_reserved_balance(who, Self::reserved_balance(who) + value);
|
||||
Self::set_free_balance(who, b - value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Moves up to `value` from reserved balance to balance. This function cannot fail.
|
||||
///
|
||||
/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
|
||||
/// then `Some(remaining)` will be returned. Full completion is given by `None`.
|
||||
/// NOTE: This is different to `reserve`.
|
||||
pub fn unreserve(who: &T::AccountId, value: T::Balance) -> Option<T::Balance> {
|
||||
let b = Self::reserved_balance(who);
|
||||
let actual = cmp::min(b, value);
|
||||
Self::set_free_balance(who, Self::free_balance(who) + actual);
|
||||
Self::set_reserved_balance(who, b - actual);
|
||||
if actual == value {
|
||||
None
|
||||
} else {
|
||||
Some(value - actual)
|
||||
}
|
||||
}
|
||||
|
||||
/// Deducts up to `value` from reserved balance of `who`. This function cannot fail.
|
||||
///
|
||||
/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
|
||||
/// then `Some(remaining)` will be returned. Full completion is given by `None`.
|
||||
pub fn slash_reserved(who: &T::AccountId, value: T::Balance) -> Option<T::Balance> {
|
||||
let b = Self::reserved_balance(who);
|
||||
let slash = cmp::min(b, value);
|
||||
Self::set_reserved_balance(who, b - slash);
|
||||
Self::decrease_total_stake_by(slash);
|
||||
if value == slash {
|
||||
None
|
||||
} else {
|
||||
Some(value - slash)
|
||||
}
|
||||
}
|
||||
|
||||
/// Moves up to `value` from reserved balance of account `slashed` to free balance of account
|
||||
/// `beneficiary`. `beneficiary` must exist for this to succeed. If it does not, `Err` will be
|
||||
/// returned.
|
||||
///
|
||||
/// As much funds up to `value` will be moved as possible. If this is less than `value`, then
|
||||
/// `Ok(Some(remaining))` will be returned. Full completion is given by `Ok(None)`.
|
||||
pub fn repatriate_reserved(
|
||||
slashed: &T::AccountId,
|
||||
beneficiary: &T::AccountId,
|
||||
value: T::Balance
|
||||
) -> result::Result<Option<T::Balance>, &'static str> {
|
||||
if Self::total_balance(beneficiary).is_zero() {
|
||||
return Err("beneficiary account must pre-exist");
|
||||
}
|
||||
let b = Self::reserved_balance(slashed);
|
||||
let slash = cmp::min(b, value);
|
||||
Self::set_free_balance(beneficiary, Self::free_balance(beneficiary) + slash);
|
||||
Self::set_reserved_balance(slashed, b - slash);
|
||||
if value == slash {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(value - slash))
|
||||
}
|
||||
}
|
||||
|
||||
fn enum_set_size() -> T::AccountIndex {
|
||||
T::AccountIndex::sa(ENUM_SET_SIZE)
|
||||
}
|
||||
|
||||
/// Register a new account (with existential balance).
|
||||
fn new_account(who: &T::AccountId, balance: T::Balance) -> NewAccountOutcome {
|
||||
let enum_set_size = Self::enum_set_size();
|
||||
let next_set_index = Self::next_enum_set();
|
||||
let reclaim_index_magic = T::AccountIndex::sa(RECLAIM_INDEX_MAGIC);
|
||||
let reclaim_index_modulus = T::AccountIndex::sa(256usize);
|
||||
let quantization = T::AccountIndex::sa(256usize);
|
||||
|
||||
// A little easter-egg for reclaiming dead indexes..
|
||||
let ret = {
|
||||
// we quantise the number of accounts so it stays constant over a reasonable
|
||||
// period of time.
|
||||
let quantized_account_count: T::AccountIndex = (next_set_index * enum_set_size / quantization + One::one()) * quantization;
|
||||
// then modify the starting balance to be modulo this to allow it to potentially
|
||||
// identify an account index for reuse.
|
||||
let maybe_try_index = balance % <T::Balance as As<T::AccountIndex>>::sa(quantized_account_count * reclaim_index_modulus);
|
||||
let maybe_try_index = As::<T::AccountIndex>::as_(maybe_try_index);
|
||||
|
||||
// this identifier must end with magic byte 0x69 to trigger this check (a minor
|
||||
// optimisation to ensure we don't check most unintended account creations).
|
||||
if maybe_try_index % reclaim_index_modulus == reclaim_index_magic {
|
||||
// reuse is probably intended. first, remove magic byte.
|
||||
let try_index = maybe_try_index / reclaim_index_modulus;
|
||||
|
||||
// then check to see if this balance identifies a dead account index.
|
||||
let set_index = try_index / enum_set_size;
|
||||
let mut try_set = Self::enum_set(set_index);
|
||||
let item_index = (try_index % enum_set_size).as_();
|
||||
if item_index < try_set.len() {
|
||||
if Self::total_balance(&try_set[item_index]).is_zero() {
|
||||
// yup - this index refers to a dead account. can be reused.
|
||||
try_set[item_index] = who.clone();
|
||||
<EnumSet<T>>::insert(set_index, try_set);
|
||||
|
||||
Self::deposit_event(RawEvent::NewAccount(who.clone(), try_index, NewAccountOutcome::GoodHint));
|
||||
|
||||
return NewAccountOutcome::GoodHint
|
||||
}
|
||||
}
|
||||
NewAccountOutcome::BadHint
|
||||
} else {
|
||||
NewAccountOutcome::NoHint
|
||||
}
|
||||
};
|
||||
|
||||
// insert normally as a back up
|
||||
let mut set_index = next_set_index;
|
||||
// defensive only: this loop should never iterate since we keep NextEnumSet up to date later.
|
||||
let mut set = loop {
|
||||
let set = Self::enum_set(set_index);
|
||||
if set.len() < ENUM_SET_SIZE {
|
||||
break set;
|
||||
}
|
||||
set_index += One::one();
|
||||
};
|
||||
|
||||
let index = T::AccountIndex::sa(set_index.as_() * ENUM_SET_SIZE + set.len());
|
||||
|
||||
// update set.
|
||||
set.push(who.clone());
|
||||
|
||||
// keep NextEnumSet up to date
|
||||
if set.len() == ENUM_SET_SIZE {
|
||||
<NextEnumSet<T>>::put(set_index + One::one());
|
||||
}
|
||||
|
||||
// write set.
|
||||
<EnumSet<T>>::insert(set_index, set);
|
||||
|
||||
Self::deposit_event(RawEvent::NewAccount(who.clone(), index, ret));
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn reap_account(who: &T::AccountId) {
|
||||
<system::AccountNonce<T>>::remove(who);
|
||||
Self::deposit_event(RawEvent::ReapedAccount(who.clone()));
|
||||
}
|
||||
|
||||
/// Kill an account's free portion.
|
||||
fn on_free_too_low(who: &T::AccountId) {
|
||||
Self::decrease_total_stake_by(Self::free_balance(who));
|
||||
<FreeBalance<T>>::remove(who);
|
||||
|
||||
T::OnFreeBalanceZero::on_free_balance_zero(who);
|
||||
|
||||
if Self::reserved_balance(who).is_zero() {
|
||||
Self::reap_account(who);
|
||||
}
|
||||
}
|
||||
|
||||
/// Kill an account's reserved portion.
|
||||
fn on_reserved_too_low(who: &T::AccountId) {
|
||||
Self::decrease_total_stake_by(Self::reserved_balance(who));
|
||||
<ReservedBalance<T>>::remove(who);
|
||||
|
||||
if Self::free_balance(who).is_zero() {
|
||||
Self::reap_account(who);
|
||||
}
|
||||
}
|
||||
|
||||
/// Increase TotalIssuance by Value.
|
||||
pub fn increase_total_stake_by(value: T::Balance) {
|
||||
if let Some(v) = <Module<T>>::total_stake().checked_add(&value) {
|
||||
<TotalIssuance<T>>::put(v);
|
||||
}
|
||||
}
|
||||
/// Decrease TotalIssuance by Value.
|
||||
pub fn decrease_total_stake_by(value: T::Balance) {
|
||||
if let Some(v) = <Module<T>>::total_stake().checked_sub(&value) {
|
||||
<TotalIssuance<T>>::put(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> Executable for Module<T> {
|
||||
fn execute() {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> AuxLookup for Module<T> {
|
||||
type Source = address::Address<T::AccountId, T::AccountIndex>;
|
||||
type Target = T::AccountId;
|
||||
fn lookup(a: Self::Source) -> result::Result<Self::Target, &'static str> {
|
||||
match a {
|
||||
address::Address::Id(i) => Ok(i),
|
||||
address::Address::Index(i) => <Module<T>>::lookup_index(i).ok_or("invalid account index"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> MakePayment<T::AccountId> for Module<T> {
|
||||
fn make_payment(transactor: &T::AccountId, encoded_len: usize) -> Result {
|
||||
let b = Self::free_balance(transactor);
|
||||
let transaction_fee = Self::transaction_base_fee() + Self::transaction_byte_fee() * <T::Balance as As<u64>>::sa(encoded_len as u64);
|
||||
if b < transaction_fee + Self::existential_deposit() {
|
||||
return Err("not enough funds for transaction fee");
|
||||
}
|
||||
Self::set_free_balance(transactor, b - transaction_fee);
|
||||
Self::decrease_total_stake_by(transaction_fee);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright 2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Test utilities
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use primitives::BuildStorage;
|
||||
use primitives::traits::HasPublicAux;
|
||||
use primitives::testing::{Digest, Header};
|
||||
use substrate_primitives::{H256, KeccakHasher};
|
||||
use runtime_io;
|
||||
use {GenesisConfig, Module, Trait, system};
|
||||
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
pub struct Test;
|
||||
impl HasPublicAux for Test {
|
||||
type PublicAux = u64;
|
||||
}
|
||||
impl system::Trait for Test {
|
||||
type PublicAux = <Self as HasPublicAux>::PublicAux;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
type Hashing = ::primitives::traits::BlakeTwo256;
|
||||
type Digest = Digest;
|
||||
type AccountId = u64;
|
||||
type Header = Header;
|
||||
type Event = ();
|
||||
}
|
||||
impl Trait for Test {
|
||||
type Balance = u64;
|
||||
type AccountIndex = u64;
|
||||
type OnFreeBalanceZero = ();
|
||||
type EnsureAccountLiquid = ();
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
pub fn new_test_ext(ext_deposit: u64, monied: bool) -> runtime_io::TestExternalities<KeccakHasher> {
|
||||
let mut t = system::GenesisConfig::<Test>::default().build_storage().unwrap();
|
||||
let balance_factor = if ext_deposit > 0 {
|
||||
256
|
||||
} else {
|
||||
1
|
||||
};
|
||||
t.extend(GenesisConfig::<Test>{
|
||||
balances: if monied {
|
||||
vec![(1, 10 * balance_factor), (2, 20 * balance_factor), (3, 30 * balance_factor), (4, 40 * balance_factor)]
|
||||
} else {
|
||||
vec![(10, balance_factor), (20, balance_factor)]
|
||||
},
|
||||
transaction_base_fee: 0,
|
||||
transaction_byte_fee: 0,
|
||||
existential_deposit: ext_deposit,
|
||||
transfer_fee: 0,
|
||||
creation_fee: 0,
|
||||
reclaim_rebate: 0,
|
||||
}.build_storage().unwrap());
|
||||
t.into()
|
||||
}
|
||||
|
||||
pub type System = system::Module<Test>;
|
||||
pub type Balances = Module<Test>;
|
||||
@@ -0,0 +1,324 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Tests for the module.
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use super::*;
|
||||
use runtime_io::with_externalities;
|
||||
use mock::{Balances, System, Test, new_test_ext};
|
||||
|
||||
#[test]
|
||||
fn reward_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, true), || {
|
||||
assert_eq!(Balances::total_balance(&1), 10);
|
||||
assert_ok!(Balances::reward(&1, 10));
|
||||
assert_eq!(Balances::total_balance(&1), 20);
|
||||
assert_eq!(<TotalIssuance<Test>>::get(), 110);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indexing_lookup_should_work() {
|
||||
with_externalities(&mut new_test_ext(10, true), || {
|
||||
assert_eq!(Balances::lookup_index(0), Some(1));
|
||||
assert_eq!(Balances::lookup_index(1), Some(2));
|
||||
assert_eq!(Balances::lookup_index(2), Some(3));
|
||||
assert_eq!(Balances::lookup_index(3), Some(4));
|
||||
assert_eq!(Balances::lookup_index(4), None);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_indexing_on_new_accounts_should_work() {
|
||||
with_externalities(&mut new_test_ext(10, true), || {
|
||||
assert_eq!(Balances::lookup_index(4), None);
|
||||
assert_ok!(Balances::transfer(&1, 5.into(), 10));
|
||||
assert_eq!(Balances::lookup_index(4), Some(5));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dust_account_removal_should_work() {
|
||||
with_externalities(&mut new_test_ext(256 * 10, true), || {
|
||||
System::inc_account_nonce(&2);
|
||||
assert_eq!(System::account_nonce(&2), 1);
|
||||
assert_eq!(Balances::total_balance(&2), 256 * 20);
|
||||
|
||||
assert_ok!(Balances::transfer(&2, 5.into(), 256 * 10 + 1)); // index 1 (account 2) becomes zombie
|
||||
assert_eq!(Balances::total_balance(&2), 0);
|
||||
assert_eq!(Balances::total_balance(&5), 256 * 10 + 1);
|
||||
assert_eq!(System::account_nonce(&2), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reclaim_indexing_on_new_accounts_should_work() {
|
||||
with_externalities(&mut new_test_ext(256 * 1, true), || {
|
||||
assert_eq!(Balances::lookup_index(1), Some(2));
|
||||
assert_eq!(Balances::lookup_index(4), None);
|
||||
assert_eq!(Balances::total_balance(&2), 256 * 20);
|
||||
|
||||
assert_ok!(Balances::transfer(&2, 5.into(), 256 * 20)); // account 2 becomes zombie freeing index 1 for reclaim)
|
||||
assert_eq!(Balances::total_balance(&2), 0);
|
||||
|
||||
assert_ok!(Balances::transfer(&5, 6.into(), 256 * 1 + 0x69)); // account 6 takes index 1.
|
||||
assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69);
|
||||
assert_eq!(Balances::lookup_index(1), Some(6));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reserved_balance_should_prevent_reclaim_count() {
|
||||
with_externalities(&mut new_test_ext(256 * 1, true), || {
|
||||
System::inc_account_nonce(&2);
|
||||
assert_eq!(Balances::lookup_index(1), Some(2));
|
||||
assert_eq!(Balances::lookup_index(4), None);
|
||||
assert_eq!(Balances::total_balance(&2), 256 * 20);
|
||||
|
||||
assert_ok!(Balances::reserve(&2, 256 * 19 + 1)); // account 2 becomes mostly reserved
|
||||
assert_eq!(Balances::free_balance(&2), 0); // "free" account deleted."
|
||||
assert_eq!(Balances::total_balance(&2), 256 * 19 + 1); // reserve still exists.
|
||||
assert_eq!(System::account_nonce(&2), 1);
|
||||
|
||||
assert_ok!(Balances::transfer(&4, 5.into(), 256 * 1 + 0x69)); // account 4 tries to take index 1 for account 5.
|
||||
assert_eq!(Balances::total_balance(&5), 256 * 1 + 0x69);
|
||||
assert_eq!(Balances::lookup_index(1), Some(2)); // but fails.
|
||||
assert_eq!(System::account_nonce(&2), 1);
|
||||
|
||||
assert_eq!(Balances::slash(&2, 256 * 18 + 2), None); // account 2 gets slashed
|
||||
assert_eq!(Balances::total_balance(&2), 0); // "free" account deleted."
|
||||
assert_eq!(System::account_nonce(&2), 0);
|
||||
|
||||
assert_ok!(Balances::transfer(&4, 6.into(), 256 * 1 + 0x69)); // account 4 tries to take index 1 again for account 6.
|
||||
assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69);
|
||||
assert_eq!(Balances::lookup_index(1), Some(6)); // and succeeds.
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn balance_works() {
|
||||
with_externalities(&mut new_test_ext(0, false), || {
|
||||
Balances::set_free_balance(&1, 42);
|
||||
assert_eq!(Balances::free_balance(&1), 42);
|
||||
assert_eq!(Balances::reserved_balance(&1), 0);
|
||||
assert_eq!(Balances::total_balance(&1), 42);
|
||||
assert_eq!(Balances::free_balance(&2), 0);
|
||||
assert_eq!(Balances::reserved_balance(&2), 0);
|
||||
assert_eq!(Balances::total_balance(&2), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn balance_transfer_works() {
|
||||
with_externalities(&mut new_test_ext(0, false), || {
|
||||
Balances::set_free_balance(&1, 111);
|
||||
Balances::increase_total_stake_by(111);
|
||||
assert_ok!(Balances::transfer(&1, 2.into(), 69));
|
||||
assert_eq!(Balances::total_balance(&1), 42);
|
||||
assert_eq!(Balances::total_balance(&2), 69);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reserving_balance_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, false), || {
|
||||
Balances::set_free_balance(&1, 111);
|
||||
|
||||
assert_eq!(Balances::total_balance(&1), 111);
|
||||
assert_eq!(Balances::free_balance(&1), 111);
|
||||
assert_eq!(Balances::reserved_balance(&1), 0);
|
||||
|
||||
assert_ok!(Balances::reserve(&1, 69));
|
||||
|
||||
assert_eq!(Balances::total_balance(&1), 111);
|
||||
assert_eq!(Balances::free_balance(&1), 42);
|
||||
assert_eq!(Balances::reserved_balance(&1), 69);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn balance_transfer_when_reserved_should_not_work() {
|
||||
with_externalities(&mut new_test_ext(0, false), || {
|
||||
Balances::set_free_balance(&1, 111);
|
||||
assert_ok!(Balances::reserve(&1, 69));
|
||||
assert_noop!(Balances::transfer(&1, 2.into(), 69), "balance too low to send value");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deducting_balance_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, false), || {
|
||||
Balances::set_free_balance(&1, 111);
|
||||
assert_ok!(Balances::reserve(&1, 69));
|
||||
assert_eq!(Balances::free_balance(&1), 42);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn refunding_balance_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, false), || {
|
||||
Balances::set_free_balance(&1, 42);
|
||||
Balances::set_reserved_balance(&1, 69);
|
||||
Balances::unreserve(&1, 69);
|
||||
assert_eq!(Balances::free_balance(&1), 111);
|
||||
assert_eq!(Balances::reserved_balance(&1), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slashing_balance_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, false), || {
|
||||
Balances::set_free_balance(&1, 111);
|
||||
Balances::increase_total_stake_by(111);
|
||||
assert_ok!(Balances::reserve(&1, 69));
|
||||
assert!(Balances::slash(&1, 69).is_none());
|
||||
assert_eq!(Balances::free_balance(&1), 0);
|
||||
assert_eq!(Balances::reserved_balance(&1), 42);
|
||||
assert_eq!(<TotalIssuance<Test>>::get(), 44);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slashing_incomplete_balance_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, false), || {
|
||||
Balances::set_free_balance(&1, 42);
|
||||
Balances::increase_total_stake_by(42);
|
||||
assert_ok!(Balances::reserve(&1, 21));
|
||||
assert!(Balances::slash(&1, 69).is_some());
|
||||
assert_eq!(Balances::free_balance(&1), 0);
|
||||
assert_eq!(Balances::reserved_balance(&1), 0);
|
||||
assert_eq!(<TotalIssuance<Test>>::get(), 2);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unreserving_balance_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, false), || {
|
||||
Balances::set_free_balance(&1, 111);
|
||||
assert_ok!(Balances::reserve(&1, 111));
|
||||
Balances::unreserve(&1, 42);
|
||||
assert_eq!(Balances::reserved_balance(&1), 69);
|
||||
assert_eq!(Balances::free_balance(&1), 42);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slashing_reserved_balance_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, false), || {
|
||||
Balances::set_free_balance(&1, 111);
|
||||
Balances::increase_total_stake_by(111);
|
||||
assert_ok!(Balances::reserve(&1, 111));
|
||||
assert!(Balances::slash_reserved(&1, 42).is_none());
|
||||
assert_eq!(Balances::reserved_balance(&1), 69);
|
||||
assert_eq!(Balances::free_balance(&1), 0);
|
||||
assert_eq!(<TotalIssuance<Test>>::get(), 71);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slashing_incomplete_reserved_balance_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, false), || {
|
||||
Balances::set_free_balance(&1, 111);
|
||||
Balances::increase_total_stake_by(111);
|
||||
assert_ok!(Balances::reserve(&1, 42));
|
||||
assert!(Balances::slash_reserved(&1, 69).is_some());
|
||||
assert_eq!(Balances::free_balance(&1), 69);
|
||||
assert_eq!(Balances::reserved_balance(&1), 0);
|
||||
assert_eq!(<TotalIssuance<Test>>::get(), 71);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transferring_reserved_balance_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, false), || {
|
||||
Balances::set_free_balance(&1, 110);
|
||||
Balances::set_free_balance(&2, 1);
|
||||
assert_ok!(Balances::reserve(&1, 110));
|
||||
assert_ok!(Balances::repatriate_reserved(&1, &2, 41), None);
|
||||
assert_eq!(Balances::reserved_balance(&1), 69);
|
||||
assert_eq!(Balances::free_balance(&1), 0);
|
||||
assert_eq!(Balances::reserved_balance(&2), 0);
|
||||
assert_eq!(Balances::free_balance(&2), 42);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transferring_reserved_balance_to_nonexistent_should_fail() {
|
||||
with_externalities(&mut new_test_ext(0, false), || {
|
||||
Balances::set_free_balance(&1, 111);
|
||||
assert_ok!(Balances::reserve(&1, 111));
|
||||
assert_noop!(Balances::repatriate_reserved(&1, &2, 42), "beneficiary account must pre-exist");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transferring_incomplete_reserved_balance_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, false), || {
|
||||
Balances::set_free_balance(&1, 110);
|
||||
Balances::set_free_balance(&2, 1);
|
||||
assert_ok!(Balances::reserve(&1, 41));
|
||||
assert!(Balances::repatriate_reserved(&1, &2, 69).unwrap().is_some());
|
||||
assert_eq!(Balances::reserved_balance(&1), 0);
|
||||
assert_eq!(Balances::free_balance(&1), 69);
|
||||
assert_eq!(Balances::reserved_balance(&2), 0);
|
||||
assert_eq!(Balances::free_balance(&2), 42);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transferring_too_high_value_should_not_panic() {
|
||||
with_externalities(&mut new_test_ext(0, false), || {
|
||||
<FreeBalance<Test>>::insert(1, u64::max_value());
|
||||
<FreeBalance<Test>>::insert(2, 1);
|
||||
|
||||
assert_err!(
|
||||
Balances::transfer(&1, 2.into(), u64::max_value()),
|
||||
"destination balance too high to receive value"
|
||||
);
|
||||
|
||||
assert_eq!(Balances::free_balance(&1), u64::max_value());
|
||||
assert_eq!(Balances::free_balance(&2), 1);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn account_removal_on_free_too_low() {
|
||||
with_externalities(&mut new_test_ext(100, false), || {
|
||||
// Setup two accounts with free balance above the exsistential threshold.
|
||||
{
|
||||
Balances::set_free_balance(&1, 110);
|
||||
Balances::increase_total_stake_by(110);
|
||||
|
||||
Balances::set_free_balance(&2, 110);
|
||||
Balances::increase_total_stake_by(110);
|
||||
|
||||
assert_eq!(<TotalIssuance<Test>>::get(), 732);
|
||||
}
|
||||
|
||||
// Transfer funds from account 1 of such amount that after this transfer
|
||||
// the balance of account 1 will be below the exsistential threshold.
|
||||
// This should lead to the removal of all balance of this account.
|
||||
assert_ok!(Balances::transfer(&1, 2.into(), 20));
|
||||
|
||||
// Verify free balance removal of account 1.
|
||||
assert_eq!(Balances::free_balance(&1), 0);
|
||||
|
||||
// Verify that TotalIssuance tracks balance removal when free balance is too low.
|
||||
assert_eq!(<TotalIssuance<Test>>::get(), 642);
|
||||
});
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate Demo is free software: you can redistribute it and/or modify
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate Demo is distributed in the hope that it will be useful,
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Conensus module for runtime; manages the authority set ready for the native code.
|
||||
|
||||
@@ -42,7 +42,7 @@ use rstd::prelude::*;
|
||||
use runtime_support::{storage, Parameter};
|
||||
use runtime_support::dispatch::Result;
|
||||
use runtime_support::storage::unhashed::StorageVec;
|
||||
use primitives::traits::{RefInto, MaybeSerializeDebug, MaybeEmpty};
|
||||
use primitives::traits::{MaybeSerializeDebug, MaybeEmpty};
|
||||
use primitives::bft::MisbehaviorReport;
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
@@ -63,9 +63,20 @@ pub const CODE: &'static [u8] = b":code";
|
||||
|
||||
pub type KeyValue = (Vec<u8>, Vec<u8>);
|
||||
|
||||
pub trait OnOfflineValidator {
|
||||
fn on_offline_validator(validator_index: usize);
|
||||
}
|
||||
|
||||
impl OnOfflineValidator for () {
|
||||
fn on_offline_validator(_validator_index: usize) {}
|
||||
}
|
||||
|
||||
pub trait Trait: system::Trait {
|
||||
type PublicAux: RefInto<Self::AccountId> + MaybeEmpty; // MaybeEmpty is for Timestamp's usage.
|
||||
/// The allowed extrinsic position for `note_offline` inherent.
|
||||
const NOTE_OFFLINE_POSITION: u32;
|
||||
|
||||
type SessionKey: Parameter + Default + MaybeSerializeDebug;
|
||||
type OnOfflineValidator: OnOfflineValidator;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
@@ -74,7 +85,8 @@ decl_module! {
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
fn report_misbehavior(aux, report: MisbehaviorReport<T::Hash, T::BlockNumber>) -> Result = 0;
|
||||
fn remark(aux, remark: Vec<u8>) -> Result = 1;
|
||||
fn note_offline(aux, offline_val_indices: Vec<u32>) -> Result = 1;
|
||||
fn remark(aux, remark: Vec<u8>) -> Result = 2;
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
@@ -110,6 +122,24 @@ impl<T: Trait> Module<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Note the previous block's validator missed their opportunity to propose a block. This only comes in
|
||||
/// if 2/3+1 of the validators agree that no proposal was submitted. It's only relevant
|
||||
/// for the previous block.
|
||||
fn note_offline(aux: &T::PublicAux, offline_val_indices: Vec<u32>) -> Result {
|
||||
assert!(aux.is_empty());
|
||||
assert!(
|
||||
<system::Module<T>>::extrinsic_index() == Some(T::NOTE_OFFLINE_POSITION),
|
||||
"note_offline extrinsic must be at position {} in the block",
|
||||
T::NOTE_OFFLINE_POSITION
|
||||
);
|
||||
|
||||
for validator_index in offline_val_indices.into_iter() {
|
||||
T::OnOfflineValidator::on_offline_validator(validator_index as usize);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Make some on-chain remark.
|
||||
fn remark(_aux: &T::PublicAux, _remark: Vec<u8>) -> Result {
|
||||
Ok(())
|
||||
|
||||
@@ -8,16 +8,13 @@ serde = { version = "1.0", default_features = false }
|
||||
serde_derive = { version = "1.0", optional = true }
|
||||
substrate-codec = { path = "../../codec", default_features = false }
|
||||
substrate-primitives = { path = "../../primitives" }
|
||||
substrate-runtime-consensus = { path = "../../runtime/consensus", default_features = false }
|
||||
substrate-runtime-primitives = { path = "../../runtime/primitives" }
|
||||
substrate-runtime-io = { path = "../../runtime-io", default_features = false }
|
||||
substrate-runtime-std = { path = "../../runtime-std", default_features = false }
|
||||
substrate-runtime-sandbox = { path = "../../runtime-sandbox", default_features = false }
|
||||
substrate-runtime-staking = { path = "../../runtime/staking", default_features = false }
|
||||
substrate-runtime-support = { path = "../../runtime-support", default_features = false }
|
||||
substrate-runtime-system = { path = "../../runtime/system", default_features = false }
|
||||
substrate-runtime-session = { path = "../session", default_features = false }
|
||||
substrate-runtime-timestamp = { path = "../timestamp", default_features = false }
|
||||
substrate-runtime-balances = { path = "../balances", default_features = false }
|
||||
parity-wasm = { version = "0.31", default_features = false }
|
||||
pwasm-utils = { version = "0.3", default_features = false }
|
||||
|
||||
@@ -32,15 +29,12 @@ std = [
|
||||
"serde/std",
|
||||
"substrate-codec/std",
|
||||
"substrate-runtime-primitives/std",
|
||||
"substrate-runtime-consensus/std",
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-runtime-std/std",
|
||||
"substrate-runtime-balances/std",
|
||||
"substrate-runtime-sandbox/std",
|
||||
"substrate-runtime-staking/std",
|
||||
"substrate-runtime-support/std",
|
||||
"substrate-runtime-system/std",
|
||||
"substrate-runtime-timestamp/std",
|
||||
"substrate-runtime-session/std",
|
||||
"parity-wasm/std",
|
||||
"pwasm-utils/std",
|
||||
]
|
||||
|
||||
@@ -22,8 +22,7 @@ use rstd::cell::RefCell;
|
||||
use rstd::collections::btree_map::{BTreeMap, Entry};
|
||||
use rstd::prelude::*;
|
||||
use runtime_support::StorageMap;
|
||||
use staking;
|
||||
use system;
|
||||
use {balances, system};
|
||||
|
||||
pub struct ChangeEntry<T: Trait> {
|
||||
balance: Option<T::Balance>,
|
||||
@@ -61,13 +60,13 @@ impl<T: Trait> AccountDb<T> for DirectAccountDb {
|
||||
<CodeOf<T>>::get(account)
|
||||
}
|
||||
fn get_balance(&self, account: &T::AccountId) -> T::Balance {
|
||||
staking::Module::<T>::free_balance(account)
|
||||
balances::Module::<T>::free_balance(account)
|
||||
}
|
||||
fn commit(&mut self, s: ChangeSet<T>) {
|
||||
for (address, changed) in s.into_iter() {
|
||||
if let Some(balance) = changed.balance {
|
||||
if let staking::UpdateBalanceOutcome::AccountKilled =
|
||||
staking::Module::<T>::set_free_balance_creating(&address, balance)
|
||||
if let balances::UpdateBalanceOutcome::AccountKilled =
|
||||
balances::Module::<T>::set_free_balance_creating(&address, balance)
|
||||
{
|
||||
// Account killed. This will ultimately lead to calling `OnFreeBalanceZero` callback
|
||||
// which will make removal of CodeOf and StorageOf for this account.
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate Demo is free software: you can redistribute it and/or modify
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate Demo is distributed in the hope that it will be useful,
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! An implementation of double map backed by storage.
|
||||
//!
|
||||
|
||||
@@ -22,8 +22,7 @@ use vm;
|
||||
use rstd::prelude::*;
|
||||
use runtime_primitives::traits::{Zero, CheckedAdd, CheckedSub};
|
||||
use runtime_support::{StorageMap, StorageValue};
|
||||
use staking;
|
||||
use system;
|
||||
use balances::{self, EnsureAccountLiquid};
|
||||
|
||||
pub struct CreateReceipt<T: Trait> {
|
||||
pub address: T::AccountId,
|
||||
@@ -185,9 +184,9 @@ fn transfer<T: Trait>(
|
||||
<Module<T>>::contract_fee()
|
||||
} else {
|
||||
if would_create {
|
||||
<staking::Module<T>>::creation_fee()
|
||||
<balances::Module<T>>::creation_fee()
|
||||
} else {
|
||||
<staking::Module<T>>::transfer_fee()
|
||||
<balances::Module<T>>::transfer_fee()
|
||||
}
|
||||
};
|
||||
|
||||
@@ -200,12 +199,10 @@ fn transfer<T: Trait>(
|
||||
Some(b) => b,
|
||||
None => return Err("balance too low to send value"),
|
||||
};
|
||||
if would_create && value < <staking::Module<T>>::existential_deposit() {
|
||||
if would_create && value < <balances::Module<T>>::existential_deposit() {
|
||||
return Err("value too low to create account");
|
||||
}
|
||||
if <staking::Module<T>>::bondage(transactor) > <system::Module<T>>::block_number() {
|
||||
return Err("bondage too high to send value");
|
||||
}
|
||||
<T as balances::Trait>::EnsureAccountLiquid::ensure_account_liquid(transactor)?;
|
||||
|
||||
let to_balance = overlay.get_balance(dest);
|
||||
let new_to_balance = match to_balance.checked_add(&value) {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
use {Trait, Module, GasSpent};
|
||||
use runtime_primitives::traits::{As, CheckedMul, CheckedSub, Zero};
|
||||
use runtime_support::StorageValue;
|
||||
use staking;
|
||||
use balances;
|
||||
|
||||
#[must_use]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
@@ -146,15 +146,15 @@ pub fn buy_gas<T: Trait>(
|
||||
|
||||
// Buy the specified amount of gas.
|
||||
let gas_price = <Module<T>>::gas_price();
|
||||
let b = <staking::Module<T>>::free_balance(transactor);
|
||||
let b = <balances::Module<T>>::free_balance(transactor);
|
||||
let cost = <T::Gas as As<T::Balance>>::as_(gas_limit.clone())
|
||||
.checked_mul(&gas_price)
|
||||
.ok_or("overflow multiplying gas limit by price")?;
|
||||
if b < cost + <staking::Module<T>>::existential_deposit() {
|
||||
if b < cost + <balances::Module<T>>::existential_deposit() {
|
||||
return Err("not enough funds for transaction fee");
|
||||
}
|
||||
<staking::Module<T>>::set_free_balance(transactor, b - cost);
|
||||
<staking::Module<T>>::decrease_total_stake_by(cost);
|
||||
<balances::Module<T>>::set_free_balance(transactor, b - cost);
|
||||
<balances::Module<T>>::decrease_total_stake_by(cost);
|
||||
Ok(GasMeter {
|
||||
limit: gas_limit,
|
||||
gas_left: gas_limit,
|
||||
@@ -171,8 +171,8 @@ pub fn refund_unused_gas<T: Trait>(transactor: &T::AccountId, gas_meter: GasMete
|
||||
<GasSpent<T>>::put(gas_spent);
|
||||
|
||||
// Refund gas left by the price it was bought.
|
||||
let b = <staking::Module<T>>::free_balance(transactor);
|
||||
let b = <balances::Module<T>>::free_balance(transactor);
|
||||
let refund = <T::Gas as As<T::Balance>>::as_(gas_meter.gas_left) * gas_meter.gas_price;
|
||||
<staking::Module<T>>::set_free_balance(transactor, b + refund);
|
||||
<staking::Module<T>>::increase_total_stake_by(refund);
|
||||
<balances::Module<T>>::set_free_balance(transactor, b + refund);
|
||||
<balances::Module<T>>::increase_total_stake_by(refund);
|
||||
}
|
||||
|
||||
@@ -70,15 +70,9 @@ extern crate substrate_runtime_sandbox as sandbox;
|
||||
#[cfg_attr(feature = "std", macro_use)]
|
||||
extern crate substrate_runtime_std as rstd;
|
||||
|
||||
extern crate substrate_runtime_consensus as consensus;
|
||||
extern crate substrate_runtime_staking as staking;
|
||||
extern crate substrate_runtime_balances as balances;
|
||||
extern crate substrate_runtime_system as system;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate substrate_runtime_timestamp as timestamp;
|
||||
#[cfg(test)]
|
||||
extern crate substrate_runtime_session as session;
|
||||
|
||||
#[macro_use]
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
|
||||
@@ -112,7 +106,7 @@ use runtime_primitives::traits::{As, RefInto, SimpleArithmetic, Executable};
|
||||
use runtime_support::dispatch::Result;
|
||||
use runtime_support::{Parameter, StorageMap, StorageValue};
|
||||
|
||||
pub trait Trait: system::Trait + staking::Trait + consensus::Trait {
|
||||
pub trait Trait: balances::Trait {
|
||||
/// Function type to get the contract address given the creator.
|
||||
type DetermineContractAddress: ContractAddressFor<Self::AccountId>;
|
||||
|
||||
@@ -188,7 +182,7 @@ impl<T: Trait> double_map::StorageDoubleMap for StorageOf<T> {
|
||||
impl<T: Trait> Module<T> {
|
||||
/// Make a call to a specified account, optionally transferring some balance.
|
||||
fn call(
|
||||
aux: &<T as consensus::Trait>::PublicAux,
|
||||
aux: &<T as system::Trait>::PublicAux,
|
||||
dest: T::AccountId,
|
||||
value: T::Balance,
|
||||
gas_limit: T::Gas,
|
||||
@@ -233,7 +227,7 @@ impl<T: Trait> Module<T> {
|
||||
/// after the execution is saved as the `code` of the account. That code will be invoked
|
||||
/// upon any message received by this account.
|
||||
fn create(
|
||||
aux: &<T as consensus::Trait>::PublicAux,
|
||||
aux: &<T as system::Trait>::PublicAux,
|
||||
endowment: T::Balance,
|
||||
gas_limit: T::Gas,
|
||||
ctor_code: Vec<u8>,
|
||||
@@ -269,7 +263,7 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> staking::OnFreeBalanceZero<T::AccountId> for Module<T> {
|
||||
impl<T: Trait> balances::OnFreeBalanceZero<T::AccountId> for Module<T> {
|
||||
fn on_free_balance_zero(who: &T::AccountId) {
|
||||
<CodeOf<T>>::remove(who);
|
||||
<StorageOf<T>>::remove_prefix(who.clone());
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
use double_map::StorageDoubleMap;
|
||||
use runtime_io::with_externalities;
|
||||
use runtime_primitives::testing::{Digest, H256, Header};
|
||||
use runtime_primitives::traits::{BlakeTwo256, HasPublicAux, Identity};
|
||||
use runtime_primitives::traits::{BlakeTwo256, HasPublicAux};
|
||||
use runtime_primitives::BuildStorage;
|
||||
use runtime_support::StorageMap;
|
||||
use substrate_primitives::KeccakHasher;
|
||||
use wabt;
|
||||
use {
|
||||
consensus, runtime_io, session, staking, system, timestamp, CodeOf, ContractAddressFor,
|
||||
runtime_io, balances, system, CodeOf, ContractAddressFor,
|
||||
GenesisConfig, Module, StorageOf, Trait,
|
||||
};
|
||||
|
||||
@@ -32,11 +32,8 @@ pub struct Test;
|
||||
impl HasPublicAux for Test {
|
||||
type PublicAux = u64;
|
||||
}
|
||||
impl consensus::Trait for Test {
|
||||
type PublicAux = <Self as HasPublicAux>::PublicAux;
|
||||
type SessionKey = u64;
|
||||
}
|
||||
impl system::Trait for Test {
|
||||
type PublicAux = <Self as HasPublicAux>::PublicAux;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
@@ -46,20 +43,11 @@ impl system::Trait for Test {
|
||||
type Header = Header;
|
||||
type Event = ();
|
||||
}
|
||||
impl timestamp::Trait for Test {
|
||||
const TIMESTAMP_SET_POSITION: u32 = 0;
|
||||
type Moment = u64;
|
||||
}
|
||||
impl staking::Trait for Test {
|
||||
const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1;
|
||||
impl balances::Trait for Test {
|
||||
type Balance = u64;
|
||||
type AccountIndex = u64;
|
||||
type OnFreeBalanceZero = Contract;
|
||||
type Event = ();
|
||||
}
|
||||
impl session::Trait for Test {
|
||||
type ConvertAccountIdToSessionKey = Identity;
|
||||
type OnSessionChange = Staking;
|
||||
type EnsureAccountLiquid = ();
|
||||
type Event = ();
|
||||
}
|
||||
impl Trait for Test {
|
||||
@@ -67,7 +55,7 @@ impl Trait for Test {
|
||||
type DetermineContractAddress = DummyContractAddressFor;
|
||||
}
|
||||
|
||||
type Staking = staking::Module<Test>;
|
||||
type Balances = balances::Module<Test>;
|
||||
type Contract = Module<Test>;
|
||||
|
||||
pub struct DummyContractAddressFor;
|
||||
@@ -109,45 +97,17 @@ impl ExtBuilder {
|
||||
.build_storage()
|
||||
.unwrap();
|
||||
t.extend(
|
||||
consensus::GenesisConfig::<Test> {
|
||||
code: vec![],
|
||||
authorities: vec![],
|
||||
}.build_storage()
|
||||
.unwrap(),
|
||||
);
|
||||
t.extend(
|
||||
session::GenesisConfig::<Test> {
|
||||
session_length: 1,
|
||||
validators: vec![10, 20],
|
||||
}.build_storage()
|
||||
.unwrap(),
|
||||
);
|
||||
t.extend(
|
||||
staking::GenesisConfig::<Test> {
|
||||
sessions_per_era: 1,
|
||||
current_era: 0,
|
||||
balances::GenesisConfig::<Test> {
|
||||
balances: vec![],
|
||||
intentions: vec![],
|
||||
validator_count: 2,
|
||||
minimum_validator_count: 0,
|
||||
bonding_duration: 0,
|
||||
transaction_base_fee: 0,
|
||||
transaction_byte_fee: 0,
|
||||
existential_deposit: self.existential_deposit,
|
||||
transfer_fee: 0,
|
||||
creation_fee: 0,
|
||||
reclaim_rebate: 0,
|
||||
early_era_slash: 0,
|
||||
session_reward: 0,
|
||||
offline_slash_grace: 0,
|
||||
}.build_storage()
|
||||
.unwrap(),
|
||||
);
|
||||
t.extend(
|
||||
timestamp::GenesisConfig::<Test>::default()
|
||||
.build_storage()
|
||||
.unwrap(),
|
||||
);
|
||||
t.extend(
|
||||
GenesisConfig::<Test> {
|
||||
contract_fee: 21,
|
||||
@@ -195,15 +155,15 @@ fn contract_transfer() {
|
||||
with_externalities(&mut ExtBuilder::default().build(), || {
|
||||
<CodeOf<Test>>::insert(1, code_transfer.to_vec());
|
||||
|
||||
Staking::set_free_balance(&0, 100_000_000);
|
||||
Staking::increase_total_stake_by(100_000_000);
|
||||
Staking::set_free_balance(&1, 11);
|
||||
Staking::increase_total_stake_by(11);
|
||||
Balances::set_free_balance(&0, 100_000_000);
|
||||
Balances::increase_total_stake_by(100_000_000);
|
||||
Balances::set_free_balance(&1, 11);
|
||||
Balances::increase_total_stake_by(11);
|
||||
|
||||
assert_ok!(Contract::call(&0, 1, 3, 100_000, Vec::new()));
|
||||
|
||||
assert_eq!(
|
||||
Staking::free_balance(&0),
|
||||
Balances::free_balance(&0),
|
||||
// 3 - value sent with the transaction
|
||||
// 2 * 6 - gas used by the contract (6) multiplied by gas price (2)
|
||||
// 2 * 135 - base gas fee for call (by transaction)
|
||||
@@ -211,11 +171,11 @@ fn contract_transfer() {
|
||||
100_000_000 - 3 - (2 * 6) - (2 * 135) - (2 * 135),
|
||||
);
|
||||
assert_eq!(
|
||||
Staking::free_balance(&1),
|
||||
Balances::free_balance(&1),
|
||||
11 + 3 - CONTRACT_SHOULD_TRANSFER_VALUE,
|
||||
);
|
||||
assert_eq!(
|
||||
Staking::free_balance(&CONTRACT_SHOULD_TRANSFER_TO),
|
||||
Balances::free_balance(&CONTRACT_SHOULD_TRANSFER_TO),
|
||||
CONTRACT_SHOULD_TRANSFER_VALUE,
|
||||
);
|
||||
});
|
||||
@@ -230,10 +190,10 @@ fn contract_transfer_oog() {
|
||||
with_externalities(&mut ExtBuilder::default().build(), || {
|
||||
<CodeOf<Test>>::insert(1, code_transfer.to_vec());
|
||||
|
||||
Staking::set_free_balance(&0, 100_000_000);
|
||||
Staking::increase_total_stake_by(100_000_000);
|
||||
Staking::set_free_balance(&1, 11);
|
||||
Staking::increase_total_stake_by(11);
|
||||
Balances::set_free_balance(&0, 100_000_000);
|
||||
Balances::increase_total_stake_by(100_000_000);
|
||||
Balances::set_free_balance(&1, 11);
|
||||
Balances::increase_total_stake_by(11);
|
||||
|
||||
assert_err!(
|
||||
Contract::call(&0, 1, 3, 276, Vec::new()),
|
||||
@@ -241,15 +201,15 @@ fn contract_transfer_oog() {
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Staking::free_balance(&0),
|
||||
Balances::free_balance(&0),
|
||||
// 3 - value sent with the transaction
|
||||
// 2 * 6 - gas used by the contract (6) multiplied by gas price (2)
|
||||
// 2 * 135 - base gas fee for call (by transaction)
|
||||
// 2 * 135 - base gas fee for call (by contract)
|
||||
100_000_000 - (2 * 6) - (2 * 135) - (2 * 135),
|
||||
);
|
||||
assert_eq!(Staking::free_balance(&1), 11);
|
||||
assert_eq!(Staking::free_balance(&CONTRACT_SHOULD_TRANSFER_TO), 0);
|
||||
assert_eq!(Balances::free_balance(&1), 11);
|
||||
assert_eq!(Balances::free_balance(&CONTRACT_SHOULD_TRANSFER_TO), 0);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -262,10 +222,10 @@ fn contract_transfer_max_depth() {
|
||||
with_externalities(&mut ExtBuilder::default().build(), || {
|
||||
<CodeOf<Test>>::insert(CONTRACT_SHOULD_TRANSFER_TO, code_transfer.to_vec());
|
||||
|
||||
Staking::set_free_balance(&0, 100_000_000);
|
||||
Staking::increase_total_stake_by(100_000_000);
|
||||
Staking::set_free_balance(&CONTRACT_SHOULD_TRANSFER_TO, 11);
|
||||
Staking::increase_total_stake_by(11);
|
||||
Balances::set_free_balance(&0, 100_000_000);
|
||||
Balances::increase_total_stake_by(100_000_000);
|
||||
Balances::set_free_balance(&CONTRACT_SHOULD_TRANSFER_TO, 11);
|
||||
Balances::increase_total_stake_by(11);
|
||||
|
||||
assert_err!(
|
||||
Contract::call(&0, CONTRACT_SHOULD_TRANSFER_TO, 3, 100_000, Vec::new()),
|
||||
@@ -273,14 +233,14 @@ fn contract_transfer_max_depth() {
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Staking::free_balance(&0),
|
||||
Balances::free_balance(&0),
|
||||
// 3 - value sent with the transaction
|
||||
// 2 * 6 * 100 - gas used by the contract (6) multiplied by gas price (2)
|
||||
// multiplied by max depth (100).
|
||||
// 2 * 135 * 100 - base gas fee for call (by transaction) multiplied by max depth (100).
|
||||
100_000_000 - (2 * 135 * 100) - (2 * 6 * 100),
|
||||
);
|
||||
assert_eq!(Staking::free_balance(&CONTRACT_SHOULD_TRANSFER_TO), 11);
|
||||
assert_eq!(Balances::free_balance(&CONTRACT_SHOULD_TRANSFER_TO), 11);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -360,11 +320,11 @@ fn contract_create() {
|
||||
let code_create = wabt::wat2wasm(&code_create(&code_ctor_transfer)).unwrap();
|
||||
|
||||
with_externalities(&mut ExtBuilder::default().build(), || {
|
||||
Staking::set_free_balance(&0, 100_000_000);
|
||||
Staking::increase_total_stake_by(100_000_000);
|
||||
Staking::set_free_balance(&1, 0);
|
||||
Staking::set_free_balance(&9, 30);
|
||||
Staking::increase_total_stake_by(30);
|
||||
Balances::set_free_balance(&0, 100_000_000);
|
||||
Balances::increase_total_stake_by(100_000_000);
|
||||
Balances::set_free_balance(&1, 0);
|
||||
Balances::set_free_balance(&9, 30);
|
||||
Balances::increase_total_stake_by(30);
|
||||
|
||||
<CodeOf<Test>>::insert(1, code_create.to_vec());
|
||||
|
||||
@@ -383,23 +343,23 @@ fn contract_create() {
|
||||
// ((21 / 2) * 2) - price per account creation
|
||||
let expected_gas_after_create =
|
||||
100_000_000 - 11 - (2 * 128) - (2 * 135) - (2 * 175) - ((21 / 2) * 2);
|
||||
assert_eq!(Staking::free_balance(&0), expected_gas_after_create);
|
||||
assert_eq!(Staking::free_balance(&1), 8);
|
||||
assert_eq!(Staking::free_balance(&derived_address), 3);
|
||||
assert_eq!(Balances::free_balance(&0), expected_gas_after_create);
|
||||
assert_eq!(Balances::free_balance(&1), 8);
|
||||
assert_eq!(Balances::free_balance(&derived_address), 3);
|
||||
|
||||
// Initiate transfer to the newly created contract.
|
||||
assert_ok!(Contract::call(&0, derived_address, 22, 100_000, Vec::new()));
|
||||
|
||||
assert_eq!(
|
||||
Staking::free_balance(&0),
|
||||
Balances::free_balance(&0),
|
||||
// 22 - value sent with the transaction
|
||||
// (2 * 6) - gas used by the contract
|
||||
// (2 * 135) - base gas fee for call (top level)
|
||||
// (2 * 135) - base gas fee for call (by transfer contract)
|
||||
expected_gas_after_create - 22 - (2 * 6) - (2 * 135) - (2 * 135),
|
||||
);
|
||||
assert_eq!(Staking::free_balance(&derived_address), 22 - 3);
|
||||
assert_eq!(Staking::free_balance(&9), 36);
|
||||
assert_eq!(Balances::free_balance(&derived_address), 22 - 3);
|
||||
assert_eq!(Balances::free_balance(&9), 36);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -414,10 +374,10 @@ fn top_level_create() {
|
||||
&0,
|
||||
);
|
||||
|
||||
Staking::set_free_balance(&0, 100_000_000);
|
||||
Staking::increase_total_stake_by(100_000_000);
|
||||
Staking::set_free_balance(&derived_address, 30);
|
||||
Staking::increase_total_stake_by(30);
|
||||
Balances::set_free_balance(&0, 100_000_000);
|
||||
Balances::increase_total_stake_by(100_000_000);
|
||||
Balances::set_free_balance(&derived_address, 30);
|
||||
Balances::increase_total_stake_by(30);
|
||||
|
||||
assert_ok!(Contract::create(
|
||||
&0,
|
||||
@@ -432,10 +392,10 @@ fn top_level_create() {
|
||||
// (3 * 175) - base gas fee for create (175) (top level) multipled by gas price (3)
|
||||
// ((21 / 3) * 3) - price for contract creation
|
||||
assert_eq!(
|
||||
Staking::free_balance(&0),
|
||||
Balances::free_balance(&0),
|
||||
100_000_000 - 11 - (3 * 122) - (3 * 175) - ((21 / 3) * 3)
|
||||
);
|
||||
assert_eq!(Staking::free_balance(&derived_address), 30 + 11);
|
||||
assert_eq!(Balances::free_balance(&derived_address), 30 + 11);
|
||||
|
||||
assert_eq!(<CodeOf<Test>>::get(&derived_address), code_transfer);
|
||||
});
|
||||
@@ -456,12 +416,12 @@ fn refunds_unused_gas() {
|
||||
with_externalities(&mut ExtBuilder::default().build(), || {
|
||||
<CodeOf<Test>>::insert(1, code_nop.to_vec());
|
||||
|
||||
Staking::set_free_balance(&0, 100_000_000);
|
||||
Staking::increase_total_stake_by(100_000_000);
|
||||
Balances::set_free_balance(&0, 100_000_000);
|
||||
Balances::increase_total_stake_by(100_000_000);
|
||||
|
||||
assert_ok!(Contract::call(&0, 1, 0, 100_000, Vec::new()));
|
||||
|
||||
assert_eq!(Staking::free_balance(&0), 100_000_000 - 4 - (2 * 135));
|
||||
assert_eq!(Balances::free_balance(&0), 100_000_000 - 4 - (2 * 135));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -470,12 +430,12 @@ fn call_with_zero_value() {
|
||||
with_externalities(&mut ExtBuilder::default().build(), || {
|
||||
<CodeOf<Test>>::insert(1, vec![]);
|
||||
|
||||
Staking::set_free_balance(&0, 100_000_000);
|
||||
Staking::increase_total_stake_by(100_000_000);
|
||||
Balances::set_free_balance(&0, 100_000_000);
|
||||
Balances::increase_total_stake_by(100_000_000);
|
||||
|
||||
assert_ok!(Contract::call(&0, 1, 0, 100_000, Vec::new()));
|
||||
|
||||
assert_eq!(Staking::free_balance(&0), 100_000_000 - (2 * 135));
|
||||
assert_eq!(Balances::free_balance(&0), 100_000_000 - (2 * 135));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -484,13 +444,13 @@ fn create_with_zero_endowment() {
|
||||
let code_nop = wabt::wat2wasm(CODE_NOP).unwrap();
|
||||
|
||||
with_externalities(&mut ExtBuilder::default().build(), || {
|
||||
Staking::set_free_balance(&0, 100_000_000);
|
||||
Staking::increase_total_stake_by(100_000_000);
|
||||
Balances::set_free_balance(&0, 100_000_000);
|
||||
Balances::increase_total_stake_by(100_000_000);
|
||||
|
||||
assert_ok!(Contract::create(&0, 0, 100_000, code_nop, Vec::new()));
|
||||
|
||||
assert_eq!(
|
||||
Staking::free_balance(&0),
|
||||
Balances::free_balance(&0),
|
||||
// 4 - for the gas spent by the constructor
|
||||
// 2 * 175 - base gas fee for create (175) multiplied by gas price (2) (top level)
|
||||
100_000_000 - 4 - (2 * 175),
|
||||
@@ -505,13 +465,13 @@ fn account_removal_removes_storage() {
|
||||
|| {
|
||||
// Setup two accounts with free balance above than exsistential threshold.
|
||||
{
|
||||
Staking::set_free_balance(&1, 110);
|
||||
Staking::increase_total_stake_by(110);
|
||||
Balances::set_free_balance(&1, 110);
|
||||
Balances::increase_total_stake_by(110);
|
||||
<StorageOf<Test>>::insert(1, b"foo".to_vec(), b"1".to_vec());
|
||||
<StorageOf<Test>>::insert(1, b"bar".to_vec(), b"2".to_vec());
|
||||
|
||||
Staking::set_free_balance(&2, 110);
|
||||
Staking::increase_total_stake_by(110);
|
||||
Balances::set_free_balance(&2, 110);
|
||||
Balances::increase_total_stake_by(110);
|
||||
<StorageOf<Test>>::insert(2, b"hello".to_vec(), b"3".to_vec());
|
||||
<StorageOf<Test>>::insert(2, b"world".to_vec(), b"4".to_vec());
|
||||
}
|
||||
@@ -520,7 +480,7 @@ fn account_removal_removes_storage() {
|
||||
// the balance of account 1 is will be below than exsistential threshold.
|
||||
//
|
||||
// This should lead to the removal of all storage associated with this account.
|
||||
assert_ok!(Staking::transfer(&1, 2.into(), 20));
|
||||
assert_ok!(Balances::transfer(&1, 2.into(), 20));
|
||||
|
||||
// Verify that all entries from account 1 is removed, while
|
||||
// entries from account 2 is in place.
|
||||
@@ -556,15 +516,15 @@ fn top_level_call_refunds_even_if_fails() {
|
||||
with_externalities(&mut ExtBuilder::default().gas_price(4).build(), || {
|
||||
<CodeOf<Test>>::insert(1, code_unreachable.to_vec());
|
||||
|
||||
Staking::set_free_balance(&0, 100_000_000);
|
||||
Staking::increase_total_stake_by(100_000_000);
|
||||
Balances::set_free_balance(&0, 100_000_000);
|
||||
Balances::increase_total_stake_by(100_000_000);
|
||||
|
||||
assert_err!(
|
||||
Contract::call(&0, 1, 0, 100_000, Vec::new()),
|
||||
"vm execute returned error while call"
|
||||
);
|
||||
|
||||
assert_eq!(Staking::free_balance(&0), 100_000_000 - (4 * 3) - (4 * 135));
|
||||
assert_eq!(Balances::free_balance(&0), 100_000_000 - (4 * 3) - (4 * 135));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -586,8 +546,8 @@ fn block_gas_limit() {
|
||||
|| {
|
||||
<CodeOf<Test>>::insert(1, code_loop.to_vec());
|
||||
|
||||
Staking::set_free_balance(&0, 100_000_000);
|
||||
Staking::increase_total_stake_by(100_000_000);
|
||||
Balances::set_free_balance(&0, 100_000_000);
|
||||
Balances::increase_total_stake_by(100_000_000);
|
||||
|
||||
// Spend 50_000 units of gas (OOG).
|
||||
assert_err!(
|
||||
|
||||
@@ -21,12 +21,10 @@ use exec::{CallReceipt, CreateReceipt};
|
||||
use gas::{GasMeter, GasMeterResult};
|
||||
use rstd::prelude::*;
|
||||
use runtime_primitives::traits::{As, CheckedMul};
|
||||
use sandbox;
|
||||
use staking;
|
||||
use system;
|
||||
use {sandbox, balances, system};
|
||||
use Trait;
|
||||
|
||||
type BalanceOf<T> = <T as staking::Trait>::Balance;
|
||||
type BalanceOf<T> = <T as balances::Trait>::Balance;
|
||||
type AccountIdOf<T> = <T as system::Trait>::AccountId;
|
||||
|
||||
mod prepare;
|
||||
|
||||
@@ -17,14 +17,10 @@ substrate-runtime-io = { path = "../../runtime-io", default_features = false }
|
||||
substrate-runtime-support = { path = "../../runtime-support", default_features = false }
|
||||
substrate-runtime-primitives = { path = "../primitives", default_features = false }
|
||||
substrate-runtime-consensus = { path = "../consensus", default_features = false }
|
||||
substrate-runtime-balances = { path = "../balances", default_features = false }
|
||||
substrate-runtime-democracy = { path = "../democracy", default_features = false }
|
||||
substrate-runtime-session = { path = "../session", default_features = false }
|
||||
substrate-runtime-staking = { path = "../staking", default_features = false }
|
||||
substrate-runtime-system = { path = "../system", default_features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
substrate-runtime-timestamp = { path = "../timestamp" }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
@@ -38,9 +34,7 @@ std = [
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-runtime-support/std",
|
||||
"substrate-runtime-primitives/std",
|
||||
"substrate-runtime-consensus/std",
|
||||
"substrate-runtime-balances/std",
|
||||
"substrate-runtime-democracy/std",
|
||||
"substrate-runtime-session/std",
|
||||
"substrate-runtime-staking/std",
|
||||
"substrate-runtime-system/std",
|
||||
]
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate Demo is free software: you can redistribute it and/or modify
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate Demo is distributed in the hope that it will be useful,
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Council system: Handles the voting in and maintenance of council members.
|
||||
|
||||
@@ -33,19 +33,15 @@ extern crate substrate_primitives;
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
#[macro_use] extern crate substrate_runtime_support;
|
||||
extern crate substrate_runtime_primitives as primitives;
|
||||
extern crate substrate_runtime_consensus as consensus;
|
||||
extern crate substrate_runtime_balances as balances;
|
||||
extern crate substrate_runtime_democracy as democracy;
|
||||
extern crate substrate_runtime_session as session;
|
||||
extern crate substrate_runtime_staking as staking;
|
||||
extern crate substrate_runtime_system as system;
|
||||
#[cfg(test)]
|
||||
extern crate substrate_runtime_timestamp as timestamp;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use primitives::traits::{Zero, One, RefInto, As, AuxLookup};
|
||||
use substrate_runtime_support::{StorageValue, StorageMap};
|
||||
use substrate_runtime_support::dispatch::Result;
|
||||
use staking::address::Address;
|
||||
use balances::address::Address;
|
||||
#[cfg(any(feature = "std", test))]
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -245,7 +241,7 @@ impl<T: Trait> Module<T> {
|
||||
if !<LastActiveOf<T>>::exists(aux.ref_into()) {
|
||||
// not yet a voter - deduct bond.
|
||||
// NOTE: this must be the last potential bailer, since it changes state.
|
||||
<staking::Module<T>>::reserve(aux.ref_into(), Self::voting_bond())?;
|
||||
<balances::Module<T>>::reserve(aux.ref_into(), Self::voting_bond())?;
|
||||
|
||||
<Voters<T>>::put({
|
||||
let mut v = Self::voters();
|
||||
@@ -270,7 +266,7 @@ impl<T: Trait> Module<T> {
|
||||
who_index: u32,
|
||||
assumed_vote_index: VoteIndex
|
||||
) -> Result {
|
||||
let who = <staking::Module<T>>::lookup(who)?;
|
||||
let who = <balances::Module<T>>::lookup(who)?;
|
||||
ensure!(!Self::presentation_active(), "cannot reap during presentation period");
|
||||
ensure!(Self::voter_last_active(aux.ref_into()).is_some(), "reaper must be a voter");
|
||||
let last_active = Self::voter_last_active(&who).ok_or("target for inactivity cleanup must be active")?;
|
||||
@@ -300,9 +296,9 @@ impl<T: Trait> Module<T> {
|
||||
if valid {
|
||||
// This only fails if `who` doesn't exist, which it clearly must do since its the aux.
|
||||
// Still, it's no more harmful to propagate any error at this point.
|
||||
<staking::Module<T>>::transfer_reserved(&who, aux.ref_into(), Self::voting_bond())?;
|
||||
<balances::Module<T>>::repatriate_reserved(&who, aux.ref_into(), Self::voting_bond())?;
|
||||
} else {
|
||||
<staking::Module<T>>::slash_reserved(aux.ref_into(), Self::voting_bond());
|
||||
<balances::Module<T>>::slash_reserved(aux.ref_into(), Self::voting_bond());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -317,7 +313,7 @@ impl<T: Trait> Module<T> {
|
||||
ensure!(&voters[index] == aux.ref_into(), "retraction index mismatch");
|
||||
|
||||
Self::remove_voter(aux.ref_into(), index, voters);
|
||||
<staking::Module<T>>::unreserve(aux.ref_into(), Self::voting_bond());
|
||||
<balances::Module<T>>::unreserve(aux.ref_into(), Self::voting_bond());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -335,7 +331,7 @@ impl<T: Trait> Module<T> {
|
||||
"invalid candidate slot"
|
||||
);
|
||||
// NOTE: This must be last as it has side-effects.
|
||||
<staking::Module<T>>::reserve(aux.ref_into(), Self::candidacy_bond())
|
||||
<balances::Module<T>>::reserve(aux.ref_into(), Self::candidacy_bond())
|
||||
.map_err(|_| "candidate has not enough funds")?;
|
||||
|
||||
let mut candidates = candidates;
|
||||
@@ -359,13 +355,13 @@ impl<T: Trait> Module<T> {
|
||||
total: T::Balance,
|
||||
index: VoteIndex
|
||||
) -> Result {
|
||||
let candidate = <staking::Module<T>>::lookup(candidate)?;
|
||||
let candidate = <balances::Module<T>>::lookup(candidate)?;
|
||||
ensure!(index == Self::vote_index(), "index not current");
|
||||
let (_, _, expiring) = Self::next_finalise().ok_or("cannot present outside of presentation period")?;
|
||||
let stakes = Self::snapshoted_stakes();
|
||||
let voters = Self::voters();
|
||||
let bad_presentation_punishment = Self::present_slash_per_voter() * T::Balance::sa(voters.len() as u64);
|
||||
ensure!(<staking::Module<T>>::can_slash(aux.ref_into(), bad_presentation_punishment), "presenter must have sufficient slashable funds");
|
||||
ensure!(<balances::Module<T>>::can_slash(aux.ref_into(), bad_presentation_punishment), "presenter must have sufficient slashable funds");
|
||||
|
||||
let mut leaderboard = Self::leaderboard().ok_or("leaderboard must exist while present phase active")?;
|
||||
ensure!(total > leaderboard[0].0, "candidate not worthy of leaderboard");
|
||||
@@ -396,7 +392,7 @@ impl<T: Trait> Module<T> {
|
||||
} else {
|
||||
// we can rest assured it will be Ok since we checked `can_slash` earlier; still
|
||||
// better safe than sorry.
|
||||
let _ = <staking::Module<T>>::slash(aux.ref_into(), bad_presentation_punishment);
|
||||
let _ = <balances::Module<T>>::slash(aux.ref_into(), bad_presentation_punishment);
|
||||
Err(if dupe { "duplicate presentation" } else { "incorrect total" })
|
||||
}
|
||||
}
|
||||
@@ -413,7 +409,7 @@ impl<T: Trait> Module<T> {
|
||||
/// period) to fill the seat if removal means that the desired members are not met.
|
||||
/// This is effective immediately.
|
||||
fn remove_member(who: Address<T::AccountId, T::AccountIndex>) -> Result {
|
||||
let who = <staking::Module<T>>::lookup(who)?;
|
||||
let who = <balances::Module<T>>::lookup(who)?;
|
||||
let new_council: Vec<(T::AccountId, T::BlockNumber)> = Self::active_council()
|
||||
.into_iter()
|
||||
.filter(|i| i.0 != who)
|
||||
@@ -473,7 +469,7 @@ impl<T: Trait> Module<T> {
|
||||
<NextFinalise<T>>::put((number + Self::presentation_duration(), empty_seats as u32, expiring));
|
||||
|
||||
let voters = Self::voters();
|
||||
let votes = voters.iter().map(<staking::Module<T>>::voting_balance).collect::<Vec<_>>();
|
||||
let votes = voters.iter().map(<balances::Module<T>>::total_balance).collect::<Vec<_>>();
|
||||
<SnapshotedStakes<T>>::put(votes);
|
||||
|
||||
// initialise leaderboard.
|
||||
@@ -500,7 +496,7 @@ impl<T: Trait> Module<T> {
|
||||
.take_while(|&&(b, _)| !b.is_zero())
|
||||
.take(coming as usize)
|
||||
{
|
||||
<staking::Module<T>>::unreserve(w, candidacy_bond);
|
||||
<balances::Module<T>>::unreserve(w, candidacy_bond);
|
||||
}
|
||||
|
||||
// set the new council.
|
||||
@@ -622,14 +618,14 @@ mod tests {
|
||||
pub use runtime_io::with_externalities;
|
||||
pub use substrate_primitives::H256;
|
||||
use primitives::BuildStorage;
|
||||
use primitives::traits::{HasPublicAux, Identity, BlakeTwo256};
|
||||
use primitives::traits::{HasPublicAux, BlakeTwo256};
|
||||
use primitives::testing::{Digest, Header};
|
||||
use substrate_primitives::KeccakHasher;
|
||||
|
||||
impl_outer_dispatch! {
|
||||
#[derive(Debug, Clone, Eq, Serialize, Deserialize, PartialEq)]
|
||||
pub enum Proposal {
|
||||
Staking = 0,
|
||||
Balances = 0,
|
||||
Democracy = 1,
|
||||
}
|
||||
}
|
||||
@@ -640,11 +636,8 @@ mod tests {
|
||||
impl HasPublicAux for Test {
|
||||
type PublicAux = u64;
|
||||
}
|
||||
impl consensus::Trait for Test {
|
||||
type PublicAux = <Self as HasPublicAux>::PublicAux;
|
||||
type SessionKey = u64;
|
||||
}
|
||||
impl system::Trait for Test {
|
||||
type PublicAux = <Self as HasPublicAux>::PublicAux;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
@@ -654,54 +647,28 @@ mod tests {
|
||||
type Header = Header;
|
||||
type Event = ();
|
||||
}
|
||||
impl session::Trait for Test {
|
||||
type ConvertAccountIdToSessionKey = Identity;
|
||||
type OnSessionChange = staking::Module<Test>;
|
||||
type Event = ();
|
||||
}
|
||||
impl staking::Trait for Test {
|
||||
const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1;
|
||||
impl balances::Trait for Test {
|
||||
type Balance = u64;
|
||||
type AccountIndex = u64;
|
||||
type OnFreeBalanceZero = ();
|
||||
type EnsureAccountLiquid = ();
|
||||
type Event = ();
|
||||
}
|
||||
impl democracy::Trait for Test {
|
||||
type Proposal = Proposal;
|
||||
}
|
||||
impl timestamp::Trait for Test {
|
||||
const TIMESTAMP_SET_POSITION: u32 = 0;
|
||||
type Moment = u64;
|
||||
}
|
||||
impl Trait for Test {}
|
||||
|
||||
pub fn new_test_ext(with_council: bool) -> runtime_io::TestExternalities<KeccakHasher> {
|
||||
let mut t = system::GenesisConfig::<Test>::default().build_storage().unwrap();
|
||||
t.extend(consensus::GenesisConfig::<Test>{
|
||||
code: vec![],
|
||||
authorities: vec![],
|
||||
}.build_storage().unwrap());
|
||||
t.extend(session::GenesisConfig::<Test>{
|
||||
session_length: 1, //??? or 2?
|
||||
validators: vec![10, 20],
|
||||
}.build_storage().unwrap());
|
||||
t.extend(staking::GenesisConfig::<Test>{
|
||||
sessions_per_era: 1,
|
||||
current_era: 0,
|
||||
t.extend(balances::GenesisConfig::<Test>{
|
||||
balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)],
|
||||
intentions: vec![],
|
||||
validator_count: 2,
|
||||
minimum_validator_count: 0,
|
||||
bonding_duration: 0,
|
||||
transaction_base_fee: 0,
|
||||
transaction_byte_fee: 0,
|
||||
existential_deposit: 0,
|
||||
transfer_fee: 0,
|
||||
creation_fee: 0,
|
||||
reclaim_rebate: 0,
|
||||
early_era_slash: 0,
|
||||
session_reward: 0,
|
||||
offline_slash_grace: 0,
|
||||
}.build_storage().unwrap());
|
||||
t.extend(democracy::GenesisConfig::<Test>{
|
||||
launch_period: 1,
|
||||
@@ -726,12 +693,11 @@ mod tests {
|
||||
cooloff_period: 2,
|
||||
voting_period: 1,
|
||||
}.build_storage().unwrap());
|
||||
t.extend(timestamp::GenesisConfig::<Test>::default().build_storage().unwrap());
|
||||
t.into()
|
||||
}
|
||||
|
||||
pub type System = system::Module<Test>;
|
||||
pub type Staking = staking::Module<Test>;
|
||||
pub type Balances = balances::Module<Test>;
|
||||
pub type Democracy = democracy::Module<Test>;
|
||||
pub type Council = Module<Test>;
|
||||
|
||||
@@ -1045,7 +1011,7 @@ mod tests {
|
||||
#[test]
|
||||
fn double_presentations_should_be_punished() {
|
||||
with_externalities(&mut new_test_ext(false), || {
|
||||
assert!(Staking::can_slash(&4, 10));
|
||||
assert!(Balances::can_slash(&4, 10));
|
||||
|
||||
System::set_block_number(4);
|
||||
assert_ok!(Council::submit_candidacy(&2, 0));
|
||||
@@ -1061,7 +1027,7 @@ mod tests {
|
||||
assert_ok!(Council::end_block(System::block_number()));
|
||||
|
||||
assert_eq!(Council::active_council(), vec![(5, 11), (2, 11)]);
|
||||
assert_eq!(Staking::voting_balance(&4), 38);
|
||||
assert_eq!(Balances::total_balance(&4), 38);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1094,8 +1060,8 @@ mod tests {
|
||||
|
||||
assert_eq!(Council::voters(), vec![5]);
|
||||
assert_eq!(Council::approvals_of(2).len(), 0);
|
||||
assert_eq!(Staking::voting_balance(&2), 17);
|
||||
assert_eq!(Staking::voting_balance(&5), 53);
|
||||
assert_eq!(Balances::total_balance(&2), 17);
|
||||
assert_eq!(Balances::total_balance(&5), 53);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1153,8 +1119,8 @@ mod tests {
|
||||
|
||||
assert_eq!(Council::voters(), vec![5]);
|
||||
assert_eq!(Council::approvals_of(2).len(), 0);
|
||||
assert_eq!(Staking::voting_balance(&2), 17);
|
||||
assert_eq!(Staking::voting_balance(&5), 53);
|
||||
assert_eq!(Balances::total_balance(&2), 17);
|
||||
assert_eq!(Balances::total_balance(&5), 53);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1254,7 +1220,7 @@ mod tests {
|
||||
|
||||
assert_eq!(Council::voters(), vec![2, 3, 5]);
|
||||
assert_eq!(Council::approvals_of(4).len(), 0);
|
||||
assert_eq!(Staking::voting_balance(&4), 37);
|
||||
assert_eq!(Balances::total_balance(&4), 37);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1381,8 +1347,8 @@ mod tests {
|
||||
assert_ok!(Council::end_block(System::block_number()));
|
||||
|
||||
System::set_block_number(6);
|
||||
assert_eq!(Staking::free_balance(&1), 1);
|
||||
assert_eq!(Staking::reserved_balance(&1), 9);
|
||||
assert_eq!(Balances::free_balance(&1), 1);
|
||||
assert_eq!(Balances::reserved_balance(&1), 9);
|
||||
assert_noop!(Council::present_winner(&1, 1.into(), 20, 0), "presenter must have sufficient slashable funds");
|
||||
});
|
||||
}
|
||||
@@ -1392,7 +1358,7 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(false), || {
|
||||
System::set_block_number(4);
|
||||
assert!(!Council::presentation_active());
|
||||
assert_eq!(Staking::voting_balance(&4), 40);
|
||||
assert_eq!(Balances::total_balance(&4), 40);
|
||||
|
||||
assert_ok!(Council::submit_candidacy(&2, 0));
|
||||
assert_ok!(Council::submit_candidacy(&5, 1));
|
||||
@@ -1403,7 +1369,7 @@ mod tests {
|
||||
System::set_block_number(6);
|
||||
assert_err!(Council::present_winner(&4, 2.into(), 80, 0), "incorrect total");
|
||||
|
||||
assert_eq!(Staking::voting_balance(&4), 38);
|
||||
assert_eq!(Balances::total_balance(&4), 38);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate Demo is free software: you can redistribute it and/or modify
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate Demo is distributed in the hope that it will be useful,
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Council voting system.
|
||||
|
||||
@@ -227,7 +227,7 @@ mod tests {
|
||||
fn basic_environment_works() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
assert_eq!(Staking::bonding_duration(), 0);
|
||||
assert_eq!(Balances::free_balance(&42), 0);
|
||||
assert_eq!(CouncilVoting::cooloff_period(), 2);
|
||||
assert_eq!(CouncilVoting::voting_period(), 1);
|
||||
assert_eq!(CouncilVoting::will_still_be_councillor_at(&1, 1), true);
|
||||
@@ -243,8 +243,8 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
fn bonding_duration_proposal(value: u64) -> Proposal {
|
||||
Proposal::Staking(staking::PrivCall::set_bonding_duration(value))
|
||||
fn set_balance_proposal(value: u64) -> Proposal {
|
||||
Proposal::Balances(balances::PrivCall::set_balance(balances::address::Address::Id(42), value, 0))
|
||||
}
|
||||
|
||||
fn cancel_referendum_proposal(id: u32) -> Proposal {
|
||||
@@ -255,7 +255,7 @@ mod tests {
|
||||
fn referendum_cancellation_should_work_when_unanimous() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let proposal = set_balance_proposal(42);
|
||||
assert_ok!(Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove), 0);
|
||||
assert_eq!(Democracy::active_referendums(), vec![(0, 4, proposal, VoteThreshold::SuperMajorityApprove)]);
|
||||
|
||||
@@ -270,7 +270,7 @@ mod tests {
|
||||
System::set_block_number(2);
|
||||
assert_ok!(CouncilVoting::end_block(System::block_number()));
|
||||
assert_eq!(Democracy::active_referendums(), vec![]);
|
||||
assert_eq!(Staking::bonding_duration(), 0);
|
||||
assert_eq!(Balances::free_balance(&42), 0);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -278,7 +278,7 @@ mod tests {
|
||||
fn referendum_cancellation_should_fail_when_not_unanimous() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let proposal = set_balance_proposal(42);
|
||||
assert_ok!(Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove), 0);
|
||||
|
||||
let cancellation = cancel_referendum_proposal(0);
|
||||
@@ -298,7 +298,7 @@ mod tests {
|
||||
fn referendum_cancellation_should_fail_when_abstentions() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let proposal = set_balance_proposal(42);
|
||||
assert_ok!(Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove), 0);
|
||||
|
||||
let cancellation = cancel_referendum_proposal(0);
|
||||
@@ -317,7 +317,7 @@ mod tests {
|
||||
fn veto_should_work() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let proposal = set_balance_proposal(42);
|
||||
let hash = proposal.blake2_256().into();
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::veto(&2, hash));
|
||||
@@ -330,7 +330,7 @@ mod tests {
|
||||
fn double_veto_should_not_work() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let proposal = set_balance_proposal(42);
|
||||
let hash = proposal.blake2_256().into();
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::veto(&2, hash));
|
||||
@@ -345,7 +345,7 @@ mod tests {
|
||||
fn retry_in_cooloff_should_not_work() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let proposal = set_balance_proposal(42);
|
||||
let hash = proposal.blake2_256().into();
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::veto(&2, hash));
|
||||
@@ -359,7 +359,7 @@ mod tests {
|
||||
fn retry_after_cooloff_should_work() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let proposal = set_balance_proposal(42);
|
||||
let hash = proposal.blake2_256().into();
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::veto(&2, hash));
|
||||
@@ -373,7 +373,7 @@ mod tests {
|
||||
System::set_block_number(4);
|
||||
assert_ok!(CouncilVoting::end_block(System::block_number()));
|
||||
assert_eq!(CouncilVoting::proposals().len(), 0);
|
||||
assert_eq!(Democracy::active_referendums(), vec![(0, 7, bonding_duration_proposal(42), VoteThreshold::SimpleMajority)]);
|
||||
assert_eq!(Democracy::active_referendums(), vec![(0, 7, set_balance_proposal(42), VoteThreshold::SimpleMajority)]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -381,7 +381,7 @@ mod tests {
|
||||
fn alternative_double_veto_should_work() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let proposal = set_balance_proposal(42);
|
||||
let hash = proposal.blake2_256().into();
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::veto(&2, hash));
|
||||
@@ -398,7 +398,7 @@ mod tests {
|
||||
fn simple_propose_should_work() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let proposal = set_balance_proposal(42);
|
||||
let hash = proposal.blake2_256().into();
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
|
||||
assert_eq!(CouncilVoting::proposals().len(), 1);
|
||||
@@ -412,7 +412,7 @@ mod tests {
|
||||
fn unvoted_proposal_should_expire_without_action() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let proposal = set_balance_proposal(42);
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
|
||||
assert_eq!(CouncilVoting::tally(&proposal.blake2_256().into()), (1, 0, 2));
|
||||
assert_ok!(CouncilVoting::end_block(System::block_number()));
|
||||
@@ -428,7 +428,7 @@ mod tests {
|
||||
fn unanimous_proposal_should_expire_with_biased_referendum() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let proposal = set_balance_proposal(42);
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::vote(&2, proposal.blake2_256().into(), true));
|
||||
assert_ok!(CouncilVoting::vote(&3, proposal.blake2_256().into(), true));
|
||||
@@ -446,7 +446,7 @@ mod tests {
|
||||
fn majority_proposal_should_expire_with_unbiased_referendum() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let proposal = set_balance_proposal(42);
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::vote(&2, proposal.blake2_256().into(), true));
|
||||
assert_ok!(CouncilVoting::vote(&3, proposal.blake2_256().into(), false));
|
||||
@@ -464,7 +464,7 @@ mod tests {
|
||||
fn propose_by_public_should_not_work() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = bonding_duration_proposal(42);
|
||||
let proposal = set_balance_proposal(42);
|
||||
assert_noop!(CouncilVoting::propose(&4, Box::new(proposal)), "proposer would not be on council");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -15,14 +15,10 @@ substrate-runtime-std = { path = "../../runtime-std", default_features = false }
|
||||
substrate-runtime-io = { path = "../../runtime-io", default_features = false }
|
||||
substrate-runtime-support = { path = "../../runtime-support", default_features = false }
|
||||
substrate-runtime-primitives = { path = "../primitives", default_features = false }
|
||||
substrate-runtime-balances = { path = "../balances", default_features = false }
|
||||
substrate-runtime-consensus = { path = "../consensus", default_features = false }
|
||||
substrate-runtime-session = { path = "../session", default_features = false }
|
||||
substrate-runtime-staking = { path = "../staking", default_features = false }
|
||||
substrate-runtime-system = { path = "../system", default_features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
substrate-runtime-timestamp = { path = "../timestamp" }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
@@ -35,8 +31,6 @@ std = [
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-runtime-support/std",
|
||||
"substrate-runtime-primitives/std",
|
||||
"substrate-runtime-consensus/std",
|
||||
"substrate-runtime-session/std",
|
||||
"substrate-runtime-staking/std",
|
||||
"substrate-runtime-balances/std",
|
||||
"substrate-runtime-system/std",
|
||||
]
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate Demo is free software: you can redistribute it and/or modify
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate Demo is distributed in the hope that it will be useful,
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Democratic system: Handles administration of general stakeholder voting.
|
||||
|
||||
@@ -38,12 +38,8 @@ extern crate substrate_runtime_support;
|
||||
extern crate substrate_codec as codec;
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
extern crate substrate_runtime_primitives as primitives;
|
||||
extern crate substrate_runtime_consensus as consensus;
|
||||
extern crate substrate_runtime_session as session;
|
||||
extern crate substrate_runtime_staking as staking;
|
||||
extern crate substrate_runtime_balances as balances;
|
||||
extern crate substrate_runtime_system as system;
|
||||
#[cfg(test)]
|
||||
extern crate substrate_runtime_timestamp as timestamp;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::result;
|
||||
@@ -62,7 +58,7 @@ pub type PropIndex = u32;
|
||||
/// A referendum index.
|
||||
pub type ReferendumIndex = u32;
|
||||
|
||||
pub trait Trait: staking::Trait + Sized {
|
||||
pub trait Trait: balances::Trait + Sized {
|
||||
type Proposal: Parameter + Dispatchable + IsSubType<Module<Self>> + MaybeSerializeDebug;
|
||||
}
|
||||
|
||||
@@ -152,7 +148,7 @@ impl<T: Trait> Module<T> {
|
||||
/// Get the voters for the current proposal.
|
||||
pub fn tally(ref_index: ReferendumIndex) -> (T::Balance, T::Balance) {
|
||||
Self::voters_for(ref_index).iter()
|
||||
.map(|a| (<staking::Module<T>>::voting_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(|a| (<balances::Module<T>>::total_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) })
|
||||
.fold((Zero::zero(), Zero::zero()), |(a, b), (c, d)| (a + c, b + d))
|
||||
}
|
||||
@@ -162,7 +158,7 @@ impl<T: Trait> Module<T> {
|
||||
/// Propose a sensitive action to be taken.
|
||||
fn propose(aux: &T::PublicAux, proposal: Box<T::Proposal>, value: T::Balance) -> Result {
|
||||
ensure!(value >= Self::minimum_deposit(), "value too low");
|
||||
<staking::Module<T>>::reserve(aux.ref_into(), value)
|
||||
<balances::Module<T>>::reserve(aux.ref_into(), value)
|
||||
.map_err(|_| "proposer's balance too low")?;
|
||||
|
||||
let index = Self::public_prop_count();
|
||||
@@ -179,7 +175,7 @@ impl<T: Trait> Module<T> {
|
||||
fn second(aux: &T::PublicAux, proposal: PropIndex) -> Result {
|
||||
let mut deposit = Self::deposit_of(proposal)
|
||||
.ok_or("can only second an existing proposal")?;
|
||||
<staking::Module<T>>::reserve(aux.ref_into(), deposit.0)
|
||||
<balances::Module<T>>::reserve(aux.ref_into(), deposit.0)
|
||||
.map_err(|_| "seconder's balance too low")?;
|
||||
deposit.1.push(aux.ref_into().clone());
|
||||
<DepositOf<T>>::insert(proposal, deposit);
|
||||
@@ -190,7 +186,7 @@ impl<T: Trait> Module<T> {
|
||||
/// false would be a vote to keep the status quo..
|
||||
fn vote(aux: &T::PublicAux, ref_index: ReferendumIndex, approve_proposal: bool) -> Result {
|
||||
ensure!(Self::is_active_referendum(ref_index), "vote given for invalid referendum.");
|
||||
ensure!(!<staking::Module<T>>::voting_balance(aux.ref_into()).is_zero(),
|
||||
ensure!(!<balances::Module<T>>::total_balance(aux.ref_into()).is_zero(),
|
||||
"transactor must have balance to signal approval.");
|
||||
if !<VoteOf<T>>::exists(&(ref_index, aux.ref_into().clone())) {
|
||||
let mut voters = Self::voters_for(ref_index);
|
||||
@@ -268,7 +264,7 @@ impl<T: Trait> Module<T> {
|
||||
if let Some((deposit, depositors)) = <DepositOf<T>>::take(prop_index) {//: (T::Balance, Vec<T::AccountId>) =
|
||||
// refund depositors
|
||||
for d in &depositors {
|
||||
<staking::Module<T>>::unreserve(d, deposit);
|
||||
<balances::Module<T>>::unreserve(d, deposit);
|
||||
}
|
||||
<PublicProps<T>>::put(public_props);
|
||||
Self::inject_referendum(now + Self::voting_period(), proposal, VoteThreshold::SuperMajorityApprove)?;
|
||||
@@ -281,7 +277,7 @@ impl<T: Trait> Module<T> {
|
||||
// tally up votes for any expiring referenda.
|
||||
for (index, _, proposal, vote_threshold) in Self::maturing_referendums_at(now) {
|
||||
let (approve, against) = Self::tally(index);
|
||||
let total_stake = <staking::Module<T>>::total_stake();
|
||||
let total_stake = <balances::Module<T>>::total_stake();
|
||||
Self::clear_referendum(index);
|
||||
if vote_threshold.approved(approve, against, total_stake) {
|
||||
proposal.dispatch()?;
|
||||
@@ -319,14 +315,6 @@ impl<T: Trait> GenesisConfig<T> {
|
||||
minimum_deposit: T::Balance::sa(1),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extended() -> Self {
|
||||
GenesisConfig {
|
||||
launch_period: T::BlockNumber::sa(1),
|
||||
voting_period: T::BlockNumber::sa(3),
|
||||
minimum_deposit: T::Balance::sa(1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
@@ -363,16 +351,14 @@ mod tests {
|
||||
use runtime_io::with_externalities;
|
||||
use substrate_primitives::{H256, KeccakHasher};
|
||||
use primitives::BuildStorage;
|
||||
use primitives::traits::{HasPublicAux, Identity, BlakeTwo256};
|
||||
use primitives::traits::{HasPublicAux, BlakeTwo256};
|
||||
use primitives::testing::{Digest, Header};
|
||||
use session::OnSessionChange;
|
||||
|
||||
impl_outer_dispatch! {
|
||||
#[derive(Debug, Clone, Eq, Serialize, Deserialize, PartialEq)]
|
||||
pub enum Proposal {
|
||||
Session = 0,
|
||||
Staking = 1,
|
||||
Democracy = 2,
|
||||
Balances = 0,
|
||||
Democracy = 1,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,11 +368,8 @@ mod tests {
|
||||
impl HasPublicAux for Test {
|
||||
type PublicAux = u64;
|
||||
}
|
||||
impl consensus::Trait for Test {
|
||||
type PublicAux = <Self as HasPublicAux>::PublicAux;
|
||||
type SessionKey = u64;
|
||||
}
|
||||
impl system::Trait for Test {
|
||||
type PublicAux = <Self as HasPublicAux>::PublicAux;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
@@ -396,66 +379,38 @@ mod tests {
|
||||
type Header = Header;
|
||||
type Event = ();
|
||||
}
|
||||
impl session::Trait for Test {
|
||||
type ConvertAccountIdToSessionKey = Identity;
|
||||
type OnSessionChange = staking::Module<Test>;
|
||||
type Event = ();
|
||||
}
|
||||
impl staking::Trait for Test {
|
||||
const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1;
|
||||
impl balances::Trait for Test {
|
||||
type Balance = u64;
|
||||
type AccountIndex = u64;
|
||||
type OnFreeBalanceZero = ();
|
||||
type EnsureAccountLiquid = ();
|
||||
type Event = ();
|
||||
}
|
||||
impl timestamp::Trait for Test {
|
||||
const TIMESTAMP_SET_POSITION: u32 = 0;
|
||||
type Moment = u64;
|
||||
}
|
||||
impl Trait for Test {
|
||||
type Proposal = Proposal;
|
||||
}
|
||||
|
||||
fn new_test_ext() -> runtime_io::TestExternalities<KeccakHasher> {
|
||||
let mut t = system::GenesisConfig::<Test>::default().build_storage().unwrap();
|
||||
t.extend(consensus::GenesisConfig::<Test>{
|
||||
code: vec![],
|
||||
authorities: vec![],
|
||||
}.build_storage().unwrap());
|
||||
t.extend(session::GenesisConfig::<Test>{
|
||||
session_length: 1, //??? or 2?
|
||||
validators: vec![10, 20],
|
||||
}.build_storage().unwrap());
|
||||
t.extend(staking::GenesisConfig::<Test>{
|
||||
sessions_per_era: 1,
|
||||
current_era: 0,
|
||||
t.extend(balances::GenesisConfig::<Test>{
|
||||
balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)],
|
||||
intentions: vec![],
|
||||
validator_count: 2,
|
||||
minimum_validator_count: 0,
|
||||
bonding_duration: 3,
|
||||
transaction_base_fee: 0,
|
||||
transaction_byte_fee: 0,
|
||||
existential_deposit: 0,
|
||||
transfer_fee: 0,
|
||||
creation_fee: 0,
|
||||
reclaim_rebate: 0,
|
||||
early_era_slash: 0,
|
||||
session_reward: 0,
|
||||
offline_slash_grace: 0,
|
||||
}.build_storage().unwrap());
|
||||
t.extend(GenesisConfig::<Test>{
|
||||
launch_period: 1,
|
||||
voting_period: 1,
|
||||
minimum_deposit: 1,
|
||||
}.build_storage().unwrap());
|
||||
t.extend(timestamp::GenesisConfig::<Test>::default().build_storage().unwrap());
|
||||
t.into()
|
||||
}
|
||||
|
||||
type System = system::Module<Test>;
|
||||
type Session = session::Module<Test>;
|
||||
type Staking = staking::Module<Test>;
|
||||
type Balances = balances::Module<Test>;
|
||||
type Democracy = Module<Test>;
|
||||
|
||||
#[test]
|
||||
@@ -465,22 +420,26 @@ mod tests {
|
||||
assert_eq!(Democracy::voting_period(), 1);
|
||||
assert_eq!(Democracy::minimum_deposit(), 1);
|
||||
assert_eq!(Democracy::referendum_count(), 0);
|
||||
assert_eq!(Staking::sessions_per_era(), 1);
|
||||
assert_eq!(Staking::total_stake(), 210);
|
||||
assert_eq!(Balances::free_balance(&42), 0);
|
||||
assert_eq!(Balances::total_stake(), 210);
|
||||
});
|
||||
}
|
||||
|
||||
fn propose_sessions_per_era(who: u64, value: u64, locked: u64) -> super::Result {
|
||||
Democracy::propose(&who, Box::new(Proposal::Staking(staking::PrivCall::set_sessions_per_era(value))), locked)
|
||||
fn set_balance_proposal(value: u64) -> Proposal {
|
||||
Proposal::Balances(balances::PrivCall::set_balance(balances::address::Address::Id(42), value, 0))
|
||||
}
|
||||
|
||||
fn propose_set_balance(who: u64, value: u64, locked: u64) -> super::Result {
|
||||
Democracy::propose(&who, Box::new(set_balance_proposal(value)), locked)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn locked_for_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
assert_ok!(propose_sessions_per_era(1, 2, 2));
|
||||
assert_ok!(propose_sessions_per_era(1, 4, 4));
|
||||
assert_ok!(propose_sessions_per_era(1, 3, 3));
|
||||
assert_ok!(propose_set_balance(1, 2, 2));
|
||||
assert_ok!(propose_set_balance(1, 4, 4));
|
||||
assert_ok!(propose_set_balance(1, 3, 3));
|
||||
assert_eq!(Democracy::locked_for(0), Some(2));
|
||||
assert_eq!(Democracy::locked_for(1), Some(4));
|
||||
assert_eq!(Democracy::locked_for(2), Some(3));
|
||||
@@ -491,7 +450,7 @@ mod tests {
|
||||
fn single_proposal_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
assert_ok!(propose_sessions_per_era(1, 2, 1));
|
||||
assert_ok!(propose_set_balance(1, 2, 1));
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
|
||||
System::set_block_number(2);
|
||||
@@ -504,9 +463,8 @@ mod tests {
|
||||
assert_eq!(Democracy::tally(r), (10, 0));
|
||||
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
Staking::on_session_change(0, true);
|
||||
|
||||
assert_eq!(Staking::era_length(), 2);
|
||||
assert_eq!(Balances::free_balance(&42), 2);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -514,14 +472,14 @@ mod tests {
|
||||
fn deposit_for_proposals_should_be_taken() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
assert_ok!(propose_sessions_per_era(1, 2, 5));
|
||||
assert_ok!(propose_set_balance(1, 2, 5));
|
||||
assert_ok!(Democracy::second(&2, 0));
|
||||
assert_ok!(Democracy::second(&5, 0));
|
||||
assert_ok!(Democracy::second(&5, 0));
|
||||
assert_ok!(Democracy::second(&5, 0));
|
||||
assert_eq!(Staking::free_balance(&1), 5);
|
||||
assert_eq!(Staking::free_balance(&2), 15);
|
||||
assert_eq!(Staking::free_balance(&5), 35);
|
||||
assert_eq!(Balances::free_balance(&1), 5);
|
||||
assert_eq!(Balances::free_balance(&2), 15);
|
||||
assert_eq!(Balances::free_balance(&5), 35);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -529,15 +487,15 @@ mod tests {
|
||||
fn deposit_for_proposals_should_be_returned() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
assert_ok!(propose_sessions_per_era(1, 2, 5));
|
||||
assert_ok!(propose_set_balance(1, 2, 5));
|
||||
assert_ok!(Democracy::second(&2, 0));
|
||||
assert_ok!(Democracy::second(&5, 0));
|
||||
assert_ok!(Democracy::second(&5, 0));
|
||||
assert_ok!(Democracy::second(&5, 0));
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
assert_eq!(Staking::free_balance(&1), 10);
|
||||
assert_eq!(Staking::free_balance(&2), 20);
|
||||
assert_eq!(Staking::free_balance(&5), 50);
|
||||
assert_eq!(Balances::free_balance(&1), 10);
|
||||
assert_eq!(Balances::free_balance(&2), 20);
|
||||
assert_eq!(Balances::free_balance(&5), 50);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -545,7 +503,7 @@ mod tests {
|
||||
fn proposal_with_deposit_below_minimum_should_not_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
assert_noop!(propose_sessions_per_era(1, 2, 0), "value too low");
|
||||
assert_noop!(propose_set_balance(1, 2, 0), "value too low");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -553,7 +511,7 @@ mod tests {
|
||||
fn poor_proposer_should_not_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
assert_noop!(propose_sessions_per_era(1, 2, 11), "proposer\'s balance too low");
|
||||
assert_noop!(propose_set_balance(1, 2, 11), "proposer\'s balance too low");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -561,53 +519,41 @@ mod tests {
|
||||
fn poor_seconder_should_not_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
assert_ok!(propose_sessions_per_era(2, 2, 11));
|
||||
assert_ok!(propose_set_balance(2, 2, 11));
|
||||
assert_noop!(Democracy::second(&1, 0), "seconder\'s balance too low");
|
||||
});
|
||||
}
|
||||
|
||||
fn propose_bonding_duration(who: u64, value: u64, locked: u64) -> super::Result {
|
||||
Democracy::propose(&who, Box::new(Proposal::Staking(staking::PrivCall::set_bonding_duration(value))), locked)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runners_up_should_come_after() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(0);
|
||||
assert_ok!(propose_bonding_duration(1, 2, 2));
|
||||
assert_ok!(propose_bonding_duration(1, 4, 4));
|
||||
assert_ok!(propose_bonding_duration(1, 3, 3));
|
||||
assert_ok!(propose_set_balance(1, 2, 2));
|
||||
assert_ok!(propose_set_balance(1, 4, 4));
|
||||
assert_ok!(propose_set_balance(1, 3, 3));
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
|
||||
System::set_block_number(1);
|
||||
assert_ok!(Democracy::vote(&1, 0, true));
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
Staking::on_session_change(0, true);
|
||||
assert_eq!(Staking::bonding_duration(), 4);
|
||||
assert_eq!(Balances::free_balance(&42), 4);
|
||||
|
||||
System::set_block_number(2);
|
||||
assert_ok!(Democracy::vote(&1, 1, true));
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
Staking::on_session_change(0, true);
|
||||
assert_eq!(Staking::bonding_duration(), 3);
|
||||
assert_eq!(Balances::free_balance(&42), 3);
|
||||
|
||||
System::set_block_number(3);
|
||||
assert_ok!(Democracy::vote(&1, 2, true));
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
Staking::on_session_change(0, true);
|
||||
assert_eq!(Staking::bonding_duration(), 2);
|
||||
});
|
||||
}
|
||||
|
||||
fn sessions_per_era_proposal(value: u64) -> Proposal {
|
||||
Proposal::Staking(staking::PrivCall::set_sessions_per_era(value))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_passing_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||
let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||
assert_ok!(Democracy::vote(&1, r, true));
|
||||
|
||||
assert_eq!(Democracy::voters_for(r), vec![1]);
|
||||
@@ -615,9 +561,8 @@ mod tests {
|
||||
assert_eq!(Democracy::tally(r), (10, 0));
|
||||
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
Staking::on_session_change(0, true);
|
||||
|
||||
assert_eq!(Staking::era_length(), 2);
|
||||
assert_eq!(Balances::free_balance(&42), 2);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -625,14 +570,13 @@ mod tests {
|
||||
fn cancel_referendum_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||
let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||
assert_ok!(Democracy::vote(&1, r, true));
|
||||
assert_ok!(Democracy::cancel_referendum(r));
|
||||
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
Staking::on_session_change(0, true);
|
||||
|
||||
assert_eq!(Staking::era_length(), 1);
|
||||
assert_eq!(Balances::free_balance(&42), 0);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -640,7 +584,7 @@ mod tests {
|
||||
fn simple_failing_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||
let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||
assert_ok!(Democracy::vote(&1, r, false));
|
||||
|
||||
assert_eq!(Democracy::voters_for(r), vec![1]);
|
||||
@@ -648,9 +592,8 @@ mod tests {
|
||||
assert_eq!(Democracy::tally(r), (0, 10));
|
||||
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
Staking::on_session_change(0, true);
|
||||
|
||||
assert_eq!(Staking::era_length(), 1);
|
||||
assert_eq!(Balances::free_balance(&42), 0);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -658,7 +601,7 @@ mod tests {
|
||||
fn controversial_voting_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||
let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||
assert_ok!(Democracy::vote(&1, r, true));
|
||||
assert_ok!(Democracy::vote(&2, r, false));
|
||||
assert_ok!(Democracy::vote(&3, r, false));
|
||||
@@ -669,9 +612,8 @@ mod tests {
|
||||
assert_eq!(Democracy::tally(r), (110, 100));
|
||||
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
Staking::on_session_change(0, true);
|
||||
|
||||
assert_eq!(Staking::era_length(), 2);
|
||||
assert_eq!(Balances::free_balance(&42), 2);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -679,27 +621,26 @@ mod tests {
|
||||
fn controversial_low_turnout_voting_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||
let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||
assert_ok!(Democracy::vote(&5, r, false));
|
||||
assert_ok!(Democracy::vote(&6, r, true));
|
||||
|
||||
assert_eq!(Democracy::tally(r), (60, 50));
|
||||
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
Staking::on_session_change(0, true);
|
||||
|
||||
assert_eq!(Staking::era_length(), 1);
|
||||
assert_eq!(Balances::free_balance(&42), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_low_turnout_voting_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
assert_eq!(Staking::era_length(), 1);
|
||||
assert_eq!(Staking::total_stake(), 210);
|
||||
assert_eq!(Balances::free_balance(&42), 0);
|
||||
assert_eq!(Balances::total_stake(), 210);
|
||||
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||
let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||
assert_ok!(Democracy::vote(&4, r, true));
|
||||
assert_ok!(Democracy::vote(&5, r, false));
|
||||
assert_ok!(Democracy::vote(&6, r, true));
|
||||
@@ -707,9 +648,8 @@ mod tests {
|
||||
assert_eq!(Democracy::tally(r), (100, 50));
|
||||
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
Staking::on_session_change(0, true);
|
||||
|
||||
assert_eq!(Staking::era_length(), 2);
|
||||
assert_eq!(Balances::free_balance(&42), 2);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate Demo is free software: you can redistribute it and/or modify
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate Demo is distributed in the hope that it will be useful,
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Voting thresholds.
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ substrate-runtime-system = { path = "../system", default_features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
substrate-primitives = { path = "../../primitives" }
|
||||
substrate-runtime-balances = { path = "../balances" }
|
||||
substrate-runtime-session = { path = "../session" }
|
||||
substrate-runtime-staking = { path = "../staking" }
|
||||
substrate-runtime-consensus = { path = "../consensus" }
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate Demo is free software: you can redistribute it and/or modify
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate Demo is distributed in the hope that it will be useful,
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Executive: Handles all of the top-level stuff; essentially just executing blocks/extrinsics.
|
||||
|
||||
@@ -53,6 +53,9 @@ extern crate substrate_runtime_consensus as consensus;
|
||||
#[cfg(test)]
|
||||
extern crate substrate_runtime_session as session;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate substrate_runtime_balances as balances;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate substrate_runtime_staking as staking;
|
||||
|
||||
@@ -224,7 +227,7 @@ impl<
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use staking::Call;
|
||||
use balances::Call;
|
||||
use runtime_io::with_externalities;
|
||||
use substrate_primitives::{H256, KeccakHasher};
|
||||
use primitives::BuildStorage;
|
||||
@@ -242,7 +245,7 @@ mod tests {
|
||||
|
||||
impl_outer_event!{
|
||||
pub enum MetaEvent for Test {
|
||||
session, staking
|
||||
balances, session, staking
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,10 +256,19 @@ mod tests {
|
||||
type PublicAux = u64;
|
||||
}
|
||||
impl consensus::Trait for Test {
|
||||
type PublicAux = <Self as HasPublicAux>::PublicAux;
|
||||
const NOTE_OFFLINE_POSITION: u32 = 1;
|
||||
type SessionKey = u64;
|
||||
type OnOfflineValidator = staking::Module<Test>;
|
||||
}
|
||||
impl balances::Trait for Test {
|
||||
type Balance = u64;
|
||||
type AccountIndex = u64;
|
||||
type OnFreeBalanceZero = staking::Module<Test>;
|
||||
type EnsureAccountLiquid = staking::Module<Test>;
|
||||
type Event = MetaEvent;
|
||||
}
|
||||
impl system::Trait for Test {
|
||||
type PublicAux = <Self as HasPublicAux>::PublicAux;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = substrate_primitives::H256;
|
||||
@@ -272,10 +284,6 @@ mod tests {
|
||||
type Event = MetaEvent;
|
||||
}
|
||||
impl staking::Trait for Test {
|
||||
const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1;
|
||||
type Balance = u64;
|
||||
type AccountIndex = u64;
|
||||
type OnFreeBalanceZero = ();
|
||||
type Event = MetaEvent;
|
||||
}
|
||||
impl timestamp::Trait for Test {
|
||||
@@ -284,25 +292,27 @@ mod tests {
|
||||
}
|
||||
|
||||
type TestXt = primitives::testing::TestXt<Call<Test>>;
|
||||
type Executive = super::Executive<Test, Block<TestXt>, NullLookup, staking::Module<Test>, (session::Module<Test>, staking::Module<Test>)>;
|
||||
type Executive = super::Executive<Test, Block<TestXt>, NullLookup, balances::Module<Test>, (session::Module<Test>, staking::Module<Test>)>;
|
||||
|
||||
#[test]
|
||||
fn staking_balance_transfer_dispatch_works() {
|
||||
let mut t = system::GenesisConfig::<Test>::default().build_storage().unwrap();
|
||||
t.extend(staking::GenesisConfig::<Test> {
|
||||
sessions_per_era: 0,
|
||||
current_era: 0,
|
||||
t.extend(balances::GenesisConfig::<Test> {
|
||||
balances: vec![(1, 111)],
|
||||
intentions: vec![],
|
||||
validator_count: 0,
|
||||
minimum_validator_count: 0,
|
||||
bonding_duration: 0,
|
||||
transaction_base_fee: 10,
|
||||
transaction_byte_fee: 0,
|
||||
existential_deposit: 0,
|
||||
transfer_fee: 0,
|
||||
creation_fee: 0,
|
||||
reclaim_rebate: 0,
|
||||
}.build_storage().unwrap());
|
||||
t.extend(staking::GenesisConfig::<Test> {
|
||||
sessions_per_era: 0,
|
||||
current_era: 0,
|
||||
intentions: vec![],
|
||||
validator_count: 0,
|
||||
minimum_validator_count: 0,
|
||||
bonding_duration: 0,
|
||||
early_era_slash: 0,
|
||||
session_reward: 0,
|
||||
offline_slash_grace: 0,
|
||||
@@ -312,13 +322,14 @@ mod tests {
|
||||
with_externalities(&mut t, || {
|
||||
Executive::initialise_block(&Header::new(1, H256::default(), H256::default(), [69u8; 32].into(), Digest::default()));
|
||||
Executive::apply_extrinsic(xt).unwrap();
|
||||
assert_eq!(<staking::Module<Test>>::voting_balance(&1), 32);
|
||||
assert_eq!(<staking::Module<Test>>::voting_balance(&2), 69);
|
||||
assert_eq!(<balances::Module<Test>>::total_balance(&1), 32);
|
||||
assert_eq!(<balances::Module<Test>>::total_balance(&2), 69);
|
||||
});
|
||||
}
|
||||
|
||||
fn new_test_ext() -> runtime_io::TestExternalities<KeccakHasher> {
|
||||
let mut t = system::GenesisConfig::<Test>::default().build_storage().unwrap();
|
||||
t.extend(balances::GenesisConfig::<Test>::default().build_storage().unwrap());
|
||||
t.extend(consensus::GenesisConfig::<Test>::default().build_storage().unwrap());
|
||||
t.extend(session::GenesisConfig::<Test>::default().build_storage().unwrap());
|
||||
t.extend(staking::GenesisConfig::<Test>::default().build_storage().unwrap());
|
||||
@@ -336,7 +347,7 @@ mod tests {
|
||||
// Blake
|
||||
// state_root: hex!("02532989c613369596025dfcfc821339fc9861987003924913a5a1382f87034a").into(),
|
||||
// Keccak
|
||||
state_root: hex!("e576ed2adacdc09b61844b5106bfaa18d2a4bfd7feb56d7af97c3421cdefca48").into(),
|
||||
state_root: hex!("ffe27b4c3a8b421fa10592be61fb28eca7ebbe04cbfa99cdda9f703f35522569").into(),
|
||||
extrinsics_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(),
|
||||
digest: Digest { logs: vec![], },
|
||||
},
|
||||
@@ -370,7 +381,7 @@ mod tests {
|
||||
header: Header {
|
||||
parent_hash: [69u8; 32].into(),
|
||||
number: 1,
|
||||
state_root: hex!("e576ed2adacdc09b61844b5106bfaa18d2a4bfd7feb56d7af97c3421cdefca48").into(),
|
||||
state_root: hex!("ffe27b4c3a8b421fa10592be61fb28eca7ebbe04cbfa99cdda9f703f35522569").into(),
|
||||
extrinsics_root: [0u8; 32].into(),
|
||||
digest: Digest { logs: vec![], },
|
||||
},
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate Demo is free software: you can redistribute it and/or modify
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate Demo is distributed in the hope that it will be useful,
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Generic implementations of Extrinsic/Header/Block.
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate Demo is free software: you can redistribute it and/or modify
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate Demo is distributed in the hope that it will be useful,
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! System manager: Handles all of the top-level stuff; executing block/transaction, setting code
|
||||
//! and depositing logs.
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate Demo is free software: you can redistribute it and/or modify
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate Demo is distributed in the hope that it will be useful,
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Testing utilities.
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate Demo is free software: you can redistribute it and/or modify
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate Demo is distributed in the hope that it will be useful,
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Primitives for the runtime modules.
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate Demo is free software: you can redistribute it and/or modify
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate Demo is distributed in the hope that it will be useful,
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Session manager: is told the validators and allows them to manage their session keys for the
|
||||
//! consensus module.
|
||||
@@ -302,10 +302,12 @@ mod tests {
|
||||
type PublicAux = u64;
|
||||
}
|
||||
impl consensus::Trait for Test {
|
||||
type PublicAux = <Self as HasPublicAux>::PublicAux;
|
||||
const NOTE_OFFLINE_POSITION: u32 = 1;
|
||||
type SessionKey = u64;
|
||||
type OnOfflineValidator = ();
|
||||
}
|
||||
impl system::Trait for Test {
|
||||
type PublicAux = <Self as HasPublicAux>::PublicAux;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
@@ -327,7 +329,6 @@ mod tests {
|
||||
|
||||
type System = system::Module<Test>;
|
||||
type Consensus = consensus::Module<Test>;
|
||||
type Timestamp = timestamp::Module<Test>;
|
||||
type Session = Module<Test>;
|
||||
|
||||
fn new_test_ext() -> runtime_io::TestExternalities<KeccakHasher> {
|
||||
|
||||
@@ -17,6 +17,7 @@ substrate-runtime-io = { path = "../../runtime-io", default_features = false }
|
||||
substrate-runtime-sandbox = { path = "../../runtime-sandbox", default_features = false }
|
||||
substrate-runtime-support = { path = "../../runtime-support", default_features = false }
|
||||
substrate-runtime-primitives = { path = "../primitives", default_features = false }
|
||||
substrate-runtime-balances = { path = "../balances", default_features = false }
|
||||
substrate-runtime-consensus = { path = "../consensus", default_features = false }
|
||||
substrate-runtime-system = { path = "../system", default_features = false }
|
||||
substrate-runtime-session = { path = "../session", default_features = false }
|
||||
@@ -40,6 +41,7 @@ std = [
|
||||
"substrate-runtime-sandbox/std",
|
||||
"substrate-runtime-support/std",
|
||||
"substrate-runtime-primitives/std",
|
||||
"substrate-runtime-balances/std",
|
||||
"substrate-runtime-session/std",
|
||||
"substrate-runtime-system/std",
|
||||
"substrate-runtime-timestamp/std"
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate Demo is free software: you can redistribute it and/or modify
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate Demo is distributed in the hope that it will be useful,
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Build a staking genesis block.
|
||||
|
||||
@@ -21,15 +21,12 @@
|
||||
use std::collections::HashMap;
|
||||
use rstd::prelude::*;
|
||||
use codec::Encode;
|
||||
use runtime_support::{StorageValue, StorageMap};
|
||||
use primitives::traits::{Zero, As};
|
||||
use runtime_support::StorageValue;
|
||||
use primitives::traits::As;
|
||||
use substrate_primitives::KeccakHasher;
|
||||
use {runtime_io, primitives};
|
||||
use super::{Trait, ENUM_SET_SIZE, EnumSet, NextEnumSet, Intentions, CurrentEra,
|
||||
BondingDuration, CreationFee, TransferFee, ReclaimRebate,
|
||||
ExistentialDeposit, TransactionByteFee, TransactionBaseFee, TotalStake,
|
||||
SessionsPerEra, ValidatorCount, FreeBalance, SessionReward, EarlyEraSlash,
|
||||
OfflineSlashGrace, MinimumValidatorCount};
|
||||
use super::{Trait, Intentions, CurrentEra, OfflineSlashGrace, MinimumValidatorCount,
|
||||
BondingDuration, SessionsPerEra, ValidatorCount, SessionReward, EarlyEraSlash};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -37,90 +34,24 @@ use super::{Trait, ENUM_SET_SIZE, EnumSet, NextEnumSet, Intentions, CurrentEra,
|
||||
pub struct GenesisConfig<T: Trait> {
|
||||
pub sessions_per_era: T::BlockNumber,
|
||||
pub current_era: T::BlockNumber,
|
||||
pub balances: Vec<(T::AccountId, T::Balance)>,
|
||||
pub intentions: Vec<T::AccountId>,
|
||||
pub validator_count: u32,
|
||||
pub minimum_validator_count: u32,
|
||||
pub bonding_duration: T::BlockNumber,
|
||||
pub transaction_base_fee: T::Balance,
|
||||
pub transaction_byte_fee: T::Balance,
|
||||
pub transfer_fee: T::Balance,
|
||||
pub creation_fee: T::Balance,
|
||||
pub reclaim_rebate: T::Balance,
|
||||
pub existential_deposit: T::Balance,
|
||||
pub session_reward: T::Balance,
|
||||
pub early_era_slash: T::Balance,
|
||||
pub offline_slash_grace: u32,
|
||||
}
|
||||
|
||||
impl<T: Trait> GenesisConfig<T> where T::AccountId: From<u64> {
|
||||
pub fn simple() -> Self {
|
||||
GenesisConfig {
|
||||
sessions_per_era: T::BlockNumber::sa(2),
|
||||
current_era: T::BlockNumber::sa(0),
|
||||
balances: vec![(T::AccountId::from(1), T::Balance::sa(111))],
|
||||
intentions: vec![T::AccountId::from(1), T::AccountId::from(2), T::AccountId::from(3)],
|
||||
validator_count: 3,
|
||||
minimum_validator_count: 1,
|
||||
bonding_duration: T::BlockNumber::sa(0),
|
||||
transaction_base_fee: T::Balance::sa(0),
|
||||
transaction_byte_fee: T::Balance::sa(0),
|
||||
transfer_fee: T::Balance::sa(0),
|
||||
creation_fee: T::Balance::sa(0),
|
||||
existential_deposit: T::Balance::sa(0),
|
||||
reclaim_rebate: T::Balance::sa(0),
|
||||
session_reward: T::Balance::sa(0),
|
||||
early_era_slash: T::Balance::sa(0),
|
||||
offline_slash_grace: 1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extended() -> Self {
|
||||
GenesisConfig {
|
||||
sessions_per_era: T::BlockNumber::sa(3),
|
||||
current_era: T::BlockNumber::sa(1),
|
||||
balances: vec![
|
||||
(T::AccountId::from(1), T::Balance::sa(10)),
|
||||
(T::AccountId::from(2), T::Balance::sa(20)),
|
||||
(T::AccountId::from(3), T::Balance::sa(30)),
|
||||
(T::AccountId::from(4), T::Balance::sa(40)),
|
||||
(T::AccountId::from(5), T::Balance::sa(50)),
|
||||
(T::AccountId::from(6), T::Balance::sa(60)),
|
||||
(T::AccountId::from(7), T::Balance::sa(1))
|
||||
],
|
||||
intentions: vec![T::AccountId::from(1), T::AccountId::from(2), T::AccountId::from(3)],
|
||||
validator_count: 3,
|
||||
minimum_validator_count: 1,
|
||||
bonding_duration: T::BlockNumber::sa(0),
|
||||
transaction_base_fee: T::Balance::sa(1),
|
||||
transaction_byte_fee: T::Balance::sa(0),
|
||||
transfer_fee: T::Balance::sa(0),
|
||||
creation_fee: T::Balance::sa(0),
|
||||
existential_deposit: T::Balance::sa(0),
|
||||
reclaim_rebate: T::Balance::sa(0),
|
||||
session_reward: T::Balance::sa(0),
|
||||
early_era_slash: T::Balance::sa(0),
|
||||
offline_slash_grace: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> Default for GenesisConfig<T> {
|
||||
fn default() -> Self {
|
||||
GenesisConfig {
|
||||
sessions_per_era: T::BlockNumber::sa(1000),
|
||||
current_era: T::BlockNumber::sa(0),
|
||||
balances: vec![],
|
||||
intentions: vec![],
|
||||
validator_count: 0,
|
||||
minimum_validator_count: 0,
|
||||
bonding_duration: T::BlockNumber::sa(1000),
|
||||
transaction_base_fee: T::Balance::sa(0),
|
||||
transaction_byte_fee: T::Balance::sa(0),
|
||||
transfer_fee: T::Balance::sa(0),
|
||||
creation_fee: T::Balance::sa(0),
|
||||
existential_deposit: T::Balance::sa(0),
|
||||
reclaim_rebate: T::Balance::sa(0),
|
||||
session_reward: T::Balance::sa(0),
|
||||
early_era_slash: T::Balance::sa(0),
|
||||
offline_slash_grace: 0,
|
||||
@@ -130,36 +61,17 @@ impl<T: Trait> Default for GenesisConfig<T> {
|
||||
|
||||
impl<T: Trait> primitives::BuildStorage for GenesisConfig<T> {
|
||||
fn build_storage(self) -> ::std::result::Result<HashMap<Vec<u8>, Vec<u8>>, String> {
|
||||
let total_stake: T::Balance = self.balances.iter().fold(Zero::zero(), |acc, &(_, n)| acc + n);
|
||||
|
||||
let mut r: runtime_io::TestExternalities<KeccakHasher> = map![
|
||||
Self::hash(<NextEnumSet<T>>::key()).to_vec() => T::AccountIndex::sa(self.balances.len() / ENUM_SET_SIZE).encode(),
|
||||
let r: runtime_io::TestExternalities<KeccakHasher> = map![
|
||||
Self::hash(<Intentions<T>>::key()).to_vec() => self.intentions.encode(),
|
||||
Self::hash(<SessionsPerEra<T>>::key()).to_vec() => self.sessions_per_era.encode(),
|
||||
Self::hash(<ValidatorCount<T>>::key()).to_vec() => self.validator_count.encode(),
|
||||
Self::hash(<MinimumValidatorCount<T>>::key()).to_vec() => self.minimum_validator_count.encode(),
|
||||
Self::hash(<BondingDuration<T>>::key()).to_vec() => self.bonding_duration.encode(),
|
||||
Self::hash(<TransactionBaseFee<T>>::key()).to_vec() => self.transaction_base_fee.encode(),
|
||||
Self::hash(<TransactionByteFee<T>>::key()).to_vec() => self.transaction_byte_fee.encode(),
|
||||
Self::hash(<TransferFee<T>>::key()).to_vec() => self.transfer_fee.encode(),
|
||||
Self::hash(<CreationFee<T>>::key()).to_vec() => self.creation_fee.encode(),
|
||||
Self::hash(<ExistentialDeposit<T>>::key()).to_vec() => self.existential_deposit.encode(),
|
||||
Self::hash(<ReclaimRebate<T>>::key()).to_vec() => self.reclaim_rebate.encode(),
|
||||
Self::hash(<CurrentEra<T>>::key()).to_vec() => self.current_era.encode(),
|
||||
Self::hash(<SessionReward<T>>::key()).to_vec() => self.session_reward.encode(),
|
||||
Self::hash(<EarlyEraSlash<T>>::key()).to_vec() => self.early_era_slash.encode(),
|
||||
Self::hash(<OfflineSlashGrace<T>>::key()).to_vec() => self.offline_slash_grace.encode(),
|
||||
Self::hash(<TotalStake<T>>::key()).to_vec() => total_stake.encode()
|
||||
Self::hash(<OfflineSlashGrace<T>>::key()).to_vec() => self.offline_slash_grace.encode()
|
||||
];
|
||||
|
||||
let ids: Vec<_> = self.balances.iter().map(|x| x.0.clone()).collect();
|
||||
for i in 0..(ids.len() + ENUM_SET_SIZE - 1) / ENUM_SET_SIZE {
|
||||
r.insert(Self::hash(&<EnumSet<T>>::key_for(T::AccountIndex::sa(i))).to_vec(),
|
||||
ids[i * ENUM_SET_SIZE..ids.len().min((i + 1) * ENUM_SET_SIZE)].to_owned().encode());
|
||||
}
|
||||
for (who, value) in self.balances.into_iter() {
|
||||
r.insert(Self::hash(&<FreeBalance<T>>::key_for(who)).to_vec(), value.encode());
|
||||
}
|
||||
Ok(r.into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate Demo is free software: you can redistribute it and/or modify
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate Demo is distributed in the hope that it will be useful,
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Staking manager: Handles balances and periodically determines the best set of validators.
|
||||
//! Staking manager: Periodically determines the best set of validators.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
@@ -41,26 +41,23 @@ extern crate substrate_codec as codec;
|
||||
extern crate substrate_primitives;
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
extern crate substrate_runtime_primitives as primitives;
|
||||
extern crate substrate_runtime_balances as balances;
|
||||
extern crate substrate_runtime_consensus as consensus;
|
||||
extern crate substrate_runtime_sandbox as sandbox;
|
||||
extern crate substrate_runtime_session as session;
|
||||
extern crate substrate_runtime_system as system;
|
||||
extern crate substrate_runtime_timestamp as timestamp;
|
||||
|
||||
#[cfg(test)] use std::fmt::Debug;
|
||||
use rstd::prelude::*;
|
||||
use rstd::{cmp, result};
|
||||
use codec::{Encode, Decode, Codec, Input, Output};
|
||||
use runtime_support::{StorageValue, StorageMap, Parameter};
|
||||
use runtime_support::{Parameter, StorageValue, StorageMap};
|
||||
use runtime_support::dispatch::Result;
|
||||
use session::OnSessionChange;
|
||||
use primitives::traits::{Zero, One, Bounded, RefInto, SimpleArithmetic, Executable, MakePayment,
|
||||
As, AuxLookup, Member, CheckedAdd, CheckedSub, MaybeEmpty};
|
||||
use address::Address as RawAddress;
|
||||
use primitives::traits::{Zero, One, Bounded, RefInto, Executable,
|
||||
As, AuxLookup};
|
||||
use balances::address::Address;
|
||||
|
||||
mod mock;
|
||||
|
||||
pub mod address;
|
||||
mod tests;
|
||||
mod genesis_config;
|
||||
|
||||
@@ -69,44 +66,17 @@ pub use genesis_config::GenesisConfig;
|
||||
|
||||
const DEFAULT_MINIMUM_VALIDATOR_COUNT: usize = 4;
|
||||
|
||||
/// Number of account IDs stored per enum set.
|
||||
const ENUM_SET_SIZE: usize = 64;
|
||||
|
||||
/// The byte to identify intention to reclaim an existing account index.
|
||||
const RECLAIM_INDEX_MAGIC: usize = 0x69;
|
||||
|
||||
pub type Address<T> = RawAddress<<T as system::Trait>::AccountId, <T as Trait>::AccountIndex>;
|
||||
|
||||
pub type Event<T> = RawEvent<
|
||||
<T as Trait>::Balance,
|
||||
<T as system::Trait>::AccountId,
|
||||
<T as Trait>::AccountIndex
|
||||
<T as balances::Trait>::Balance,
|
||||
<T as system::Trait>::AccountId
|
||||
>;
|
||||
|
||||
#[cfg(test)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum LockStatus<BlockNumber: Debug + PartialEq + Clone> {
|
||||
Liquid,
|
||||
LockedUntil(BlockNumber),
|
||||
Staked,
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[derive(PartialEq, Clone)]
|
||||
pub enum LockStatus<BlockNumber: PartialEq + Clone> {
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
pub enum LockStatus<BlockNumber: Parameter> {
|
||||
Liquid,
|
||||
LockedUntil(BlockNumber),
|
||||
Staked,
|
||||
}
|
||||
|
||||
/// The account was the given id was killed.
|
||||
pub trait OnFreeBalanceZero<AccountId> {
|
||||
/// The account was the given id was killed.
|
||||
fn on_free_balance_zero(who: &AccountId);
|
||||
}
|
||||
|
||||
impl<AccountId> OnFreeBalanceZero<AccountId> for () {
|
||||
fn on_free_balance_zero(_who: &AccountId) {}
|
||||
Bonded,
|
||||
}
|
||||
|
||||
/// Preference of what happens on a slash event.
|
||||
@@ -125,21 +95,7 @@ impl Default for SlashPreference {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Trait: system::Trait + session::Trait {
|
||||
/// The allowed extrinsic position for `missed_proposal` inherent.
|
||||
const NOTE_MISSED_PROPOSAL_POSITION: u32;
|
||||
|
||||
/// The balance of an account.
|
||||
type Balance: Parameter + SimpleArithmetic + Codec + Default + Copy + As<Self::AccountIndex> + As<usize> + As<u64>;
|
||||
/// Type used for storing an account's index; implies the maximum number of accounts the system
|
||||
/// can hold.
|
||||
type AccountIndex: Parameter + Member + Codec + SimpleArithmetic + As<u8> + As<u16> + As<u32> + As<u64> + As<usize> + Copy;
|
||||
/// A function which is invoked when the free-balance has fallen below the existential deposit and
|
||||
/// has been reduced to zero.
|
||||
///
|
||||
/// Gives a chance to clean up resources associated with the given account.
|
||||
type OnFreeBalanceZero: OnFreeBalanceZero<Self::AccountId>;
|
||||
|
||||
pub trait Trait: balances::Trait + session::Trait {
|
||||
/// The overarching event type.
|
||||
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
|
||||
}
|
||||
@@ -149,13 +105,11 @@ decl_module! {
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
fn transfer(aux, dest: RawAddress<T::AccountId, T::AccountIndex>, value: T::Balance) -> Result = 0;
|
||||
fn stake(aux) -> Result = 1;
|
||||
fn unstake(aux, intentions_index: u32) -> Result = 2;
|
||||
fn nominate(aux, target: RawAddress<T::AccountId, T::AccountIndex>) -> Result = 3;
|
||||
fn unnominate(aux, target_index: u32) -> Result = 4;
|
||||
fn register_slash_preference(aux, intentions_index: u32, p: SlashPreference) -> Result = 5;
|
||||
fn note_missed_proposal(aux, offline_val_indices: Vec<u32>) -> Result = 6;
|
||||
fn stake(aux) -> Result = 0;
|
||||
fn unstake(aux, intentions_index: u32) -> Result = 1;
|
||||
fn nominate(aux, target: Address<T::AccountId, T::AccountIndex>) -> Result = 2;
|
||||
fn unnominate(aux, target_index: u32) -> Result = 3;
|
||||
fn register_slash_preference(aux, intentions_index: u32, p: SlashPreference) -> Result = 4;
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
@@ -165,14 +119,13 @@ decl_module! {
|
||||
fn set_validator_count(new: u32) -> Result = 2;
|
||||
fn force_new_era(apply_rewards: bool) -> Result = 3;
|
||||
fn set_offline_slash_grace(new: u32) -> Result = 4;
|
||||
fn set_balance(who: RawAddress<T::AccountId, T::AccountIndex>, free: T::Balance, reserved: T::Balance) -> Result = 5;
|
||||
}
|
||||
}
|
||||
|
||||
/// An event in this module.
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[derive(Encode, Decode, PartialEq, Eq, Clone)]
|
||||
pub enum RawEvent<Balance, AccountId, AccountIndex> {
|
||||
pub enum RawEvent<Balance, AccountId> {
|
||||
/// All validators have been rewarded by the given balance.
|
||||
Reward(Balance),
|
||||
/// One validator (and their nominators) has been given a offline-warning (they're still within
|
||||
@@ -180,47 +133,28 @@ pub enum RawEvent<Balance, AccountId, AccountIndex> {
|
||||
OfflineWarning(AccountId, u32),
|
||||
/// One validator (and their nominators) has been slashed by the given amount.
|
||||
OfflineSlash(AccountId, Balance),
|
||||
/// A new account was created.
|
||||
NewAccount(AccountId, AccountIndex, NewAccountOutcome),
|
||||
/// An account was reaped.
|
||||
ReapedAccount(AccountId),
|
||||
}
|
||||
impl<B, A, I> From<RawEvent<B, A, I>> for () {
|
||||
fn from(_: RawEvent<B, A, I>) -> () { () }
|
||||
impl<B, A> From<RawEvent<B, A>> for () {
|
||||
fn from(_: RawEvent<B, A>) -> () { () }
|
||||
}
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as Staking {
|
||||
|
||||
// The length of the bonding duration in eras.
|
||||
pub BondingDuration get(bonding_duration): required T::BlockNumber;
|
||||
// The ideal number of staking participants.
|
||||
pub ValidatorCount get(validator_count): required u32;
|
||||
// Minimum number of staking participants before emergency conditions are imposed.
|
||||
pub MinimumValidatorCount: u32;
|
||||
// The length of a staking era in sessions.
|
||||
pub SessionsPerEra get(sessions_per_era): required T::BlockNumber;
|
||||
// The total amount of stake on the system.
|
||||
// TODO: this doesn't actually track total stake yet - it should do.
|
||||
pub TotalStake get(total_stake): required T::Balance;
|
||||
// The fee to be paid for making a transaction; the base.
|
||||
pub TransactionBaseFee get(transaction_base_fee): required T::Balance;
|
||||
// The fee to be paid for making a transaction; the per-byte portion.
|
||||
pub TransactionByteFee get(transaction_byte_fee): required T::Balance;
|
||||
// The minimum amount allowed to keep an account open.
|
||||
pub ExistentialDeposit get(existential_deposit): required T::Balance;
|
||||
// The amount credited to a destination's account whose index was reclaimed.
|
||||
pub ReclaimRebate get(reclaim_rebate): required T::Balance;
|
||||
// The fee required to make a transfer.
|
||||
pub TransferFee get(transfer_fee): required T::Balance;
|
||||
// The fee required to create an account. At least as big as ReclaimRebate.
|
||||
pub CreationFee get(creation_fee): required T::Balance;
|
||||
// Maximum reward, per validator, that is provided per acceptable session.
|
||||
pub SessionReward get(session_reward): required T::Balance;
|
||||
// Slash, per validator that is taken per abnormal era end.
|
||||
pub EarlyEraSlash get(early_era_slash): required T::Balance;
|
||||
// Number of instances of offline reports before slashing begins for validators.
|
||||
pub OfflineSlashGrace get(offline_slash_grace): default u32;
|
||||
// The length of the bonding duration in blocks.
|
||||
pub BondingDuration get(bonding_duration): required T::BlockNumber;
|
||||
|
||||
// The current era index.
|
||||
pub CurrentEra get(current_era): required T::BlockNumber;
|
||||
@@ -238,69 +172,20 @@ decl_storage! {
|
||||
pub NextSessionsPerEra get(next_sessions_per_era): T::BlockNumber;
|
||||
// The session index at which the era length last changed.
|
||||
pub LastEraLengthChange get(last_era_length_change): default T::BlockNumber;
|
||||
// The current era stake threshold
|
||||
|
||||
// The current era stake threshold - unused at present. Consider for removal.
|
||||
pub StakeThreshold get(stake_threshold): required T::Balance;
|
||||
|
||||
// The number of times a given validator has been reported offline. This gets decremented by one each era that passes.
|
||||
pub SlashCount get(slash_count): default map [ T::AccountId => u32 ];
|
||||
|
||||
// The next free enumeration set.
|
||||
pub NextEnumSet get(next_enum_set): required T::AccountIndex;
|
||||
// The enumeration sets.
|
||||
pub EnumSet get(enum_set): default map [ T::AccountIndex => Vec<T::AccountId> ];
|
||||
|
||||
// We are forcing a new era.
|
||||
pub ForcingNewEra get(forcing_new_era): ();
|
||||
|
||||
// The "free" balance of a given account.
|
||||
//
|
||||
// This is the only balance that matters in terms of most operations on tokens. It is
|
||||
// alone used to determine the balance when in the contract execution environment. When this
|
||||
// balance falls below the value of `ExistentialDeposit`, then the "current account" is
|
||||
// deleted: specifically, `Bondage` and `FreeBalance`. Furthermore, `OnFreeBalanceZero` callback
|
||||
// is invoked, giving a chance to external modules to cleanup data associated with
|
||||
// the deleted account.
|
||||
//
|
||||
// `system::AccountNonce` is also deleted if `ReservedBalance` is also zero (it also gets
|
||||
// collapsed to zero if it ever becomes less than `ExistentialDeposit`.
|
||||
pub FreeBalance get(free_balance): default map [ T::AccountId => T::Balance ];
|
||||
|
||||
// The amount of the balance of a given account that is exterally reserved; this can still get
|
||||
// slashed, but gets slashed last of all.
|
||||
//
|
||||
// This balance is a "reserve" balance that other subsystems use in order to set aside tokens
|
||||
// that are still "owned" by the account holder, but which are unspendable. This is different
|
||||
// and wholly unrelated to the `Bondage` system used for staking.
|
||||
//
|
||||
// When this balance falls below the value of `ExistentialDeposit`, then this "reserve account"
|
||||
// is deleted: specifically, `ReservedBalance`.
|
||||
//
|
||||
// `system::AccountNonce` is also deleted if `FreeBalance` is also zero (it also gets
|
||||
// collapsed to zero if it ever becomes less than `ExistentialDeposit`.
|
||||
pub ReservedBalance get(reserved_balance): default map [ T::AccountId => T::Balance ];
|
||||
|
||||
// The block at which the `who`'s funds become entirely liquid.
|
||||
pub Bondage get(bondage): default map [ T::AccountId => T::BlockNumber ];
|
||||
// The number of times a given validator has been reported offline. This gets decremented by one each era that passes.
|
||||
pub SlashCount get(slash_count): default map [ T::AccountId => u32 ];
|
||||
|
||||
// We are forcing a new era.
|
||||
pub ForcingNewEra get(forcing_new_era): ();
|
||||
}
|
||||
}
|
||||
|
||||
/// Whatever happened about the hint given when creating the new account.
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[derive(Encode, Decode, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum NewAccountOutcome {
|
||||
NoHint,
|
||||
GoodHint,
|
||||
BadHint,
|
||||
}
|
||||
|
||||
/// Outcome of a balance update.
|
||||
pub enum UpdateBalanceOutcome {
|
||||
/// Account balance was simply updated.
|
||||
Updated,
|
||||
/// The update has led to killing of the account.
|
||||
AccountKilled,
|
||||
}
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
|
||||
// PUBLIC IMMUTABLES
|
||||
@@ -314,112 +199,31 @@ impl<T: Trait> Module<T> {
|
||||
Self::sessions_per_era() * <session::Module<T>>::length()
|
||||
}
|
||||
|
||||
/// The combined balance of `who`.
|
||||
pub fn voting_balance(who: &T::AccountId) -> T::Balance {
|
||||
Self::free_balance(who) + Self::reserved_balance(who)
|
||||
}
|
||||
|
||||
/// Balance of a (potential) validator that includes all nominators.
|
||||
pub fn nomination_balance(who: &T::AccountId) -> T::Balance {
|
||||
Self::nominators_for(who).iter()
|
||||
.map(Self::voting_balance)
|
||||
.map(<balances::Module<T>>::total_balance)
|
||||
.fold(Zero::zero(), |acc, x| acc + x)
|
||||
}
|
||||
|
||||
/// The total balance that can be slashed from an account.
|
||||
pub fn slashable_balance(who: &T::AccountId) -> T::Balance {
|
||||
Self::nominators_for(who).iter()
|
||||
.map(Self::voting_balance)
|
||||
.fold(Self::voting_balance(who), |acc, x| acc + x)
|
||||
}
|
||||
|
||||
/// Some result as `slash(who, value)` (but without the side-effects) assuming there are no
|
||||
/// balance changes in the meantime and only the reserved balance is not taken into account.
|
||||
pub fn can_slash(who: &T::AccountId, value: T::Balance) -> bool {
|
||||
Self::free_balance(who) >= value
|
||||
}
|
||||
|
||||
/// Same result as `reserve(who, value)` (but without the side-effects) assuming there
|
||||
/// are no balance changes in the meantime.
|
||||
pub fn can_reserve(who: &T::AccountId, value: T::Balance) -> bool {
|
||||
if let LockStatus::Liquid = Self::unlock_block(who) {
|
||||
Self::free_balance(who) >= value
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Lookup an T::AccountIndex to get an Id, if there's one there.
|
||||
pub fn lookup_index(index: T::AccountIndex) -> Option<T::AccountId> {
|
||||
let enum_set_size = Self::enum_set_size();
|
||||
let set = Self::enum_set(index / enum_set_size);
|
||||
let i: usize = (index % enum_set_size).as_();
|
||||
set.get(i).map(|x| x.clone())
|
||||
}
|
||||
|
||||
/// `true` if the account `index` is ready for reclaim.
|
||||
pub fn can_reclaim(try_index: T::AccountIndex) -> bool {
|
||||
let enum_set_size = Self::enum_set_size();
|
||||
let try_set = Self::enum_set(try_index / enum_set_size);
|
||||
let i = (try_index % enum_set_size).as_();
|
||||
i < try_set.len() && Self::voting_balance(&try_set[i]).is_zero()
|
||||
.map(<balances::Module<T>>::total_balance)
|
||||
.fold(<balances::Module<T>>::total_balance(who), |acc, x| acc + x)
|
||||
}
|
||||
|
||||
/// The block at which the `who`'s funds become entirely liquid.
|
||||
pub fn unlock_block(who: &T::AccountId) -> LockStatus<T::BlockNumber> {
|
||||
match Self::bondage(who) {
|
||||
i if i == T::BlockNumber::max_value() => LockStatus::Staked,
|
||||
i if i == T::BlockNumber::max_value() => LockStatus::Bonded,
|
||||
i if i <= <system::Module<T>>::block_number() => LockStatus::Liquid,
|
||||
i => LockStatus::LockedUntil(i),
|
||||
}
|
||||
}
|
||||
|
||||
/// Lookup an address to get an Id, if there's one there.
|
||||
pub fn lookup_address(a: address::Address<T::AccountId, T::AccountIndex>) -> Option<T::AccountId> {
|
||||
match a {
|
||||
address::Address::Id(i) => Some(i),
|
||||
address::Address::Index(i) => Self::lookup_index(i),
|
||||
}
|
||||
}
|
||||
|
||||
// PUBLIC DISPATCH
|
||||
|
||||
/// Transfer some unlocked staking balance to another staker.
|
||||
pub fn transfer(aux: &T::PublicAux, dest: Address<T>, value: T::Balance) -> Result {
|
||||
let dest = Self::lookup(dest)?;
|
||||
|
||||
let transactor = aux.ref_into();
|
||||
let from_balance = Self::free_balance(transactor);
|
||||
let would_create = from_balance.is_zero();
|
||||
let fee = if would_create { Self::creation_fee() } else { Self::transfer_fee() };
|
||||
let liability = value + fee;
|
||||
|
||||
let new_from_balance = match from_balance.checked_sub(&liability) {
|
||||
Some(b) => b,
|
||||
None => return Err("balance too low to send value"),
|
||||
};
|
||||
if would_create && value < Self::existential_deposit() {
|
||||
return Err("value too low to create account");
|
||||
}
|
||||
if <Bondage<T>>::get(transactor) > <system::Module<T>>::block_number() {
|
||||
return Err("bondage too high to send value");
|
||||
}
|
||||
|
||||
let to_balance = Self::free_balance(&dest);
|
||||
let new_to_balance = match to_balance.checked_add(&value) {
|
||||
Some(b) => b,
|
||||
None => return Err("destination balance too high to receive value"),
|
||||
};
|
||||
|
||||
if transactor != &dest {
|
||||
Self::set_free_balance(transactor, new_from_balance);
|
||||
Self::decrease_total_stake_by(fee);
|
||||
Self::set_free_balance_creating(&dest, new_to_balance);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Declare the desire to stake for the transactor.
|
||||
///
|
||||
/// Effects will be felt at the beginning of the next era.
|
||||
@@ -446,8 +250,8 @@ impl<T: Trait> Module<T> {
|
||||
Self::apply_unstake(aux.ref_into(), intentions_index as usize)
|
||||
}
|
||||
|
||||
fn nominate(aux: &T::PublicAux, target: RawAddress<T::AccountId, T::AccountIndex>) -> Result {
|
||||
let target = Self::lookup(target)?;
|
||||
fn nominate(aux: &T::PublicAux, target: Address<T::AccountId, T::AccountIndex>) -> Result {
|
||||
let target = <balances::Module<T>>::lookup(target)?;
|
||||
let aux = aux.ref_into();
|
||||
|
||||
ensure!(Self::nominating(aux).is_none(), "Cannot nominate if already nominating.");
|
||||
@@ -490,7 +294,7 @@ impl<T: Trait> Module<T> {
|
||||
<Nominating<T>>::remove(source);
|
||||
|
||||
// update bondage
|
||||
<Bondage<T>>::insert(aux.ref_into(), Self::current_era() + Self::bonding_duration());
|
||||
<Bondage<T>>::insert(aux.ref_into(), <system::Module<T>>::block_number() + Self::bonding_duration());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -513,49 +317,6 @@ impl<T: Trait> Module<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Note the previous block's validator missed their opportunity to propose a block. This only comes in
|
||||
/// if 2/3+1 of the validators agree that no proposal was submitted. It's only relevant
|
||||
/// for the previous block.
|
||||
fn note_missed_proposal(aux: &T::PublicAux, offline_val_indices: Vec<u32>) -> Result {
|
||||
assert!(aux.is_empty());
|
||||
assert!(
|
||||
<system::Module<T>>::extrinsic_index() == Some(T::NOTE_MISSED_PROPOSAL_POSITION),
|
||||
"note_missed_proposal extrinsic must be at position {} in the block",
|
||||
T::NOTE_MISSED_PROPOSAL_POSITION
|
||||
);
|
||||
|
||||
for validator_index in offline_val_indices.into_iter() {
|
||||
let v = <session::Module<T>>::validators()[validator_index as usize].clone();
|
||||
let slash_count = Self::slash_count(&v);
|
||||
<SlashCount<T>>::insert(v.clone(), slash_count + 1);
|
||||
let grace = Self::offline_slash_grace();
|
||||
|
||||
let event = if slash_count >= grace {
|
||||
let instances = slash_count - grace;
|
||||
let slash = Self::early_era_slash() << instances;
|
||||
let next_slash = slash << 1u32;
|
||||
let _ = Self::slash_validator(&v, slash);
|
||||
if instances >= Self::slash_preference_of(&v).unstake_threshold
|
||||
|| Self::slashable_balance(&v) < next_slash
|
||||
{
|
||||
if let Some(pos) = Self::intentions().into_iter().position(|x| &x == &v) {
|
||||
Self::apply_unstake(&v, pos)
|
||||
.expect("pos derived correctly from Self::intentions(); \
|
||||
apply_unstake can only fail if pos wrong; \
|
||||
Self::intentions() doesn't change; qed");
|
||||
}
|
||||
let _ = Self::force_new_era(false);
|
||||
}
|
||||
RawEvent::OfflineSlash(v, slash)
|
||||
} else {
|
||||
RawEvent::OfflineWarning(v, slash_count)
|
||||
};
|
||||
Self::deposit_event(event);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// PRIV DISPATCH
|
||||
|
||||
/// Deposit one of this module's events.
|
||||
@@ -594,195 +355,8 @@ impl<T: Trait> Module<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the balances of a given account.
|
||||
fn set_balance(who: Address<T>, free: T::Balance, reserved: T::Balance) -> Result {
|
||||
let who = Self::lookup(who)?;
|
||||
Self::set_free_balance(&who, free);
|
||||
Self::set_reserved_balance(&who, reserved);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// PUBLIC MUTABLES (DANGEROUS)
|
||||
|
||||
/// Set the free balance of an account to some new value.
|
||||
///
|
||||
/// Will enforce ExistentialDeposit law, anulling the account as needed.
|
||||
/// In that case it will return `AccountKilled`.
|
||||
pub fn set_reserved_balance(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome {
|
||||
if balance < Self::existential_deposit() {
|
||||
<ReservedBalance<T>>::insert(who, balance);
|
||||
Self::on_reserved_too_low(who);
|
||||
UpdateBalanceOutcome::AccountKilled
|
||||
} else {
|
||||
<ReservedBalance<T>>::insert(who, balance);
|
||||
UpdateBalanceOutcome::Updated
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the free balance of an account to some new value. Will enforce ExistentialDeposit
|
||||
/// law anulling the account as needed.
|
||||
///
|
||||
/// Doesn't do any preparatory work for creating a new account, so should only be used when it
|
||||
/// is known that the account already exists.
|
||||
///
|
||||
/// Returns if the account was successfully updated or update has led to killing of the account.
|
||||
pub fn set_free_balance(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome {
|
||||
// Commented out for no - but consider it instructive.
|
||||
// assert!(!Self::voting_balance(who).is_zero());
|
||||
if balance < Self::existential_deposit() {
|
||||
<FreeBalance<T>>::insert(who, balance);
|
||||
Self::on_free_too_low(who);
|
||||
UpdateBalanceOutcome::AccountKilled
|
||||
} else {
|
||||
<FreeBalance<T>>::insert(who, balance);
|
||||
UpdateBalanceOutcome::Updated
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the free balance on an account to some new value.
|
||||
///
|
||||
/// Same as [`set_free_balance`], but will create a new account.
|
||||
///
|
||||
/// Returns if the account was successfully updated or update has led to killing of the account.
|
||||
///
|
||||
/// [`set_free_balance`]: #method.set_free_balance
|
||||
pub fn set_free_balance_creating(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome {
|
||||
let ed = <Module<T>>::existential_deposit();
|
||||
// If the balance is too low, then the account is reaped.
|
||||
// NOTE: There are two balances for every account: `reserved_balance` and
|
||||
// `free_balance`. This contract subsystem only cares about the latter: whenever
|
||||
// the term "balance" is used *here* it should be assumed to mean "free balance"
|
||||
// in the rest of the module.
|
||||
// Free balance can never be less than ED. If that happens, it gets reduced to zero
|
||||
// and the account information relevant to this subsystem is deleted (i.e. the
|
||||
// account is reaped).
|
||||
// NOTE: This is orthogonal to the `Bondage` value that an account has, a high
|
||||
// value of which makes even the `free_balance` unspendable.
|
||||
// TODO: enforce this for the other balance-altering functions.
|
||||
if balance < ed {
|
||||
Self::set_free_balance(who, balance);
|
||||
UpdateBalanceOutcome::AccountKilled
|
||||
} else {
|
||||
if !<FreeBalance<T>>::exists(who) {
|
||||
let outcome = Self::new_account(&who, balance);
|
||||
let credit = match outcome {
|
||||
NewAccountOutcome::GoodHint => balance + <Module<T>>::reclaim_rebate(),
|
||||
_ => balance,
|
||||
};
|
||||
Self::set_free_balance(who, credit);
|
||||
Self::increase_total_stake_by(credit - balance);
|
||||
} else {
|
||||
Self::set_free_balance(who, balance);
|
||||
}
|
||||
|
||||
UpdateBalanceOutcome::Updated
|
||||
}
|
||||
}
|
||||
|
||||
/// Deducts up to `value` from the combined balance of `who`, preferring to deduct from the
|
||||
/// free balance. This function cannot fail.
|
||||
///
|
||||
/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
|
||||
/// then `Some(remaining)` will be returned. Full completion is given by `None`.
|
||||
pub fn slash(who: &T::AccountId, value: T::Balance) -> Option<T::Balance> {
|
||||
let free_balance = Self::free_balance(who);
|
||||
let free_slash = cmp::min(free_balance, value);
|
||||
Self::set_free_balance(who, free_balance - free_slash);
|
||||
Self::decrease_total_stake_by(free_slash);
|
||||
if free_slash < value {
|
||||
Self::slash_reserved(who, value - free_slash)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds up to `value` to the free balance of `who`.
|
||||
///
|
||||
/// If `who` doesn't exist, nothing is done and an Err returned.
|
||||
pub fn reward(who: &T::AccountId, value: T::Balance) -> Result {
|
||||
if Self::voting_balance(who).is_zero() {
|
||||
return Err("beneficiary account must pre-exist");
|
||||
}
|
||||
Self::set_free_balance(who, Self::free_balance(who) + value);
|
||||
Self::increase_total_stake_by(value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Moves `value` from balance to reserved balance.
|
||||
///
|
||||
/// If the free balance is lower than `value`, then no funds will be moved and an `Err` will
|
||||
/// be returned to notify of this. This is different behaviour to `unreserve`.
|
||||
pub fn reserve(who: &T::AccountId, value: T::Balance) -> Result {
|
||||
let b = Self::free_balance(who);
|
||||
if b < value {
|
||||
return Err("not enough free funds")
|
||||
}
|
||||
if Self::unlock_block(who) != LockStatus::Liquid {
|
||||
return Err("free funds are still bonded")
|
||||
}
|
||||
Self::set_reserved_balance(who, Self::reserved_balance(who) + value);
|
||||
Self::set_free_balance(who, b - value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Moves up to `value` from reserved balance to balance. This function cannot fail.
|
||||
///
|
||||
/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
|
||||
/// then `Some(remaining)` will be returned. Full completion is given by `None`.
|
||||
/// NOTE: This is different to `reserve`.
|
||||
pub fn unreserve(who: &T::AccountId, value: T::Balance) -> Option<T::Balance> {
|
||||
let b = Self::reserved_balance(who);
|
||||
let actual = cmp::min(b, value);
|
||||
Self::set_free_balance(who, Self::free_balance(who) + actual);
|
||||
Self::set_reserved_balance(who, b - actual);
|
||||
if actual == value {
|
||||
None
|
||||
} else {
|
||||
Some(value - actual)
|
||||
}
|
||||
}
|
||||
|
||||
/// Deducts up to `value` from reserved balance of `who`. This function cannot fail.
|
||||
///
|
||||
/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
|
||||
/// then `Some(remaining)` will be returned. Full completion is given by `None`.
|
||||
pub fn slash_reserved(who: &T::AccountId, value: T::Balance) -> Option<T::Balance> {
|
||||
let b = Self::reserved_balance(who);
|
||||
let slash = cmp::min(b, value);
|
||||
Self::set_reserved_balance(who, b - slash);
|
||||
Self::decrease_total_stake_by(slash);
|
||||
if value == slash {
|
||||
None
|
||||
} else {
|
||||
Some(value - slash)
|
||||
}
|
||||
}
|
||||
|
||||
/// Moves up to `value` from reserved balance of account `slashed` to free balance of account
|
||||
/// `beneficiary`. `beneficiary` must exist for this to succeed. If it does not, `Err` will be
|
||||
/// returned.
|
||||
///
|
||||
/// As much funds up to `value` will be moved as possible. If this is less than `value`, then
|
||||
/// `Ok(Some(remaining))` will be returned. Full completion is given by `Ok(None)`.
|
||||
pub fn transfer_reserved(
|
||||
slashed: &T::AccountId,
|
||||
beneficiary: &T::AccountId,
|
||||
value: T::Balance
|
||||
) -> result::Result<Option<T::Balance>, &'static str> {
|
||||
if Self::voting_balance(beneficiary).is_zero() {
|
||||
return Err("beneficiary account must pre-exist");
|
||||
}
|
||||
let b = Self::reserved_balance(slashed);
|
||||
let slash = cmp::min(b, value);
|
||||
Self::set_free_balance(beneficiary, Self::free_balance(beneficiary) + slash);
|
||||
Self::set_reserved_balance(slashed, b - slash);
|
||||
if value == slash {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(value - slash))
|
||||
}
|
||||
}
|
||||
|
||||
/// Slash a given validator by a specific amount. Removes the slash from their balance by preference,
|
||||
/// and reduces the nominators' balance if needed.
|
||||
fn slash_validator(v: &T::AccountId, slash: T::Balance) {
|
||||
@@ -792,13 +366,13 @@ impl<T: Trait> Module<T> {
|
||||
return
|
||||
}
|
||||
|
||||
if let Some(rem) = Self::slash(v, slash) {
|
||||
if let Some(rem) = <balances::Module<T>>::slash(v, slash) {
|
||||
let noms = Self::current_nominators_for(v);
|
||||
let total = noms.iter().map(Self::voting_balance).fold(T::Balance::zero(), |acc, x| acc + x);
|
||||
let total = noms.iter().map(<balances::Module<T>>::total_balance).fold(T::Balance::zero(), |acc, x| acc + x);
|
||||
if !total.is_zero() {
|
||||
let safe_mul_rational = |b| b * rem / total;// TODO: avoid overflow
|
||||
for n in noms.iter() {
|
||||
let _ = Self::slash(n, safe_mul_rational(Self::voting_balance(n))); // best effort - not much that can be done on fail.
|
||||
let _ = <balances::Module<T>>::slash(n, safe_mul_rational(<balances::Module<T>>::total_balance(n))); // best effort - not much that can be done on fail.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -808,13 +382,13 @@ impl<T: Trait> Module<T> {
|
||||
/// balance, pro-rata.
|
||||
fn reward_validator(who: &T::AccountId, reward: T::Balance) {
|
||||
let noms = Self::current_nominators_for(who);
|
||||
let total = noms.iter().map(Self::voting_balance).fold(Self::voting_balance(who), |acc, x| acc + x);
|
||||
let total = noms.iter().map(<balances::Module<T>>::total_balance).fold(<balances::Module<T>>::total_balance(who), |acc, x| acc + x);
|
||||
if !total.is_zero() {
|
||||
let safe_mul_rational = |b| b * reward / total;// TODO: avoid overflow
|
||||
for n in noms.iter() {
|
||||
let _ = Self::reward(n, safe_mul_rational(Self::voting_balance(n)));
|
||||
let _ = <balances::Module<T>>::reward(n, safe_mul_rational(<balances::Module<T>>::total_balance(n)));
|
||||
}
|
||||
let _ = Self::reward(who, safe_mul_rational(Self::voting_balance(who)));
|
||||
let _ = <balances::Module<T>>::reward(who, safe_mul_rational(<balances::Module<T>>::total_balance(who)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -829,7 +403,7 @@ impl<T: Trait> Module<T> {
|
||||
<Intentions<T>>::put(intentions);
|
||||
<SlashPreferenceOf<T>>::remove(who);
|
||||
<SlashCount<T>>::remove(who);
|
||||
<Bondage<T>>::insert(who, Self::current_era() + Self::bonding_duration());
|
||||
<Bondage<T>>::insert(who, <system::Module<T>>::block_number() + Self::bonding_duration());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -916,125 +490,6 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
<session::Module<T>>::set_validators(vals);
|
||||
}
|
||||
|
||||
fn enum_set_size() -> T::AccountIndex {
|
||||
T::AccountIndex::sa(ENUM_SET_SIZE)
|
||||
}
|
||||
|
||||
/// Register a new account (with existential balance).
|
||||
fn new_account(who: &T::AccountId, balance: T::Balance) -> NewAccountOutcome {
|
||||
let enum_set_size = Self::enum_set_size();
|
||||
let next_set_index = Self::next_enum_set();
|
||||
let reclaim_index_magic = T::AccountIndex::sa(RECLAIM_INDEX_MAGIC);
|
||||
let reclaim_index_modulus = T::AccountIndex::sa(256usize);
|
||||
let quantization = T::AccountIndex::sa(256usize);
|
||||
|
||||
// A little easter-egg for reclaiming dead indexes..
|
||||
let ret = {
|
||||
// we quantise the number of accounts so it stays constant over a reasonable
|
||||
// period of time.
|
||||
let quantized_account_count: T::AccountIndex = (next_set_index * enum_set_size / quantization + One::one()) * quantization;
|
||||
// then modify the starting balance to be modulo this to allow it to potentially
|
||||
// identify an account index for reuse.
|
||||
let maybe_try_index = balance % <T::Balance as As<T::AccountIndex>>::sa(quantized_account_count * reclaim_index_modulus);
|
||||
let maybe_try_index = As::<T::AccountIndex>::as_(maybe_try_index);
|
||||
|
||||
// this identifier must end with magic byte 0x69 to trigger this check (a minor
|
||||
// optimisation to ensure we don't check most unintended account creations).
|
||||
if maybe_try_index % reclaim_index_modulus == reclaim_index_magic {
|
||||
// reuse is probably intended. first, remove magic byte.
|
||||
let try_index = maybe_try_index / reclaim_index_modulus;
|
||||
|
||||
// then check to see if this balance identifies a dead account index.
|
||||
let set_index = try_index / enum_set_size;
|
||||
let mut try_set = Self::enum_set(set_index);
|
||||
let item_index = (try_index % enum_set_size).as_();
|
||||
if item_index < try_set.len() {
|
||||
if Self::voting_balance(&try_set[item_index]).is_zero() {
|
||||
// yup - this index refers to a dead account. can be reused.
|
||||
try_set[item_index] = who.clone();
|
||||
<EnumSet<T>>::insert(set_index, try_set);
|
||||
|
||||
Self::deposit_event(RawEvent::NewAccount(who.clone(), try_index, NewAccountOutcome::GoodHint));
|
||||
|
||||
return NewAccountOutcome::GoodHint
|
||||
}
|
||||
}
|
||||
NewAccountOutcome::BadHint
|
||||
} else {
|
||||
NewAccountOutcome::NoHint
|
||||
}
|
||||
};
|
||||
|
||||
// insert normally as a back up
|
||||
let mut set_index = next_set_index;
|
||||
// defensive only: this loop should never iterate since we keep NextEnumSet up to date later.
|
||||
let mut set = loop {
|
||||
let set = Self::enum_set(set_index);
|
||||
if set.len() < ENUM_SET_SIZE {
|
||||
break set;
|
||||
}
|
||||
set_index += One::one();
|
||||
};
|
||||
|
||||
let index = T::AccountIndex::sa(set_index.as_() * ENUM_SET_SIZE + set.len());
|
||||
|
||||
// update set.
|
||||
set.push(who.clone());
|
||||
|
||||
// keep NextEnumSet up to date
|
||||
if set.len() == ENUM_SET_SIZE {
|
||||
<NextEnumSet<T>>::put(set_index + One::one());
|
||||
}
|
||||
|
||||
// write set.
|
||||
<EnumSet<T>>::insert(set_index, set);
|
||||
|
||||
Self::deposit_event(RawEvent::NewAccount(who.clone(), index, ret));
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn reap_account(who: &T::AccountId) {
|
||||
<system::AccountNonce<T>>::remove(who);
|
||||
Self::deposit_event(RawEvent::ReapedAccount(who.clone()));
|
||||
}
|
||||
|
||||
/// Kill an account's free portion.
|
||||
fn on_free_too_low(who: &T::AccountId) {
|
||||
Self::decrease_total_stake_by(Self::free_balance(who));
|
||||
<FreeBalance<T>>::remove(who);
|
||||
<Bondage<T>>::remove(who);
|
||||
|
||||
T::OnFreeBalanceZero::on_free_balance_zero(who);
|
||||
|
||||
if Self::reserved_balance(who).is_zero() {
|
||||
Self::reap_account(who);
|
||||
}
|
||||
}
|
||||
|
||||
/// Kill an account's reserved portion.
|
||||
fn on_reserved_too_low(who: &T::AccountId) {
|
||||
Self::decrease_total_stake_by(Self::reserved_balance(who));
|
||||
<ReservedBalance<T>>::remove(who);
|
||||
|
||||
if Self::free_balance(who).is_zero() {
|
||||
Self::reap_account(who);
|
||||
}
|
||||
}
|
||||
|
||||
/// Increase TotalStake by Value.
|
||||
pub fn increase_total_stake_by(value: T::Balance) {
|
||||
if let Some(v) = <Module<T>>::total_stake().checked_add(&value) {
|
||||
<TotalStake<T>>::put(v);
|
||||
}
|
||||
}
|
||||
/// Decrease TotalStake by Value.
|
||||
pub fn decrease_total_stake_by(value: T::Balance) {
|
||||
if let Some(v) = <Module<T>>::total_stake().checked_sub(&value) {
|
||||
<TotalStake<T>>::put(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> Executable for Module<T> {
|
||||
@@ -1048,26 +503,49 @@ impl<T: Trait> OnSessionChange<T::Moment> for Module<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> AuxLookup for Module<T> {
|
||||
type Source = address::Address<T::AccountId, T::AccountIndex>;
|
||||
type Target = T::AccountId;
|
||||
fn lookup(a: Self::Source) -> result::Result<Self::Target, &'static str> {
|
||||
match a {
|
||||
address::Address::Id(i) => Ok(i),
|
||||
address::Address::Index(i) => <Module<T>>::lookup_index(i).ok_or("invalid account index"),
|
||||
impl<T: Trait> balances::EnsureAccountLiquid<T::AccountId> for Module<T> {
|
||||
fn ensure_account_liquid(who: &T::AccountId) -> Result {
|
||||
if Self::bondage(who) <= <system::Module<T>>::block_number() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err("cannot transfer illiquid funds")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> MakePayment<T::AccountId> for Module<T> {
|
||||
fn make_payment(transactor: &T::AccountId, encoded_len: usize) -> Result {
|
||||
let b = Self::free_balance(transactor);
|
||||
let transaction_fee = Self::transaction_base_fee() + Self::transaction_byte_fee() * <T::Balance as As<u64>>::sa(encoded_len as u64);
|
||||
if b < transaction_fee + Self::existential_deposit() {
|
||||
return Err("not enough funds for transaction fee");
|
||||
}
|
||||
Self::set_free_balance(transactor, b - transaction_fee);
|
||||
Self::decrease_total_stake_by(transaction_fee);
|
||||
Ok(())
|
||||
impl<T: Trait> balances::OnFreeBalanceZero<T::AccountId> for Module<T> {
|
||||
fn on_free_balance_zero(who: &T::AccountId) {
|
||||
<Bondage<T>>::remove(who);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> consensus::OnOfflineValidator for Module<T> {
|
||||
fn on_offline_validator(validator_index: usize) {
|
||||
let v = <session::Module<T>>::validators()[validator_index].clone();
|
||||
let slash_count = Self::slash_count(&v);
|
||||
<SlashCount<T>>::insert(v.clone(), slash_count + 1);
|
||||
let grace = Self::offline_slash_grace();
|
||||
|
||||
let event = if slash_count >= grace {
|
||||
let instances = slash_count - grace;
|
||||
let slash = Self::early_era_slash() << instances;
|
||||
let next_slash = slash << 1u32;
|
||||
let _ = Self::slash_validator(&v, slash);
|
||||
if instances >= Self::slash_preference_of(&v).unstake_threshold
|
||||
|| Self::slashable_balance(&v) < next_slash
|
||||
{
|
||||
if let Some(pos) = Self::intentions().into_iter().position(|x| &x == &v) {
|
||||
Self::apply_unstake(&v, pos)
|
||||
.expect("pos derived correctly from Self::intentions(); \
|
||||
apply_unstake can only fail if pos wrong; \
|
||||
Self::intentions() doesn't change; qed");
|
||||
}
|
||||
let _ = Self::force_new_era(false);
|
||||
}
|
||||
RawEvent::OfflineSlash(v, slash)
|
||||
} else {
|
||||
RawEvent::OfflineWarning(v, slash_count)
|
||||
};
|
||||
Self::deposit_event(event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ use primitives::traits::{HasPublicAux, Identity};
|
||||
use primitives::testing::{Digest, Header};
|
||||
use substrate_primitives::{H256, KeccakHasher};
|
||||
use runtime_io;
|
||||
use {GenesisConfig, Module, Trait, consensus, session, system, timestamp};
|
||||
use {GenesisConfig, Module, Trait, consensus, session, system, timestamp, balances};
|
||||
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
@@ -32,10 +32,12 @@ impl HasPublicAux for Test {
|
||||
type PublicAux = u64;
|
||||
}
|
||||
impl consensus::Trait for Test {
|
||||
type PublicAux = <Self as HasPublicAux>::PublicAux;
|
||||
const NOTE_OFFLINE_POSITION: u32 = 1;
|
||||
type SessionKey = u64;
|
||||
type OnOfflineValidator = ();
|
||||
}
|
||||
impl system::Trait for Test {
|
||||
type PublicAux = <Self as HasPublicAux>::PublicAux;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
@@ -45,6 +47,13 @@ impl system::Trait for Test {
|
||||
type Header = Header;
|
||||
type Event = ();
|
||||
}
|
||||
impl balances::Trait for Test {
|
||||
type Balance = u64;
|
||||
type AccountIndex = u64;
|
||||
type OnFreeBalanceZero = Staking;
|
||||
type EnsureAccountLiquid = Staking;
|
||||
type Event = ();
|
||||
}
|
||||
impl session::Trait for Test {
|
||||
type ConvertAccountIdToSessionKey = Identity;
|
||||
type OnSessionChange = Staking;
|
||||
@@ -55,10 +64,6 @@ impl timestamp::Trait for Test {
|
||||
type Moment = u64;
|
||||
}
|
||||
impl Trait for Test {
|
||||
const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1;
|
||||
type Balance = u64;
|
||||
type AccountIndex = u64;
|
||||
type OnFreeBalanceZero = ();
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
@@ -77,9 +82,7 @@ pub fn new_test_ext(ext_deposit: u64, session_length: u64, sessions_per_era: u64
|
||||
session_length,
|
||||
validators: vec![10, 20],
|
||||
}.build_storage().unwrap());
|
||||
t.extend(GenesisConfig::<Test>{
|
||||
sessions_per_era,
|
||||
current_era,
|
||||
t.extend(balances::GenesisConfig::<Test>{
|
||||
balances: if monied {
|
||||
if reward > 0 {
|
||||
vec![(1, 10 * balance_factor), (2, 20 * balance_factor), (3, 30 * balance_factor), (4, 40 * balance_factor), (10, balance_factor), (20, balance_factor)]
|
||||
@@ -89,16 +92,20 @@ pub fn new_test_ext(ext_deposit: u64, session_length: u64, sessions_per_era: u64
|
||||
} else {
|
||||
vec![(10, balance_factor), (20, balance_factor)]
|
||||
},
|
||||
intentions: vec![10, 20],
|
||||
validator_count: 2,
|
||||
minimum_validator_count: 0,
|
||||
bonding_duration: 3,
|
||||
transaction_base_fee: 0,
|
||||
transaction_byte_fee: 0,
|
||||
existential_deposit: ext_deposit,
|
||||
transfer_fee: 0,
|
||||
creation_fee: 0,
|
||||
reclaim_rebate: 0,
|
||||
}.build_storage().unwrap());
|
||||
t.extend(GenesisConfig::<Test>{
|
||||
sessions_per_era,
|
||||
current_era,
|
||||
intentions: vec![10, 20],
|
||||
validator_count: 2,
|
||||
minimum_validator_count: 0,
|
||||
bonding_duration: sessions_per_era * session_length * 3,
|
||||
session_reward: reward,
|
||||
early_era_slash: if monied { 20 } else { 0 },
|
||||
offline_slash_grace: 0,
|
||||
@@ -110,6 +117,7 @@ pub fn new_test_ext(ext_deposit: u64, session_length: u64, sessions_per_era: u64
|
||||
}
|
||||
|
||||
pub type System = system::Module<Test>;
|
||||
pub type Balances = balances::Module<Test>;
|
||||
pub type Session = session::Module<Test>;
|
||||
pub type Timestamp = timestamp::Module<Test>;
|
||||
pub type Staking = Module<Test>;
|
||||
|
||||
@@ -1,175 +1,171 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate Demo is free software: you can redistribute it and/or modify
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate Demo is distributed in the hope that it will be useful,
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Tests for the module.
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use super::*;
|
||||
use consensus::OnOfflineValidator;
|
||||
use runtime_io::with_externalities;
|
||||
use mock::{Session, Staking, System, Timestamp, Test, new_test_ext};
|
||||
use mock::{Balances, Session, Staking, System, Timestamp, Test, new_test_ext};
|
||||
|
||||
#[test]
|
||||
fn note_null_missed_proposal_should_work() {
|
||||
fn note_null_offline_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
|
||||
assert_eq!(Staking::offline_slash_grace(), 0);
|
||||
assert_eq!(Staking::slash_count(&10), 0);
|
||||
assert_eq!(Staking::free_balance(&10), 1);
|
||||
assert_eq!(Balances::free_balance(&10), 1);
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![]));
|
||||
assert_eq!(Staking::slash_count(&10), 0);
|
||||
assert_eq!(Staking::free_balance(&10), 1);
|
||||
assert_eq!(Balances::free_balance(&10), 1);
|
||||
assert!(Staking::forcing_new_era().is_none());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn note_missed_proposal_should_work() {
|
||||
fn note_offline_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
|
||||
Staking::set_free_balance(&10, 70);
|
||||
Balances::set_free_balance(&10, 70);
|
||||
assert_eq!(Staking::offline_slash_grace(), 0);
|
||||
assert_eq!(Staking::slash_count(&10), 0);
|
||||
assert_eq!(Staking::free_balance(&10), 70);
|
||||
assert_eq!(Balances::free_balance(&10), 70);
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0]));
|
||||
Staking::on_offline_validator(0);
|
||||
assert_eq!(Staking::slash_count(&10), 1);
|
||||
assert_eq!(Staking::free_balance(&10), 50);
|
||||
assert_eq!(Balances::free_balance(&10), 50);
|
||||
assert!(Staking::forcing_new_era().is_none());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn note_missed_proposal_exponent_should_work() {
|
||||
fn note_offline_exponent_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
|
||||
Staking::set_free_balance(&10, 150);
|
||||
Balances::set_free_balance(&10, 150);
|
||||
assert_eq!(Staking::offline_slash_grace(), 0);
|
||||
assert_eq!(Staking::slash_count(&10), 0);
|
||||
assert_eq!(Staking::free_balance(&10), 150);
|
||||
assert_eq!(Balances::free_balance(&10), 150);
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0]));
|
||||
Staking::on_offline_validator(0);
|
||||
assert_eq!(Staking::slash_count(&10), 1);
|
||||
assert_eq!(Staking::free_balance(&10), 130);
|
||||
assert_eq!(Balances::free_balance(&10), 130);
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0]));
|
||||
Staking::on_offline_validator(0);
|
||||
assert_eq!(Staking::slash_count(&10), 2);
|
||||
assert_eq!(Staking::free_balance(&10), 90);
|
||||
assert_eq!(Balances::free_balance(&10), 90);
|
||||
assert!(Staking::forcing_new_era().is_none());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn note_missed_proposal_grace_should_work() {
|
||||
fn note_offline_grace_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
|
||||
Staking::set_free_balance(&10, 70);
|
||||
Staking::set_free_balance(&20, 70);
|
||||
Balances::set_free_balance(&10, 70);
|
||||
Balances::set_free_balance(&20, 70);
|
||||
assert_ok!(Staking::set_offline_slash_grace(1));
|
||||
assert_eq!(Staking::offline_slash_grace(), 1);
|
||||
|
||||
assert_eq!(Staking::slash_count(&10), 0);
|
||||
assert_eq!(Staking::free_balance(&10), 70);
|
||||
assert_eq!(Balances::free_balance(&10), 70);
|
||||
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0]));
|
||||
Staking::on_offline_validator(0);
|
||||
assert_eq!(Staking::slash_count(&10), 1);
|
||||
assert_eq!(Staking::free_balance(&10), 70);
|
||||
assert_eq!(Balances::free_balance(&10), 70);
|
||||
assert_eq!(Staking::slash_count(&20), 0);
|
||||
assert_eq!(Staking::free_balance(&20), 70);
|
||||
assert_eq!(Balances::free_balance(&20), 70);
|
||||
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0, 1]));
|
||||
Staking::on_offline_validator(0);
|
||||
Staking::on_offline_validator(1);
|
||||
assert_eq!(Staking::slash_count(&10), 2);
|
||||
assert_eq!(Staking::free_balance(&10), 50);
|
||||
assert_eq!(Balances::free_balance(&10), 50);
|
||||
assert_eq!(Staking::slash_count(&20), 1);
|
||||
assert_eq!(Staking::free_balance(&20), 70);
|
||||
assert_eq!(Balances::free_balance(&20), 70);
|
||||
assert!(Staking::forcing_new_era().is_none());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn note_missed_proposal_force_unstake_session_change_should_work() {
|
||||
fn note_offline_force_unstake_session_change_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
|
||||
Staking::set_free_balance(&10, 70);
|
||||
Staking::set_free_balance(&20, 70);
|
||||
Balances::set_free_balance(&10, 70);
|
||||
Balances::set_free_balance(&20, 70);
|
||||
assert_ok!(Staking::stake(&1));
|
||||
|
||||
assert_eq!(Staking::slash_count(&10), 0);
|
||||
assert_eq!(Staking::free_balance(&10), 70);
|
||||
assert_eq!(Balances::free_balance(&10), 70);
|
||||
assert_eq!(Staking::intentions(), vec![10, 20, 1]);
|
||||
assert_eq!(Session::validators(), vec![10, 20]);
|
||||
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0]));
|
||||
assert_eq!(Staking::free_balance(&10), 50);
|
||||
Staking::on_offline_validator(0);
|
||||
assert_eq!(Balances::free_balance(&10), 50);
|
||||
assert_eq!(Staking::slash_count(&10), 1);
|
||||
assert_eq!(Staking::intentions(), vec![10, 20, 1]);
|
||||
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0]));
|
||||
Staking::on_offline_validator(0);
|
||||
assert_eq!(Staking::intentions(), vec![1, 20]);
|
||||
assert_eq!(Staking::free_balance(&10), 10);
|
||||
assert_eq!(Balances::free_balance(&10), 10);
|
||||
assert!(Staking::forcing_new_era().is_some());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn note_missed_proposal_auto_unstake_session_change_should_work() {
|
||||
fn note_offline_auto_unstake_session_change_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
|
||||
Staking::set_free_balance(&10, 7000);
|
||||
Staking::set_free_balance(&20, 7000);
|
||||
Balances::set_free_balance(&10, 7000);
|
||||
Balances::set_free_balance(&20, 7000);
|
||||
assert_ok!(Staking::register_slash_preference(&10, 0, SlashPreference { unstake_threshold: 1 }));
|
||||
|
||||
assert_eq!(Staking::intentions(), vec![10, 20]);
|
||||
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0, 1]));
|
||||
assert_eq!(Staking::free_balance(&10), 6980);
|
||||
assert_eq!(Staking::free_balance(&20), 6980);
|
||||
Staking::on_offline_validator(0);
|
||||
Staking::on_offline_validator(1);
|
||||
assert_eq!(Balances::free_balance(&10), 6980);
|
||||
assert_eq!(Balances::free_balance(&20), 6980);
|
||||
assert_eq!(Staking::intentions(), vec![10, 20]);
|
||||
assert!(Staking::forcing_new_era().is_none());
|
||||
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0, 1]));
|
||||
assert_eq!(Staking::free_balance(&10), 6940);
|
||||
assert_eq!(Staking::free_balance(&20), 6940);
|
||||
Staking::on_offline_validator(0);
|
||||
Staking::on_offline_validator(1);
|
||||
assert_eq!(Balances::free_balance(&10), 6940);
|
||||
assert_eq!(Balances::free_balance(&20), 6940);
|
||||
assert_eq!(Staking::intentions(), vec![20]);
|
||||
assert!(Staking::forcing_new_era().is_some());
|
||||
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![1]));
|
||||
assert_eq!(Staking::free_balance(&10), 6940);
|
||||
assert_eq!(Staking::free_balance(&20), 6860);
|
||||
Staking::on_offline_validator(1);
|
||||
assert_eq!(Balances::free_balance(&10), 6940);
|
||||
assert_eq!(Balances::free_balance(&20), 6860);
|
||||
assert_eq!(Staking::intentions(), vec![20]);
|
||||
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![1]));
|
||||
assert_eq!(Staking::free_balance(&10), 6940);
|
||||
assert_eq!(Staking::free_balance(&20), 6700);
|
||||
Staking::on_offline_validator(1);
|
||||
assert_eq!(Balances::free_balance(&10), 6940);
|
||||
assert_eq!(Balances::free_balance(&20), 6700);
|
||||
assert_eq!(Staking::intentions(), vec![0u64; 0]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reward_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
|
||||
assert_eq!(Staking::voting_balance(&10), 1);
|
||||
assert_ok!(Staking::reward(&10, 10));
|
||||
assert_eq!(Staking::voting_balance(&10), 11);
|
||||
assert_eq!(<TotalStake<Test>>::get(), 112);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
fn rewards_should_work() {
|
||||
@@ -179,26 +175,26 @@ fn rewards_should_work() {
|
||||
assert_eq!(Staking::last_era_length_change(), 0);
|
||||
assert_eq!(Staking::current_era(), 0);
|
||||
assert_eq!(Session::current_index(), 0);
|
||||
assert_eq!(Staking::voting_balance(&10), 1);
|
||||
assert_eq!(Balances::total_balance(&10), 1);
|
||||
|
||||
System::set_block_number(3);
|
||||
Timestamp::set_timestamp(15); // on time.
|
||||
Session::check_rotate_session();
|
||||
assert_eq!(Staking::current_era(), 0);
|
||||
assert_eq!(Session::current_index(), 1);
|
||||
assert_eq!(Staking::voting_balance(&10), 11);
|
||||
assert_eq!(Balances::total_balance(&10), 11);
|
||||
System::set_block_number(6);
|
||||
Timestamp::set_timestamp(31); // a little late
|
||||
Session::check_rotate_session();
|
||||
assert_eq!(Staking::current_era(), 0);
|
||||
assert_eq!(Session::current_index(), 2);
|
||||
assert_eq!(Staking::voting_balance(&10), 20); // less reward
|
||||
assert_eq!(Balances::total_balance(&10), 20); // less reward
|
||||
System::set_block_number(9);
|
||||
Timestamp::set_timestamp(50); // very late
|
||||
Session::check_rotate_session();
|
||||
assert_eq!(Staking::current_era(), 1);
|
||||
assert_eq!(Session::current_index(), 3);
|
||||
assert_eq!(Staking::voting_balance(&10), 27); // much less reward
|
||||
assert_eq!(Balances::total_balance(&10), 27); // much less reward
|
||||
});
|
||||
}
|
||||
|
||||
@@ -210,112 +206,40 @@ fn slashing_should_work() {
|
||||
assert_eq!(Staking::last_era_length_change(), 0);
|
||||
assert_eq!(Staking::current_era(), 0);
|
||||
assert_eq!(Session::current_index(), 0);
|
||||
assert_eq!(Staking::voting_balance(&10), 1);
|
||||
assert_eq!(Balances::total_balance(&10), 1);
|
||||
|
||||
System::set_block_number(3);
|
||||
Session::check_rotate_session();
|
||||
assert_eq!(Staking::current_era(), 0);
|
||||
assert_eq!(Session::current_index(), 1);
|
||||
assert_eq!(Staking::voting_balance(&10), 11);
|
||||
assert_eq!(Balances::total_balance(&10), 11);
|
||||
|
||||
System::set_block_number(6);
|
||||
Session::check_rotate_session();
|
||||
assert_eq!(Staking::current_era(), 0);
|
||||
assert_eq!(Session::current_index(), 2);
|
||||
assert_eq!(Staking::voting_balance(&10), 21);
|
||||
assert_eq!(Balances::total_balance(&10), 21);
|
||||
|
||||
System::set_block_number(7);
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0, 1]));
|
||||
assert_eq!(Staking::voting_balance(&10), 1);
|
||||
Staking::on_offline_validator(0);
|
||||
Staking::on_offline_validator(1);
|
||||
assert_eq!(Balances::total_balance(&10), 1);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indexing_lookup_should_work() {
|
||||
with_externalities(&mut new_test_ext(10, 1, 2, 0, true, 0), || {
|
||||
assert_eq!(Staking::lookup_index(0), Some(1));
|
||||
assert_eq!(Staking::lookup_index(1), Some(2));
|
||||
assert_eq!(Staking::lookup_index(2), Some(3));
|
||||
assert_eq!(Staking::lookup_index(3), Some(4));
|
||||
assert_eq!(Staking::lookup_index(4), None);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_indexing_on_new_accounts_should_work() {
|
||||
with_externalities(&mut new_test_ext(10, 1, 2, 0, true, 0), || {
|
||||
assert_eq!(Staking::lookup_index(4), None);
|
||||
assert_ok!(Staking::transfer(&1, 5.into(), 10));
|
||||
assert_eq!(Staking::lookup_index(4), Some(5));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dust_account_removal_should_work() {
|
||||
with_externalities(&mut new_test_ext(256 * 10, 1, 2, 0, true, 0), || {
|
||||
System::inc_account_nonce(&2);
|
||||
assert_eq!(System::account_nonce(&2), 1);
|
||||
assert_eq!(Staking::voting_balance(&2), 256 * 20);
|
||||
|
||||
assert_ok!(Staking::transfer(&2, 5.into(), 256 * 10 + 1)); // index 1 (account 2) becomes zombie
|
||||
assert_eq!(Staking::voting_balance(&2), 0);
|
||||
assert_eq!(Staking::voting_balance(&5), 256 * 10 + 1);
|
||||
assert_eq!(System::account_nonce(&2), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reclaim_indexing_on_new_accounts_should_work() {
|
||||
with_externalities(&mut new_test_ext(256 * 1, 1, 2, 0, true, 0), || {
|
||||
assert_eq!(Staking::lookup_index(1), Some(2));
|
||||
assert_eq!(Staking::lookup_index(4), None);
|
||||
assert_eq!(Staking::voting_balance(&2), 256 * 20);
|
||||
|
||||
assert_ok!(Staking::transfer(&2, 5.into(), 256 * 20)); // account 2 becomes zombie freeing index 1 for reclaim)
|
||||
assert_eq!(Staking::voting_balance(&2), 0);
|
||||
|
||||
assert_ok!(Staking::transfer(&5, 6.into(), 256 * 1 + 0x69)); // account 6 takes index 1.
|
||||
assert_eq!(Staking::voting_balance(&6), 256 * 1 + 0x69);
|
||||
assert_eq!(Staking::lookup_index(1), Some(6));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reserved_balance_should_prevent_reclaim_count() {
|
||||
with_externalities(&mut new_test_ext(256 * 1, 1, 2, 0, true, 0), || {
|
||||
System::inc_account_nonce(&2);
|
||||
assert_eq!(Staking::lookup_index(1), Some(2));
|
||||
assert_eq!(Staking::lookup_index(4), None);
|
||||
assert_eq!(Staking::voting_balance(&2), 256 * 20);
|
||||
|
||||
assert_ok!(Staking::reserve(&2, 256 * 19 + 1)); // account 2 becomes mostly reserved
|
||||
assert_eq!(Staking::free_balance(&2), 0); // "free" account deleted."
|
||||
assert_eq!(Staking::voting_balance(&2), 256 * 19 + 1); // reserve still exists.
|
||||
assert_eq!(System::account_nonce(&2), 1);
|
||||
|
||||
assert_ok!(Staking::transfer(&4, 5.into(), 256 * 1 + 0x69)); // account 4 tries to take index 1 for account 5.
|
||||
assert_eq!(Staking::voting_balance(&5), 256 * 1 + 0x69);
|
||||
assert_eq!(Staking::lookup_index(1), Some(2)); // but fails.
|
||||
assert_eq!(System::account_nonce(&2), 1);
|
||||
|
||||
assert_eq!(Staking::slash(&2, 256 * 18 + 2), None); // account 2 gets slashed
|
||||
assert_eq!(Staking::voting_balance(&2), 0); // "free" account deleted."
|
||||
assert_eq!(System::account_nonce(&2), 0);
|
||||
|
||||
assert_ok!(Staking::transfer(&4, 6.into(), 256 * 1 + 0x69)); // account 4 tries to take index 1 again for account 6.
|
||||
assert_eq!(Staking::voting_balance(&6), 256 * 1 + 0x69);
|
||||
assert_eq!(Staking::lookup_index(1), Some(6)); // and succeeds.
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn staking_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, 1, 2, 0, true, 0), || {
|
||||
|
||||
assert_eq!(Staking::era_length(), 2);
|
||||
assert_eq!(Staking::validator_count(), 2);
|
||||
assert_eq!(Staking::bonding_duration(), 3);
|
||||
assert_eq!(Session::validators(), vec![10, 20]);
|
||||
|
||||
assert_ok!(Staking::set_bonding_duration(2));
|
||||
assert_eq!(Staking::bonding_duration(), 2);
|
||||
|
||||
// Block 1: Add three validators. No obvious change.
|
||||
System::set_block_number(1);
|
||||
@@ -347,7 +271,7 @@ fn staking_should_work() {
|
||||
|
||||
// Block 5: Transfer stake from highest to lowest. No change yet.
|
||||
System::set_block_number(5);
|
||||
assert_ok!(Staking::transfer(&4, 1.into(), 40));
|
||||
assert_ok!(Balances::transfer(&4, 1.into(), 40));
|
||||
Session::check_rotate_session();
|
||||
|
||||
// Block 6: Lowest now validator.
|
||||
@@ -384,20 +308,20 @@ fn nominating_and_rewards_should_work() {
|
||||
Session::check_rotate_session();
|
||||
assert_eq!(Staking::current_era(), 1);
|
||||
assert_eq!(Session::validators(), vec![1, 3]); // 4 + 1, 3
|
||||
assert_eq!(Staking::voting_balance(&1), 10);
|
||||
assert_eq!(Staking::voting_balance(&2), 20);
|
||||
assert_eq!(Staking::voting_balance(&3), 30);
|
||||
assert_eq!(Staking::voting_balance(&4), 40);
|
||||
assert_eq!(Balances::total_balance(&1), 10);
|
||||
assert_eq!(Balances::total_balance(&2), 20);
|
||||
assert_eq!(Balances::total_balance(&3), 30);
|
||||
assert_eq!(Balances::total_balance(&4), 40);
|
||||
|
||||
System::set_block_number(2);
|
||||
assert_ok!(Staking::unnominate(&4, 0));
|
||||
Session::check_rotate_session();
|
||||
assert_eq!(Staking::current_era(), 2);
|
||||
assert_eq!(Session::validators(), vec![3, 2]);
|
||||
assert_eq!(Staking::voting_balance(&1), 12);
|
||||
assert_eq!(Staking::voting_balance(&2), 20);
|
||||
assert_eq!(Staking::voting_balance(&3), 40);
|
||||
assert_eq!(Staking::voting_balance(&4), 48);
|
||||
assert_eq!(Balances::total_balance(&1), 12);
|
||||
assert_eq!(Balances::total_balance(&2), 20);
|
||||
assert_eq!(Balances::total_balance(&3), 40);
|
||||
assert_eq!(Balances::total_balance(&4), 48);
|
||||
|
||||
System::set_block_number(3);
|
||||
assert_ok!(Staking::stake(&4));
|
||||
@@ -405,17 +329,17 @@ fn nominating_and_rewards_should_work() {
|
||||
assert_ok!(Staking::nominate(&3, 1.into()));
|
||||
Session::check_rotate_session();
|
||||
assert_eq!(Session::validators(), vec![1, 4]);
|
||||
assert_eq!(Staking::voting_balance(&1), 12);
|
||||
assert_eq!(Staking::voting_balance(&2), 30);
|
||||
assert_eq!(Staking::voting_balance(&3), 50);
|
||||
assert_eq!(Staking::voting_balance(&4), 48);
|
||||
assert_eq!(Balances::total_balance(&1), 12);
|
||||
assert_eq!(Balances::total_balance(&2), 30);
|
||||
assert_eq!(Balances::total_balance(&3), 50);
|
||||
assert_eq!(Balances::total_balance(&4), 48);
|
||||
|
||||
System::set_block_number(4);
|
||||
Session::check_rotate_session();
|
||||
assert_eq!(Staking::voting_balance(&1), 13);
|
||||
assert_eq!(Staking::voting_balance(&2), 30);
|
||||
assert_eq!(Staking::voting_balance(&3), 58);
|
||||
assert_eq!(Staking::voting_balance(&4), 58);
|
||||
assert_eq!(Balances::total_balance(&1), 13);
|
||||
assert_eq!(Balances::total_balance(&2), 30);
|
||||
assert_eq!(Balances::total_balance(&3), 58);
|
||||
assert_eq!(Balances::total_balance(&4), 58);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -424,7 +348,7 @@ fn nominating_slashes_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, 2, 2, 0, true, 10), || {
|
||||
assert_eq!(Staking::era_length(), 4);
|
||||
assert_eq!(Staking::validator_count(), 2);
|
||||
assert_eq!(Staking::bonding_duration(), 3);
|
||||
assert_eq!(Staking::bonding_duration(), 12);
|
||||
assert_eq!(Session::validators(), vec![10, 20]);
|
||||
|
||||
System::set_block_number(2);
|
||||
@@ -440,18 +364,19 @@ fn nominating_slashes_should_work() {
|
||||
|
||||
assert_eq!(Staking::current_era(), 1);
|
||||
assert_eq!(Session::validators(), vec![1, 3]); // 1 + 4, 3 + 2
|
||||
assert_eq!(Staking::voting_balance(&1), 10);
|
||||
assert_eq!(Staking::voting_balance(&2), 20);
|
||||
assert_eq!(Staking::voting_balance(&3), 30);
|
||||
assert_eq!(Staking::voting_balance(&4), 40);
|
||||
assert_eq!(Balances::total_balance(&1), 10);
|
||||
assert_eq!(Balances::total_balance(&2), 20);
|
||||
assert_eq!(Balances::total_balance(&3), 30);
|
||||
assert_eq!(Balances::total_balance(&4), 40);
|
||||
|
||||
System::set_block_number(5);
|
||||
::system::ExtrinsicIndex::<Test>::put(1);
|
||||
assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0, 1]));
|
||||
assert_eq!(Staking::voting_balance(&1), 0);
|
||||
assert_eq!(Staking::voting_balance(&2), 20);
|
||||
assert_eq!(Staking::voting_balance(&3), 10);
|
||||
assert_eq!(Staking::voting_balance(&4), 30);
|
||||
Staking::on_offline_validator(0);
|
||||
Staking::on_offline_validator(1);
|
||||
assert_eq!(Balances::total_balance(&1), 0);
|
||||
assert_eq!(Balances::total_balance(&2), 20);
|
||||
assert_eq!(Balances::total_balance(&3), 10);
|
||||
assert_eq!(Balances::total_balance(&4), 30);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -536,235 +461,22 @@ fn staking_eras_work() {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn staking_balance_works() {
|
||||
with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || {
|
||||
Staking::set_free_balance(&1, 42);
|
||||
assert_eq!(Staking::free_balance(&1), 42);
|
||||
assert_eq!(Staking::reserved_balance(&1), 0);
|
||||
assert_eq!(Staking::voting_balance(&1), 42);
|
||||
assert_eq!(Staking::free_balance(&2), 0);
|
||||
assert_eq!(Staking::reserved_balance(&2), 0);
|
||||
assert_eq!(Staking::voting_balance(&2), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn staking_balance_transfer_works() {
|
||||
with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || {
|
||||
Staking::set_free_balance(&1, 111);
|
||||
Staking::increase_total_stake_by(111);
|
||||
assert_ok!(Staking::transfer(&1, 2.into(), 69));
|
||||
assert_eq!(Staking::voting_balance(&1), 42);
|
||||
assert_eq!(Staking::voting_balance(&2), 69);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn staking_balance_transfer_when_bonded_should_not_work() {
|
||||
with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || {
|
||||
Staking::set_free_balance(&1, 111);
|
||||
Balances::set_free_balance(&1, 111);
|
||||
assert_ok!(Staking::stake(&1));
|
||||
assert_noop!(Staking::transfer(&1, 2.into(), 69), "bondage too high to send value");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reserving_balance_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || {
|
||||
Staking::set_free_balance(&1, 111);
|
||||
|
||||
assert_eq!(Staking::voting_balance(&1), 111);
|
||||
assert_eq!(Staking::free_balance(&1), 111);
|
||||
assert_eq!(Staking::reserved_balance(&1), 0);
|
||||
|
||||
assert_ok!(Staking::reserve(&1, 69));
|
||||
|
||||
assert_eq!(Staking::voting_balance(&1), 111);
|
||||
assert_eq!(Staking::free_balance(&1), 42);
|
||||
assert_eq!(Staking::reserved_balance(&1), 69);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn staking_balance_transfer_when_reserved_should_not_work() {
|
||||
with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || {
|
||||
Staking::set_free_balance(&1, 111);
|
||||
assert_ok!(Staking::reserve(&1, 69));
|
||||
assert_noop!(Staking::transfer(&1, 2.into(), 69), "balance too low to send value");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deducting_balance_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || {
|
||||
Staking::set_free_balance(&1, 111);
|
||||
assert_ok!(Staking::reserve(&1, 69));
|
||||
assert_eq!(Staking::free_balance(&1), 42);
|
||||
assert_noop!(Balances::transfer(&1, 2.into(), 69), "cannot transfer illiquid funds");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deducting_balance_when_bonded_should_not_work() {
|
||||
with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || {
|
||||
Staking::set_free_balance(&1, 111);
|
||||
Balances::set_free_balance(&1, 111);
|
||||
<Bondage<Test>>::insert(1, 2);
|
||||
System::set_block_number(1);
|
||||
assert_eq!(Staking::unlock_block(&1), LockStatus::LockedUntil(2));
|
||||
assert_noop!(Staking::reserve(&1, 69), "free funds are still bonded");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn refunding_balance_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || {
|
||||
Staking::set_free_balance(&1, 42);
|
||||
Staking::set_reserved_balance(&1, 69);
|
||||
Staking::unreserve(&1, 69);
|
||||
assert_eq!(Staking::free_balance(&1), 111);
|
||||
assert_eq!(Staking::reserved_balance(&1), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slashing_balance_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || {
|
||||
Staking::set_free_balance(&1, 111);
|
||||
Staking::increase_total_stake_by(111);
|
||||
assert_ok!(Staking::reserve(&1, 69));
|
||||
assert!(Staking::slash(&1, 69).is_none());
|
||||
assert_eq!(Staking::free_balance(&1), 0);
|
||||
assert_eq!(Staking::reserved_balance(&1), 42);
|
||||
assert_eq!(<TotalStake<Test>>::get(), 44);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slashing_incomplete_balance_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || {
|
||||
Staking::set_free_balance(&1, 42);
|
||||
Staking::increase_total_stake_by(42);
|
||||
assert_ok!(Staking::reserve(&1, 21));
|
||||
assert!(Staking::slash(&1, 69).is_some());
|
||||
assert_eq!(Staking::free_balance(&1), 0);
|
||||
assert_eq!(Staking::reserved_balance(&1), 0);
|
||||
assert_eq!(<TotalStake<Test>>::get(), 2);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unreserving_balance_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || {
|
||||
Staking::set_free_balance(&1, 111);
|
||||
assert_ok!(Staking::reserve(&1, 111));
|
||||
Staking::unreserve(&1, 42);
|
||||
assert_eq!(Staking::reserved_balance(&1), 69);
|
||||
assert_eq!(Staking::free_balance(&1), 42);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slashing_reserved_balance_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || {
|
||||
Staking::set_free_balance(&1, 111);
|
||||
Staking::increase_total_stake_by(111);
|
||||
assert_ok!(Staking::reserve(&1, 111));
|
||||
assert!(Staking::slash_reserved(&1, 42).is_none());
|
||||
assert_eq!(Staking::reserved_balance(&1), 69);
|
||||
assert_eq!(Staking::free_balance(&1), 0);
|
||||
assert_eq!(<TotalStake<Test>>::get(), 71);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slashing_incomplete_reserved_balance_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || {
|
||||
Staking::set_free_balance(&1, 111);
|
||||
Staking::increase_total_stake_by(111);
|
||||
assert_ok!(Staking::reserve(&1, 42));
|
||||
assert!(Staking::slash_reserved(&1, 69).is_some());
|
||||
assert_eq!(Staking::free_balance(&1), 69);
|
||||
assert_eq!(Staking::reserved_balance(&1), 0);
|
||||
assert_eq!(<TotalStake<Test>>::get(), 71);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transferring_reserved_balance_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || {
|
||||
Staking::set_free_balance(&1, 110);
|
||||
Staking::set_free_balance(&2, 1);
|
||||
assert_ok!(Staking::reserve(&1, 110));
|
||||
assert_ok!(Staking::transfer_reserved(&1, &2, 41), None);
|
||||
assert_eq!(Staking::reserved_balance(&1), 69);
|
||||
assert_eq!(Staking::free_balance(&1), 0);
|
||||
assert_eq!(Staking::reserved_balance(&2), 0);
|
||||
assert_eq!(Staking::free_balance(&2), 42);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transferring_reserved_balance_to_nonexistent_should_fail() {
|
||||
with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || {
|
||||
Staking::set_free_balance(&1, 111);
|
||||
assert_ok!(Staking::reserve(&1, 111));
|
||||
assert_noop!(Staking::transfer_reserved(&1, &2, 42), "beneficiary account must pre-exist");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transferring_incomplete_reserved_balance_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || {
|
||||
Staking::set_free_balance(&1, 110);
|
||||
Staking::set_free_balance(&2, 1);
|
||||
assert_ok!(Staking::reserve(&1, 41));
|
||||
assert!(Staking::transfer_reserved(&1, &2, 69).unwrap().is_some());
|
||||
assert_eq!(Staking::reserved_balance(&1), 0);
|
||||
assert_eq!(Staking::free_balance(&1), 69);
|
||||
assert_eq!(Staking::reserved_balance(&2), 0);
|
||||
assert_eq!(Staking::free_balance(&2), 42);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transferring_too_high_value_should_not_panic() {
|
||||
with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || {
|
||||
<FreeBalance<Test>>::insert(1, u64::max_value());
|
||||
<FreeBalance<Test>>::insert(2, 1);
|
||||
|
||||
assert_err!(
|
||||
Staking::transfer(&1, 2.into(), u64::max_value()),
|
||||
"destination balance too high to receive value"
|
||||
);
|
||||
|
||||
assert_eq!(Staking::free_balance(&1), u64::max_value());
|
||||
assert_eq!(Staking::free_balance(&2), 1);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn account_removal_on_free_too_low() {
|
||||
with_externalities(&mut new_test_ext(100, 1, 3, 1, false, 0), || {
|
||||
// Setup two accounts with free balance above the exsistential threshold.
|
||||
{
|
||||
Staking::set_free_balance(&1, 110);
|
||||
Staking::increase_total_stake_by(110);
|
||||
|
||||
Staking::set_free_balance(&2, 110);
|
||||
Staking::increase_total_stake_by(110);
|
||||
|
||||
assert_eq!(<TotalStake<Test>>::get(), 732);
|
||||
}
|
||||
|
||||
// Transfer funds from account 1 of such amount that after this transfer
|
||||
// the balance of account 1 will be below the exsistential threshold.
|
||||
// This should lead to the removal of all balance of this account.
|
||||
assert_ok!(Staking::transfer(&1, 2.into(), 20));
|
||||
|
||||
// Verify free balance removal of account 1.
|
||||
assert_eq!(Staking::free_balance(&1), 0);
|
||||
|
||||
// Verify that TotalStake tracks balance removal when free balance is too low.
|
||||
assert_eq!(<TotalStake<Test>>::get(), 642);
|
||||
assert_noop!(Balances::reserve(&1, 69), "cannot transfer illiquid funds");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate Demo is free software: you can redistribute it and/or modify
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate Demo is distributed in the hope that it will be useful,
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! System manager: Handles lowest level stuff like depositing logs, basic set up and take down of
|
||||
//! temporary storage entries, access to old block hashes.
|
||||
@@ -45,7 +45,7 @@ extern crate safe_mix;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use primitives::traits::{self, CheckEqual, SimpleArithmetic, SimpleBitOps, Zero, One, Bounded,
|
||||
Hash, Member, MaybeDisplay};
|
||||
Hash, Member, MaybeDisplay, RefInto, MaybeEmpty};
|
||||
use runtime_support::{StorageValue, StorageMap, Parameter};
|
||||
use safe_mix::TripletMix;
|
||||
|
||||
@@ -69,6 +69,9 @@ pub fn extrinsics_data_root<H: Hash>(xts: Vec<Vec<u8>>) -> H::Output {
|
||||
}
|
||||
|
||||
pub trait Trait: Eq + Clone {
|
||||
// We require that PublicAux impl MaybeEmpty, since we require that inherents - or unsigned
|
||||
// user-level extrinsics - can exist.
|
||||
type PublicAux: RefInto<Self::AccountId> + MaybeEmpty;
|
||||
type Index: Parameter + Member + Default + MaybeDisplay + SimpleArithmetic + Copy;
|
||||
type BlockNumber: Parameter + Member + MaybeDisplay + SimpleArithmetic + Default + Bounded + Copy + rstd::hash::Hash;
|
||||
type Hash: Parameter + Member + MaybeDisplay + SimpleBitOps + Default + Copy + CheckEqual + rstd::hash::Hash + AsRef<[u8]>;
|
||||
@@ -287,6 +290,7 @@ mod tests {
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct Test;
|
||||
impl Trait for Test {
|
||||
type PublicAux = u64;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate Demo is free software: you can redistribute it and/or modify
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate Demo is distributed in the hope that it will be useful,
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Timestamp manager: just handles the current timestamp.
|
||||
|
||||
@@ -43,7 +43,7 @@ use runtime_support::dispatch::Result;
|
||||
use runtime_primitives::traits::{Executable, MaybeEmpty, SimpleArithmetic, As, Zero};
|
||||
|
||||
pub trait Trait: consensus::Trait where
|
||||
<Self as consensus::Trait>::PublicAux: MaybeEmpty
|
||||
<Self as system::Trait>::PublicAux: MaybeEmpty
|
||||
{
|
||||
// the position of the required timestamp-set extrinsic.
|
||||
const TIMESTAMP_SET_POSITION: u32;
|
||||
@@ -152,6 +152,7 @@ mod tests {
|
||||
type PublicAux = u64;
|
||||
}
|
||||
impl system::Trait for Test {
|
||||
type PublicAux = u64;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
@@ -162,8 +163,9 @@ mod tests {
|
||||
type Event = ();
|
||||
}
|
||||
impl consensus::Trait for Test {
|
||||
type PublicAux = u64;
|
||||
const NOTE_OFFLINE_POSITION: u32 = 1;
|
||||
type SessionKey = u64;
|
||||
type OnOfflineValidator = ();
|
||||
}
|
||||
impl Trait for Test {
|
||||
const TIMESTAMP_SET_POSITION: u32 = 0;
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Demo.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate Demo is free software: you can redistribute it and/or modify
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate Demo is distributed in the hope that it will be useful,
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate Demo. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Version module for runtime; Provide a function that returns runtime version.
|
||||
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
Reference in New Issue
Block a user