mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-08 15:58:02 +00:00
Refactor all (demo) runtime modules to use new storage (#98)
* Completely rework dispatch mechanism into something modular. Not yet complete but 75% there. * Council vote tests. * Fix tests. * whitespace. * Fix demo runtime tests. * Fix up tests. * Remove dead code. * Timestamp uses new storage API. * Move over system module to new API. * Much nicer storage API, moved over staking module. * More refactoring. * Democracy uses new storage API. * Council uses new RPC. * Fix more tests. * Use match for Id * Use match for Id * Make PrivPass better protected. * Address other grumbles. * Give PrivPass a private member. * Testing PrivPass. * Add docs. * Recompile binaries after merge. * Remove duplicated code. * New binaries. * Docs * Docs * avoid use of (arguably) confusing terminology.
This commit is contained in:
@@ -38,15 +38,15 @@ mod tests {
|
||||
use runtime_io;
|
||||
use super::Executor;
|
||||
use substrate_executor::WasmExecutor;
|
||||
use codec::{KeyedVec, Slicable, Joiner};
|
||||
use codec::{Slicable, Joiner};
|
||||
use keyring::Keyring::{self, Alice, Bob};
|
||||
use runtime_support::Hashable;
|
||||
use runtime_support::{Hashable, StorageValue, StorageMap};
|
||||
use state_machine::{CodeExecutor, TestExternalities};
|
||||
use primitives::twox_128;
|
||||
use demo_primitives::{Hash, Header, BlockNumber, Digest};
|
||||
use demo_runtime::transaction::{Transaction, UncheckedTransaction};
|
||||
use demo_runtime::block::Block;
|
||||
use demo_runtime::runtime::staking::{self, balance, BALANCE_OF};
|
||||
use demo_runtime::runtime::staking::{self, FreeBalanceOf, balance};
|
||||
use demo_runtime::dispatch;
|
||||
use ed25519::{Public, Pair};
|
||||
|
||||
@@ -75,8 +75,8 @@ mod tests {
|
||||
#[test]
|
||||
fn panic_execution_with_foreign_code_gives_error() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TRANSACTION_FEE).to_vec() => vec![0u8; 8]
|
||||
twox_128(&FreeBalanceOf::key_for(*Alice)).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TransactionFee::key()).to_vec() => vec![0u8; 8]
|
||||
];
|
||||
|
||||
let r = Executor::new().call(&mut t, BLOATY_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx()));
|
||||
@@ -86,8 +86,8 @@ mod tests {
|
||||
#[test]
|
||||
fn panic_execution_with_native_equivalent_code_gives_error() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TRANSACTION_FEE).to_vec() => vec![0u8; 8]
|
||||
twox_128(&FreeBalanceOf::key_for(*Alice)).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TransactionFee::key()).to_vec() => vec![0u8; 8]
|
||||
];
|
||||
|
||||
let r = Executor::new().call(&mut t, COMPACT_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx()));
|
||||
@@ -97,8 +97,8 @@ mod tests {
|
||||
#[test]
|
||||
fn successful_execution_with_native_equivalent_code_gives_ok() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TRANSACTION_FEE).to_vec() => vec![0u8; 8]
|
||||
twox_128(&FreeBalanceOf::key_for(*Alice)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TransactionFee::key()).to_vec() => vec![0u8; 8]
|
||||
];
|
||||
|
||||
let r = Executor::new().call(&mut t, COMPACT_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx()));
|
||||
@@ -113,8 +113,8 @@ mod tests {
|
||||
#[test]
|
||||
fn successful_execution_with_foreign_code_gives_ok() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TRANSACTION_FEE).to_vec() => vec![0u8; 8]
|
||||
twox_128(&FreeBalanceOf::key_for(*Alice)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TransactionFee::key()).to_vec() => vec![0u8; 8]
|
||||
];
|
||||
|
||||
let r = Executor::new().call(&mut t, BLOATY_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx()));
|
||||
@@ -158,7 +158,7 @@ mod tests {
|
||||
construct_block(
|
||||
1,
|
||||
[69u8; 32].into(),
|
||||
hex!("cfb76a83e40aa6a0d3f92255e6229e74808cae31d9f46053f31129b797540d03").into(),
|
||||
hex!("7a388ce5b4eeadbb9268ae96e8822b223f4fd1841327d99f4e1c21fad81f97f2").into(),
|
||||
vec![Transaction {
|
||||
signed: Alice.into(),
|
||||
nonce: 0,
|
||||
@@ -171,7 +171,7 @@ mod tests {
|
||||
construct_block(
|
||||
2,
|
||||
block1().1,
|
||||
hex!("c713bd003e303648e8d904bcfa44084865c9b70c398547e678028cc7cf60907f").into(),
|
||||
hex!("e4eb71be8b816f2061f32f284e9b429562cdc1b82f11725e5f965ff23439f5e9").into(),
|
||||
vec![
|
||||
Transaction {
|
||||
signed: Bob.into(),
|
||||
@@ -228,8 +228,8 @@ mod tests {
|
||||
#[test]
|
||||
fn panic_execution_gives_error() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TRANSACTION_FEE).to_vec() => vec![0u8; 8]
|
||||
twox_128(&FreeBalanceOf::key_for(*Alice)).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TransactionFee::key()).to_vec() => vec![0u8; 8]
|
||||
];
|
||||
|
||||
let foreign_code = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm");
|
||||
@@ -240,8 +240,8 @@ mod tests {
|
||||
#[test]
|
||||
fn successful_execution_gives_ok() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TRANSACTION_FEE).to_vec() => vec![0u8; 8]
|
||||
twox_128(&FreeBalanceOf::key_for(*Alice)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TransactionFee::key()).to_vec() => vec![0u8; 8]
|
||||
];
|
||||
|
||||
let foreign_code = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm");
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
use codec::{KeyedVec, Joiner};
|
||||
use std::collections::HashMap;
|
||||
use runtime_io::twox_128;
|
||||
use runtime_support::Hashable;
|
||||
use runtime_support::{Hashable, StorageMap, StorageList, StorageValue};
|
||||
use primitives::Block;
|
||||
use demo_primitives::{BlockNumber, AccountId};
|
||||
use runtime::staking::Balance;
|
||||
@@ -80,40 +80,37 @@ impl GenesisConfig {
|
||||
pub fn genesis_map(&self) -> HashMap<Vec<u8>, Vec<u8>> {
|
||||
let wasm_runtime = include_bytes!("../wasm/genesis.wasm").to_vec();
|
||||
vec![
|
||||
(&session::SESSION_LENGTH[..], vec![].and(&self.session_length)),
|
||||
(&session::VALIDATOR_COUNT[..], vec![].and(&(self.validators.len() as u32))),
|
||||
(session::SessionLength::key(), vec![].and(&self.session_length)),
|
||||
(session::Validators::key(), vec![].and(&self.validators)),
|
||||
|
||||
(&staking::INTENTION_COUNT[..], vec![].and(&0u32)),
|
||||
(&staking::SESSIONS_PER_ERA[..], vec![].and(&self.sessions_per_era)),
|
||||
(&staking::CURRENT_ERA[..], vec![].and(&0u64)),
|
||||
(&staking::Intention::len_key()[..], vec![].and(&0u32)),
|
||||
(&staking::SessionsPerEra::key()[..], vec![].and(&self.sessions_per_era)),
|
||||
(&staking::CurrentEra::key()[..], vec![].and(&0u64)),
|
||||
|
||||
(&democracy::LAUNCH_PERIOD[..], vec![].and(&self.launch_period)),
|
||||
(&democracy::VOTING_PERIOD[..], vec![].and(&self.voting_period)),
|
||||
(&democracy::MINIMUM_DEPOSIT[..], vec![].and(&self.minimum_deposit)),
|
||||
(democracy::LaunchPeriod::key(), vec![].and(&self.launch_period)),
|
||||
(democracy::VotingPeriod::key(), vec![].and(&self.voting_period)),
|
||||
(democracy::MinimumDeposit::key(), vec![].and(&self.minimum_deposit)),
|
||||
|
||||
(&council::CANDIDACY_BOND[..], vec![].and(&self.candidacy_bond)),
|
||||
(&council::VOTING_BOND[..], vec![].and(&self.voter_bond)),
|
||||
(&council::PRESENT_SLASH_PER_VOTER[..], vec![].and(&self.present_slash_per_voter)),
|
||||
(&council::CARRY_COUNT[..], vec![].and(&self.carry_count)),
|
||||
(&council::PRESENTATION_DURATION[..], vec![].and(&self.presentation_duration)),
|
||||
(&council::VOTING_PERIOD[..], vec![].and(&self.council_election_voting_period)),
|
||||
(&council::TERM_DURATION[..], vec![].and(&self.council_term_duration)),
|
||||
(&council::DESIRED_SEATS[..], vec![].and(&self.desired_seats)),
|
||||
(&council::INACTIVE_GRACE_PERIOD[..], vec![].and(&self.inactive_grace_period)),
|
||||
(council::CandidacyBond::key(), vec![].and(&self.candidacy_bond)),
|
||||
(council::VotingBond::key(), vec![].and(&self.voter_bond)),
|
||||
(council::PresentSlashPerVoter::key(), vec![].and(&self.present_slash_per_voter)),
|
||||
(council::CarryCount::key(), vec![].and(&self.carry_count)),
|
||||
(council::PresentationDuration::key(), vec![].and(&self.presentation_duration)),
|
||||
(council::VotingPeriod::key(), vec![].and(&self.council_election_voting_period)),
|
||||
(council::TermDuration::key(), vec![].and(&self.council_term_duration)),
|
||||
(council::DesiredSeats::key(), vec![].and(&self.desired_seats)),
|
||||
(council::InactiveGracePeriod::key(), vec![].and(&self.inactive_grace_period)),
|
||||
|
||||
(&council_vote::COOLOFF_PERIOD[..], vec![].and(&self.cooloff_period)),
|
||||
(&council_vote::VOTING_PERIOD[..], vec![].and(&self.council_proposal_voting_period))
|
||||
(council_vote::CooloffPeriod::key(), vec![].and(&self.cooloff_period)),
|
||||
(council_vote::VotingPeriod::key(), vec![].and(&self.council_proposal_voting_period))
|
||||
].into_iter()
|
||||
.map(|(k, v)| (k.into(), v))
|
||||
.chain(self.validators.iter()
|
||||
.enumerate()
|
||||
.map(|(i, account)| ((i as u32).to_keyed_vec(session::VALIDATOR_AT), vec![].and(account)))
|
||||
).chain(self.balances.iter()
|
||||
.map(|&(account, balance)| (account.to_keyed_vec(staking::BALANCE_OF), vec![].and(&balance)))
|
||||
.chain(self.balances.iter()
|
||||
.map(|&(account, balance)| (staking::FreeBalanceOf::key_for(&account), vec![].and(&balance)))
|
||||
)
|
||||
.map(|(k, v)| (twox_128(&k[..])[..].to_vec(), v.to_vec()))
|
||||
.chain(vec![
|
||||
(system::CODE[..].into(), wasm_runtime),
|
||||
(system::CODE.to_vec(), wasm_runtime),
|
||||
(consensus::AUTHORITY_COUNT[..].into(), vec![].and(&(self.authorities.len() as u32))),
|
||||
].into_iter())
|
||||
.chain(self.authorities.iter()
|
||||
@@ -127,6 +124,6 @@ impl GenesisConfig {
|
||||
pub fn additional_storage_with_genesis(genesis_block: &Block) -> HashMap<Vec<u8>, Vec<u8>> {
|
||||
use codec::Slicable;
|
||||
map![
|
||||
twox_128(&0u64.to_keyed_vec(system::BLOCK_HASH_AT)).to_vec() => genesis_block.header.blake2_256().encode()
|
||||
system::BlockHashAt::key_for(&0) => genesis_block.header.blake2_256().encode()
|
||||
]
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#[allow(unused_imports)] #[macro_use] extern crate substrate_runtime_std as rstd;
|
||||
#[macro_use] extern crate substrate_runtime_io as runtime_io;
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
#[macro_use] extern crate substrate_runtime_support as runtime_support;
|
||||
#[cfg(any(feature = "std", test))] extern crate substrate_keyring as keyring;
|
||||
|
||||
#[cfg(feature = "std")] #[macro_use] extern crate serde_derive;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
use rstd::prelude::*;
|
||||
use codec::KeyedVec;
|
||||
use runtime_support::storage;
|
||||
use runtime_support::{StorageMap, StorageValue};
|
||||
use demo_primitives::{AccountId, Hash, BlockNumber};
|
||||
use runtime::{staking, system, session};
|
||||
use runtime::democracy::PrivPass;
|
||||
@@ -80,103 +80,66 @@ use runtime::staking::{PublicPass, Balance};
|
||||
|
||||
pub type VoteIndex = u32;
|
||||
|
||||
// parameters
|
||||
pub const CANDIDACY_BOND: &[u8] = b"cou:cbo";
|
||||
pub const VOTING_BOND: &[u8] = b"cou:vbo";
|
||||
pub const PRESENT_SLASH_PER_VOTER: &[u8] = b"cou:pss";
|
||||
pub const CARRY_COUNT: &[u8] = b"cou:cco";
|
||||
pub const PRESENTATION_DURATION: &[u8] = b"cou:pdu";
|
||||
pub const INACTIVE_GRACE_PERIOD: &[u8] = b"cou:vgp";
|
||||
pub const VOTING_PERIOD: &[u8] = b"cou:per";
|
||||
pub const TERM_DURATION: &[u8] = b"cou:trm";
|
||||
pub const DESIRED_SEATS: &[u8] = b"cou:sts";
|
||||
storage_items! {
|
||||
// parameters
|
||||
// How much should be locked up in order to submit one's candidacy.
|
||||
pub CandidacyBond get(candidacy_bond): b"cou:cbo" => required Balance;
|
||||
// How much should be locked up in order to be able to submit votes.
|
||||
pub VotingBond get(voting_bond): b"cou:vbo" => required Balance;
|
||||
// The punishment, per voter, if you provide an invalid presentation.
|
||||
pub PresentSlashPerVoter get(present_slash_per_voter): b"cou:pss" => required Balance;
|
||||
// How many runners-up should have their approvals persist until the next vote.
|
||||
pub CarryCount get(carry_count): b"cou:cco" => required u32;
|
||||
// How long to give each top candidate to present themselves after the vote ends.
|
||||
pub PresentationDuration get(presentation_duration): b"cou:pdu" => required BlockNumber;
|
||||
// How many votes need to go by after a voter's last vote before they can be reaped if their
|
||||
// approvals are moot.
|
||||
pub InactiveGracePeriod get(inactivity_grace_period): b"cou:vgp" => required VoteIndex;
|
||||
// How often (in blocks) to check for new votes.
|
||||
pub VotingPeriod get(voting_period): b"cou:per" => required BlockNumber;
|
||||
// How long each position is active for.
|
||||
pub TermDuration get(term_duration): b"cou:trm" => required BlockNumber;
|
||||
// Number of accounts that should be sitting on the council.
|
||||
pub DesiredSeats get(desired_seats): b"cou:sts" => required u32;
|
||||
|
||||
// permanent state (always relevant, changes only at the finalisation of voting)
|
||||
pub const ACTIVE_COUNCIL: &[u8] = b"cou:act"; // Vec<(AccountId, expiry: BlockNumber)>
|
||||
pub const VOTE_COUNT: &[u8] = b"cou:vco"; // VoteIndex
|
||||
// permanent state (always relevant, changes only at the finalisation of voting)
|
||||
// The current council. When there's a vote going on, this should still be used for executive
|
||||
// matters.
|
||||
pub ActiveCouncil get(active_council): b"cou:act" => default Vec<(AccountId, BlockNumber)>;
|
||||
// The total number of votes that have happened or are in progress.
|
||||
pub VoteCount get(vote_index): b"cou:vco" => default VoteIndex;
|
||||
|
||||
// persistent state (always relevant, changes constantly)
|
||||
pub const APPROVALS_OF: &[u8] = b"cou:apr:"; // Vec<bool>
|
||||
pub const REGISTER_INFO_OF: &[u8] = b"cou:reg:"; // Candidate -> (VoteIndex, u32)
|
||||
pub const LAST_ACTIVE_OF: &[u8] = b"cou:lac:"; // Voter -> VoteIndex
|
||||
pub const VOTERS: &[u8] = b"cou:vrs"; // Vec<AccountId>
|
||||
pub const CANDIDATES: &[u8] = b"cou:can"; // Vec<AccountId>, has holes
|
||||
pub const CANDIDATE_COUNT: &[u8] = b"cou:cnc"; // u32
|
||||
// persistent state (always relevant, changes constantly)
|
||||
// The last cleared vote index that this voter was last active at.
|
||||
pub ApprovalsOf get(approvals_of): b"cou:apr" => default map [ AccountId => Vec<bool> ];
|
||||
// The vote index and list slot that the candidate `who` was registered or `None` if they are not
|
||||
// currently registered.
|
||||
pub RegisterInfoOf get(candidate_reg_info): b"cou:reg" => map [ AccountId => (VoteIndex, u32) ];
|
||||
// The last cleared vote index that this voter was last active at.
|
||||
pub LastActiveOf get(voter_last_active): b"cou:lac" => map [ AccountId => VoteIndex ];
|
||||
// The present voter list.
|
||||
pub Voters get(voters): b"cou:vrs" => default Vec<AccountId>;
|
||||
// The present candidate list.
|
||||
pub Candidates get(candidates): b"cou:can" => default Vec<AccountId>; // has holes
|
||||
pub CandidateCount get(candidate_count): b"cou:cnc" => default u32;
|
||||
|
||||
// temporary state (only relevant during finalisation/presentation)
|
||||
pub const NEXT_FINALISE: &[u8] = b"cou:nxt";
|
||||
pub const SNAPSHOTED_STAKES: &[u8] = b"cou:sss"; // Vec<Balance>
|
||||
pub const LEADERBOARD: &[u8] = b"cou:win"; // Vec<(Balance, AccountId)> ORDERED low -> high
|
||||
|
||||
/// How much should be locked up in order to submit one's candidacy.
|
||||
pub fn candidacy_bond() -> Balance {
|
||||
storage::get(CANDIDACY_BOND)
|
||||
.expect("all core parameters of council module must be in place")
|
||||
}
|
||||
|
||||
/// How much should be locked up in order to be able to submit votes.
|
||||
pub fn voting_bond() -> Balance {
|
||||
storage::get(VOTING_BOND)
|
||||
.expect("all core parameters of council module must be in place")
|
||||
}
|
||||
|
||||
/// How long to give each top candidate to present themselves after the vote ends.
|
||||
pub fn presentation_duration() -> BlockNumber {
|
||||
storage::get(PRESENTATION_DURATION)
|
||||
.expect("all core parameters of council module must be in place")
|
||||
}
|
||||
|
||||
/// How often (in blocks) to check for new votes.
|
||||
pub fn voting_period() -> BlockNumber {
|
||||
storage::get(VOTING_PERIOD)
|
||||
.expect("all core parameters of council module must be in place")
|
||||
}
|
||||
|
||||
/// How many votes need to go by after a voter's last vote before they can be reaped if their
|
||||
/// approvals are moot.
|
||||
pub fn inactivity_grace_period() -> VoteIndex {
|
||||
storage::get(INACTIVE_GRACE_PERIOD)
|
||||
.expect("all core parameters of council module must be in place")
|
||||
}
|
||||
|
||||
/// How long each position is active for.
|
||||
pub fn term_duration() -> BlockNumber {
|
||||
storage::get(TERM_DURATION)
|
||||
.expect("all core parameters of council module must be in place")
|
||||
}
|
||||
|
||||
/// The punishment, per voter, if you provide an invalid presentation.
|
||||
pub fn present_slash_per_voter() -> Balance {
|
||||
storage::get(PRESENT_SLASH_PER_VOTER)
|
||||
.expect("all core parameters of council module must be in place")
|
||||
}
|
||||
|
||||
/// Number of accounts that should be sitting on the council.
|
||||
pub fn desired_seats() -> u32 {
|
||||
storage::get(DESIRED_SEATS)
|
||||
.expect("all core parameters of council module must be in place")
|
||||
}
|
||||
|
||||
/// How many runners-up should have their approvals persist until the next vote.
|
||||
pub fn carry_count() -> u32 {
|
||||
storage::get(CARRY_COUNT)
|
||||
.expect("all core parameters of council module must be in place")
|
||||
// temporary state (only relevant during finalisation/presentation)
|
||||
// The accounts holding the seats that will become free on the next tally.
|
||||
pub NextFinalise get(next_finalise): b"cou:nxt" => (BlockNumber, u32, Vec<AccountId>);
|
||||
// The stakes as they were at the point that the vote ended.
|
||||
pub SnapshotedStakes get(snapshoted_stakes): b"cou:sss" => required Vec<Balance>;
|
||||
// Get the leaderboard if we;re in the presentation phase.
|
||||
pub Leaderboard get(leaderboard): b"cou:win" => Vec<(Balance, AccountId)>; // ORDERED low -> high
|
||||
}
|
||||
|
||||
/// True if we're currently in a presentation period.
|
||||
pub fn presentation_active() -> bool {
|
||||
storage::exists(NEXT_FINALISE)
|
||||
}
|
||||
|
||||
/// The current council. When there's a vote going on, this should still be used for executive
|
||||
/// matters.
|
||||
pub fn active_council() -> Vec<(AccountId, BlockNumber)> {
|
||||
storage::get_or_default(ACTIVE_COUNCIL)
|
||||
NextFinalise::exists()
|
||||
}
|
||||
|
||||
/// If `who` a candidate at the moment?
|
||||
pub fn is_a_candidate(who: &AccountId) -> bool {
|
||||
storage::exists(&who.to_keyed_vec(REGISTER_INFO_OF))
|
||||
RegisterInfoOf::exists(who)
|
||||
}
|
||||
|
||||
/// Determine the block that a vote can happen on which is no less than `n`.
|
||||
@@ -215,47 +178,6 @@ pub fn next_tally() -> Option<BlockNumber> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The accounts holding the seats that will become free on the next tally.
|
||||
pub fn next_finalise() -> Option<(BlockNumber, u32, Vec<AccountId>)> {
|
||||
storage::get(NEXT_FINALISE)
|
||||
}
|
||||
|
||||
/// The total number of votes that have happened or are in progress.
|
||||
pub fn vote_index() -> VoteIndex {
|
||||
storage::get_or_default(VOTE_COUNT)
|
||||
}
|
||||
|
||||
/// The present candidate list.
|
||||
pub fn candidates() -> Vec<AccountId> {
|
||||
storage::get_or_default(CANDIDATES)
|
||||
}
|
||||
|
||||
/// The present voter list.
|
||||
pub fn voters() -> Vec<AccountId> {
|
||||
storage::get_or_default(VOTERS)
|
||||
}
|
||||
|
||||
/// The vote index and list slot that the candidate `who` was registered or `None` if they are not
|
||||
/// currently registered.
|
||||
pub fn candidate_reg_info(who: &AccountId) -> Option<(VoteIndex, u32)> {
|
||||
storage::get(&who.to_keyed_vec(REGISTER_INFO_OF))
|
||||
}
|
||||
|
||||
/// The last cleared vote index that this voter was last active at.
|
||||
pub fn voter_last_active(voter: &AccountId) -> Option<VoteIndex> {
|
||||
storage::get(&voter.to_keyed_vec(LAST_ACTIVE_OF))
|
||||
}
|
||||
|
||||
/// The last cleared vote index that this voter was last active at.
|
||||
pub fn approvals_of(voter: &AccountId) -> Vec<bool> {
|
||||
storage::get_or_default(&voter.to_keyed_vec(APPROVALS_OF))
|
||||
}
|
||||
|
||||
/// Get the leaderboard if we;re in the presentation phase.
|
||||
pub fn leaderboard() -> Option<Vec<(Balance, AccountId)>> {
|
||||
storage::get(LEADERBOARD)
|
||||
}
|
||||
|
||||
impl_dispatch! {
|
||||
pub mod public;
|
||||
fn set_approvals(votes: Vec<bool>, index: VoteIndex) = 0;
|
||||
@@ -271,17 +193,17 @@ impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
fn set_approvals(self, votes: Vec<bool>, index: VoteIndex) {
|
||||
assert!(!presentation_active());
|
||||
assert_eq!(index, vote_index());
|
||||
if !storage::exists(&self.to_keyed_vec(LAST_ACTIVE_OF)) {
|
||||
if !LastActiveOf::exists(*self) {
|
||||
// not yet a voter - deduct bond.
|
||||
staking::internal::reserve_balance(&self, voting_bond());
|
||||
storage::put(VOTERS, &{
|
||||
let mut v: Vec<AccountId> = storage::get_or_default(VOTERS);
|
||||
Voters::put({
|
||||
let mut v = Voters::get();
|
||||
v.push(self.clone());
|
||||
v
|
||||
});
|
||||
}
|
||||
storage::put(&self.to_keyed_vec(APPROVALS_OF), &votes);
|
||||
storage::put(&self.to_keyed_vec(LAST_ACTIVE_OF), &index);
|
||||
ApprovalsOf::insert(*self, votes);
|
||||
LastActiveOf::insert(*self, index);
|
||||
}
|
||||
|
||||
/// Remove a voter. For it not to be a bond-consuming no-op, all approved candidate indices
|
||||
@@ -291,7 +213,7 @@ impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
/// May be called by anyone. Returns the voter deposit to `signed`.
|
||||
fn reap_inactive_voter(self, signed_index: u32, who: AccountId, who_index: u32, assumed_vote_index: VoteIndex) {
|
||||
assert!(!presentation_active(), "cannot reap during presentation period");
|
||||
assert!(voter_last_active(&self).is_some(), "reaper must be a voter");
|
||||
assert!(voter_last_active(*self).is_some(), "reaper must be a voter");
|
||||
let last_active = voter_last_active(&who).expect("target for inactivity cleanup must be active");
|
||||
assert!(assumed_vote_index == vote_index(), "vote index not current");
|
||||
assert!(last_active < assumed_vote_index - inactivity_grace_period(), "cannot reap during grace perid");
|
||||
@@ -326,7 +248,7 @@ impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
/// Remove a voter. All votes are cancelled and the voter deposit is returned.
|
||||
fn retract_voter(self, index: u32) {
|
||||
assert!(!presentation_active(), "cannot retract when presenting");
|
||||
assert!(storage::exists(&self.to_keyed_vec(LAST_ACTIVE_OF)), "cannot retract non-voter");
|
||||
assert!(LastActiveOf::exists(*self), "cannot retract non-voter");
|
||||
let voters = voters();
|
||||
let index = index as usize;
|
||||
assert!(index < voters.len(), "retraction index invalid");
|
||||
@@ -343,8 +265,8 @@ impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
assert!(staking::internal::deduct_unbonded(&self, candidacy_bond()), "candidate has not enough funds");
|
||||
|
||||
let slot = slot as usize;
|
||||
let count = storage::get_or_default::<u32>(CANDIDATE_COUNT) as usize;
|
||||
let candidates: Vec<AccountId> = storage::get_or_default(CANDIDATES);
|
||||
let count = CandidateCount::get() as usize;
|
||||
let candidates = Candidates::get();
|
||||
assert!(
|
||||
(slot == count && count == candidates.len()) ||
|
||||
(slot < candidates.len() && candidates[slot] == AccountId::default()),
|
||||
@@ -357,20 +279,20 @@ impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
} else {
|
||||
candidates[slot] = self.clone();
|
||||
}
|
||||
storage::put(CANDIDATES, &candidates);
|
||||
storage::put(CANDIDATE_COUNT, &(count as u32 + 1));
|
||||
storage::put(&self.to_keyed_vec(REGISTER_INFO_OF), &(vote_index(), slot));
|
||||
Candidates::put(candidates);
|
||||
CandidateCount::put(count as u32 + 1);
|
||||
RegisterInfoOf::insert(*self, (vote_index(), slot as u32));
|
||||
}
|
||||
|
||||
/// Claim that `signed` is one of the top carry_count() + current_vote().1 candidates.
|
||||
/// Only works if the block number >= current_vote().0 and < current_vote().0 + presentation_duration()
|
||||
/// Only works if the `block_number >= current_vote().0` and `< current_vote().0 + presentation_duration()``
|
||||
/// `signed` should have at least
|
||||
fn present_winner(self, candidate: AccountId, total: Balance, index: VoteIndex) {
|
||||
assert_eq!(index, vote_index(), "index not current");
|
||||
let (_, _, expiring): (BlockNumber, u32, Vec<AccountId>) = storage::get(NEXT_FINALISE)
|
||||
let (_, _, expiring) = NextFinalise::get()
|
||||
.expect("cannot present outside of presentation period");
|
||||
let stakes: Vec<Balance> = storage::get_or_default(SNAPSHOTED_STAKES);
|
||||
let voters: Vec<AccountId> = storage::get_or_default(VOTERS);
|
||||
let stakes = SnapshotedStakes::get();
|
||||
let voters = Voters::get();
|
||||
let bad_presentation_punishment = present_slash_per_voter() * voters.len() as Balance;
|
||||
assert!(staking::can_slash(&self, bad_presentation_punishment), "presenter must have sufficient slashable funds");
|
||||
|
||||
@@ -382,14 +304,14 @@ impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
}
|
||||
|
||||
let (registered_since, candidate_index): (VoteIndex, u32) =
|
||||
storage::get(&candidate.to_keyed_vec(REGISTER_INFO_OF)).expect("presented candidate must be current");
|
||||
RegisterInfoOf::get(candidate).expect("presented candidate must be current");
|
||||
let actual_total = voters.iter()
|
||||
.zip(stakes.iter())
|
||||
.filter_map(|(voter, stake)|
|
||||
match voter_last_active(voter) {
|
||||
Some(b) if b >= registered_since =>
|
||||
approvals_of(voter).get(candidate_index as usize)
|
||||
.and_then(|approved| if *approved { Some(stake) } else { None }),
|
||||
.and_then(|approved| if *approved { Some(*stake) } else { None }),
|
||||
_ => None,
|
||||
})
|
||||
.sum();
|
||||
@@ -398,7 +320,7 @@ impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
// insert into leaderboard
|
||||
leaderboard[0] = (total, candidate.clone());
|
||||
leaderboard.sort_by_key(|&(t, _)| t);
|
||||
storage::put(LEADERBOARD, &leaderboard);
|
||||
Leaderboard::put(leaderboard);
|
||||
} else {
|
||||
staking::internal::slash(&self, bad_presentation_punishment);
|
||||
}
|
||||
@@ -418,7 +340,7 @@ impl privileged::Dispatch for PrivPass {
|
||||
/// election when they expire. If more, then a new vote will be started if one is not already
|
||||
/// in progress.
|
||||
fn set_desired_seats(self, count: u32) {
|
||||
storage::put(DESIRED_SEATS, &count);
|
||||
DesiredSeats::put(count);
|
||||
}
|
||||
|
||||
/// Remove a particular member. A tally will happen instantly (if not already in a presentation
|
||||
@@ -429,19 +351,19 @@ impl privileged::Dispatch for PrivPass {
|
||||
.into_iter()
|
||||
.filter(|i| i.0 != who)
|
||||
.collect();
|
||||
storage::put(ACTIVE_COUNCIL, &new_council);
|
||||
ActiveCouncil::put(new_council);
|
||||
}
|
||||
|
||||
/// Set the presentation duration. If there is current a vote being presented for, will
|
||||
/// invoke `finalise_vote`.
|
||||
fn set_presentation_duration(self, count: BlockNumber) {
|
||||
storage::put(PRESENTATION_DURATION, &count);
|
||||
PresentationDuration::put(count);
|
||||
}
|
||||
|
||||
/// Set the presentation duration. If there is current a vote being presented for, will
|
||||
/// invoke `finalise_vote`.
|
||||
fn set_term_duration(self, count: BlockNumber) {
|
||||
storage::put(TERM_DURATION, &count);
|
||||
TermDuration::put(count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,9 +390,9 @@ pub mod internal {
|
||||
|
||||
/// Remove a voter from the system. Trusts that voters()[index] != voter.
|
||||
fn remove_voter(voter: &AccountId, index: usize, mut voters: Vec<AccountId>) {
|
||||
storage::put(VOTERS, &{ voters.swap_remove(index); voters });
|
||||
storage::kill(&voter.to_keyed_vec(APPROVALS_OF));
|
||||
storage::kill(&voter.to_keyed_vec(LAST_ACTIVE_OF));
|
||||
Voters::put({ voters.swap_remove(index); voters });
|
||||
ApprovalsOf::remove(voter);
|
||||
LastActiveOf::remove(voter);
|
||||
}
|
||||
|
||||
/// Close the voting, snapshot the staking and the number of seats that are actually up for grabs.
|
||||
@@ -478,18 +400,18 @@ fn start_tally() {
|
||||
let active_council = active_council();
|
||||
let desired_seats = desired_seats() as usize;
|
||||
let number = system::block_number();
|
||||
let expiring = active_council.iter().take_while(|i| i.1 == number).cloned().collect::<Vec<_>>();
|
||||
let expiring = active_council.iter().take_while(|i| i.1 == number).map(|i| i.0).collect::<Vec<_>>();
|
||||
if active_council.len() - expiring.len() < desired_seats {
|
||||
let empty_seats = desired_seats - (active_council.len() - expiring.len());
|
||||
storage::put(NEXT_FINALISE, &(number + presentation_duration(), empty_seats as u32, expiring));
|
||||
NextFinalise::put((number + presentation_duration(), empty_seats as u32, expiring));
|
||||
|
||||
let voters: Vec<AccountId> = storage::get_or_default(VOTERS);
|
||||
let votes: Vec<Balance> = voters.iter().map(staking::balance).collect();
|
||||
storage::put(SNAPSHOTED_STAKES, &votes);
|
||||
let voters = Voters::get();
|
||||
let votes = voters.iter().map(staking::balance).collect::<Vec<_>>();
|
||||
SnapshotedStakes::put(votes);
|
||||
|
||||
// initialise leaderboard.
|
||||
let leaderboard_size = empty_seats + carry_count() as usize;
|
||||
storage::put(LEADERBOARD, &vec![(0 as Balance, AccountId::default()); leaderboard_size]);
|
||||
Leaderboard::put(vec![(0 as Balance, AccountId::default()); leaderboard_size]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -498,10 +420,10 @@ fn start_tally() {
|
||||
/// a new vote is started.
|
||||
/// Clears all presented candidates, returning the bond of the elected ones.
|
||||
fn finalise_tally() {
|
||||
storage::kill(SNAPSHOTED_STAKES);
|
||||
let (_, coming, expiring): (BlockNumber, u32, Vec<AccountId>) = storage::take(NEXT_FINALISE)
|
||||
SnapshotedStakes::kill();
|
||||
let (_, coming, expiring): (BlockNumber, u32, Vec<AccountId>) = NextFinalise::take()
|
||||
.expect("finalise can only be called after a tally is started.");
|
||||
let leaderboard: Vec<(Balance, AccountId)> = storage::take(LEADERBOARD).unwrap_or_default();
|
||||
let leaderboard: Vec<(Balance, AccountId)> = Leaderboard::take().unwrap_or_default();
|
||||
let new_expiry = system::block_number() + term_duration();
|
||||
|
||||
// return bond to winners.
|
||||
@@ -526,10 +448,10 @@ fn finalise_tally() {
|
||||
.map(|(_, a)| (a, new_expiry)))
|
||||
.collect();
|
||||
new_council.sort_by_key(|&(_, expiry)| expiry);
|
||||
storage::put(ACTIVE_COUNCIL, &new_council);
|
||||
ActiveCouncil::put(new_council);
|
||||
|
||||
// clear all except runners-up from candidate list.
|
||||
let candidates: Vec<AccountId> = storage::get_or_default(CANDIDATES);
|
||||
let candidates = Candidates::get();
|
||||
let mut new_candidates = vec![AccountId::default(); candidates.len()]; // shrink later.
|
||||
let runners_up = leaderboard.into_iter()
|
||||
.rev()
|
||||
@@ -544,16 +466,16 @@ fn finalise_tally() {
|
||||
for (old, new) in candidates.iter().zip(new_candidates.iter()) {
|
||||
if old != new {
|
||||
// removed - kill it
|
||||
storage::kill(&old.to_keyed_vec(REGISTER_INFO_OF));
|
||||
RegisterInfoOf::remove(old);
|
||||
}
|
||||
}
|
||||
// discard any superfluous slots.
|
||||
if let Some(last_index) = new_candidates.iter().rposition(|c| *c != AccountId::default()) {
|
||||
new_candidates.truncate(last_index + 1);
|
||||
}
|
||||
storage::put(CANDIDATES, &(new_candidates));
|
||||
storage::put(CANDIDATE_COUNT, &count);
|
||||
storage::put(VOTE_COUNT, &(vote_index() + 1));
|
||||
Candidates::put(new_candidates);
|
||||
CandidateCount::put(count);
|
||||
VoteCount::put(vote_index() + 1);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -565,15 +487,15 @@ pub mod testing {
|
||||
|
||||
pub fn externalities() -> TestExternalities {
|
||||
let extras: TestExternalities = map![
|
||||
twox_128(CANDIDACY_BOND).to_vec() => vec![].and(&9u64),
|
||||
twox_128(VOTING_BOND).to_vec() => vec![].and(&3u64),
|
||||
twox_128(PRESENT_SLASH_PER_VOTER).to_vec() => vec![].and(&1u64),
|
||||
twox_128(CARRY_COUNT).to_vec() => vec![].and(&2u32),
|
||||
twox_128(PRESENTATION_DURATION).to_vec() => vec![].and(&2u64),
|
||||
twox_128(VOTING_PERIOD).to_vec() => vec![].and(&4u64),
|
||||
twox_128(TERM_DURATION).to_vec() => vec![].and(&5u64),
|
||||
twox_128(DESIRED_SEATS).to_vec() => vec![].and(&2u32),
|
||||
twox_128(INACTIVE_GRACE_PERIOD).to_vec() => vec![].and(&1u32)
|
||||
twox_128(CandidacyBond::key()).to_vec() => vec![].and(&9u64),
|
||||
twox_128(VotingBond::key()).to_vec() => vec![].and(&3u64),
|
||||
twox_128(PresentSlashPerVoter::key()).to_vec() => vec![].and(&1u64),
|
||||
twox_128(CarryCount::key()).to_vec() => vec![].and(&2u32),
|
||||
twox_128(PresentationDuration::key()).to_vec() => vec![].and(&2u64),
|
||||
twox_128(VotingPeriod::key()).to_vec() => vec![].and(&4u64),
|
||||
twox_128(TermDuration::key()).to_vec() => vec![].and(&5u64),
|
||||
twox_128(DesiredSeats::key()).to_vec() => vec![].and(&2u32),
|
||||
twox_128(InactiveGracePeriod::key()).to_vec() => vec![].and(&1u32)
|
||||
];
|
||||
democracy::testing::externalities()
|
||||
.into_iter().chain(extras.into_iter()).collect()
|
||||
@@ -621,11 +543,11 @@ mod tests {
|
||||
|
||||
assert_eq!(candidates(), Vec::<AccountId>::new());
|
||||
assert_eq!(is_a_candidate(&Alice), false);
|
||||
assert_eq!(candidate_reg_info(&Alice), None);
|
||||
assert_eq!(candidate_reg_info(*Alice), None);
|
||||
|
||||
assert_eq!(voters(), Vec::<AccountId>::new());
|
||||
assert_eq!(voter_last_active(&Alice), None);
|
||||
assert_eq!(approvals_of(&Alice), vec![]);
|
||||
assert_eq!(voter_last_active(*Alice), None);
|
||||
assert_eq!(approvals_of(*Alice), vec![]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -634,22 +556,22 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
assert_eq!(candidates(), Vec::<AccountId>::new());
|
||||
assert_eq!(candidate_reg_info(&Alice), None);
|
||||
assert_eq!(candidate_reg_info(&Bob), None);
|
||||
assert_eq!(candidate_reg_info(*Alice), None);
|
||||
assert_eq!(candidate_reg_info(*Bob), None);
|
||||
assert_eq!(is_a_candidate(&Alice), false);
|
||||
assert_eq!(is_a_candidate(&Bob), false);
|
||||
|
||||
PublicPass::test(&Alice).submit_candidacy(0);
|
||||
assert_eq!(candidates(), vec![Alice.to_raw_public()]);
|
||||
assert_eq!(candidate_reg_info(&Alice), Some((0 as VoteIndex, 0u32)));
|
||||
assert_eq!(candidate_reg_info(&Bob), None);
|
||||
assert_eq!(candidate_reg_info(*Alice), Some((0 as VoteIndex, 0u32)));
|
||||
assert_eq!(candidate_reg_info(*Bob), None);
|
||||
assert_eq!(is_a_candidate(&Alice), true);
|
||||
assert_eq!(is_a_candidate(&Bob), false);
|
||||
|
||||
PublicPass::test(&Bob).submit_candidacy(1);
|
||||
assert_eq!(candidates(), vec![Alice.to_raw_public(), Bob.into()]);
|
||||
assert_eq!(candidate_reg_info(&Alice), Some((0 as VoteIndex, 0u32)));
|
||||
assert_eq!(candidate_reg_info(&Bob), Some((0 as VoteIndex, 1u32)));
|
||||
assert_eq!(candidate_reg_info(*Alice), Some((0 as VoteIndex, 0u32)));
|
||||
assert_eq!(candidate_reg_info(*Bob), Some((0 as VoteIndex, 1u32)));
|
||||
assert_eq!(is_a_candidate(&Alice), true);
|
||||
assert_eq!(is_a_candidate(&Bob), true);
|
||||
});
|
||||
@@ -657,9 +579,9 @@ mod tests {
|
||||
|
||||
fn new_test_ext_with_candidate_holes() -> TestExternalities {
|
||||
let mut t = new_test_ext();
|
||||
t.insert(twox_128(CANDIDATES).to_vec(), vec![].and(&vec![AccountId::default(), AccountId::default(), Alice.to_raw_public()]));
|
||||
t.insert(twox_128(CANDIDATE_COUNT).to_vec(), vec![].and(&1u32));
|
||||
t.insert(twox_128(&Alice.to_raw_public().to_keyed_vec(REGISTER_INFO_OF)).to_vec(), vec![].and(&(0 as VoteIndex, 2u32)));
|
||||
t.insert(twox_128(Candidates::key()).to_vec(), vec![].and(&vec![AccountId::default(), AccountId::default(), Alice.to_raw_public()]));
|
||||
t.insert(twox_128(CandidateCount::key()).to_vec(), vec![].and(&1u32));
|
||||
t.insert(twox_128(&RegisterInfoOf::key_for(*Alice)).to_vec(), vec![].and(&(0 as VoteIndex, 2u32)));
|
||||
t
|
||||
}
|
||||
|
||||
@@ -758,8 +680,8 @@ mod tests {
|
||||
PublicPass::test(&Alice).set_approvals(vec![true], 0);
|
||||
PublicPass::test(&Dave).set_approvals(vec![true], 0);
|
||||
|
||||
assert_eq!(approvals_of(&Alice), vec![true]);
|
||||
assert_eq!(approvals_of(&Dave), vec![true]);
|
||||
assert_eq!(approvals_of(*Alice), vec![true]);
|
||||
assert_eq!(approvals_of(*Dave), vec![true]);
|
||||
assert_eq!(voters(), vec![Alice.to_raw_public(), Dave.into()]);
|
||||
|
||||
PublicPass::test(&Bob).submit_candidacy(1);
|
||||
@@ -768,10 +690,10 @@ mod tests {
|
||||
PublicPass::test(&Bob).set_approvals(vec![false, true, true], 0);
|
||||
PublicPass::test(&Charlie).set_approvals(vec![false, true, true], 0);
|
||||
|
||||
assert_eq!(approvals_of(&Alice), vec![true]);
|
||||
assert_eq!(approvals_of(&Dave), vec![true]);
|
||||
assert_eq!(approvals_of(&Bob), vec![false, true, true]);
|
||||
assert_eq!(approvals_of(&Charlie), vec![false, true, true]);
|
||||
assert_eq!(approvals_of(*Alice), vec![true]);
|
||||
assert_eq!(approvals_of(*Dave), vec![true]);
|
||||
assert_eq!(approvals_of(*Bob), vec![false, true, true]);
|
||||
assert_eq!(approvals_of(*Charlie), vec![false, true, true]);
|
||||
|
||||
assert_eq!(voters(), vec![Alice.to_raw_public(), Dave.into(), Bob.into(), Charlie.into()]);
|
||||
});
|
||||
@@ -785,13 +707,13 @@ mod tests {
|
||||
PublicPass::test(&Eve).submit_candidacy(0);
|
||||
PublicPass::test(&Dave).set_approvals(vec![true], 0);
|
||||
|
||||
assert_eq!(approvals_of(&Dave), vec![true]);
|
||||
assert_eq!(approvals_of(*Dave), vec![true]);
|
||||
|
||||
PublicPass::test(&Bob).submit_candidacy(1);
|
||||
PublicPass::test(&Charlie).submit_candidacy(2);
|
||||
PublicPass::test(&Dave).set_approvals(vec![true, false, true], 0);
|
||||
|
||||
assert_eq!(approvals_of(&Dave), vec![true, false, true]);
|
||||
assert_eq!(approvals_of(*Dave), vec![true, false, true]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -810,34 +732,34 @@ mod tests {
|
||||
PublicPass::test(&Dave).set_approvals(vec![true, false, true], 0);
|
||||
|
||||
assert_eq!(voters(), vec![Alice.to_raw_public(), Bob.into(), Charlie.into(), Dave.into()]);
|
||||
assert_eq!(approvals_of(&Alice), vec![true]);
|
||||
assert_eq!(approvals_of(&Bob), vec![false, true, true]);
|
||||
assert_eq!(approvals_of(&Charlie), vec![false, true, true]);
|
||||
assert_eq!(approvals_of(&Dave), vec![true, false, true]);
|
||||
assert_eq!(approvals_of(*Alice), vec![true]);
|
||||
assert_eq!(approvals_of(*Bob), vec![false, true, true]);
|
||||
assert_eq!(approvals_of(*Charlie), vec![false, true, true]);
|
||||
assert_eq!(approvals_of(*Dave), vec![true, false, true]);
|
||||
|
||||
PublicPass::test(&Alice).retract_voter(0);
|
||||
|
||||
assert_eq!(voters(), vec![Dave.to_raw_public(), Bob.into(), Charlie.into()]);
|
||||
assert_eq!(approvals_of(&Alice), Vec::<bool>::new());
|
||||
assert_eq!(approvals_of(&Bob), vec![false, true, true]);
|
||||
assert_eq!(approvals_of(&Charlie), vec![false, true, true]);
|
||||
assert_eq!(approvals_of(&Dave), vec![true, false, true]);
|
||||
assert_eq!(approvals_of(*Alice), Vec::<bool>::new());
|
||||
assert_eq!(approvals_of(*Bob), vec![false, true, true]);
|
||||
assert_eq!(approvals_of(*Charlie), vec![false, true, true]);
|
||||
assert_eq!(approvals_of(*Dave), vec![true, false, true]);
|
||||
|
||||
PublicPass::test(&Bob).retract_voter(1);
|
||||
|
||||
assert_eq!(voters(), vec![Dave.to_raw_public(), Charlie.into()]);
|
||||
assert_eq!(approvals_of(&Alice), Vec::<bool>::new());
|
||||
assert_eq!(approvals_of(&Bob), Vec::<bool>::new());
|
||||
assert_eq!(approvals_of(&Charlie), vec![false, true, true]);
|
||||
assert_eq!(approvals_of(&Dave), vec![true, false, true]);
|
||||
assert_eq!(approvals_of(*Alice), Vec::<bool>::new());
|
||||
assert_eq!(approvals_of(*Bob), Vec::<bool>::new());
|
||||
assert_eq!(approvals_of(*Charlie), vec![false, true, true]);
|
||||
assert_eq!(approvals_of(*Dave), vec![true, false, true]);
|
||||
|
||||
PublicPass::test(&Charlie).retract_voter(1);
|
||||
|
||||
assert_eq!(voters(), vec![Dave.to_raw_public()]);
|
||||
assert_eq!(approvals_of(&Alice), Vec::<bool>::new());
|
||||
assert_eq!(approvals_of(&Bob), Vec::<bool>::new());
|
||||
assert_eq!(approvals_of(&Charlie), Vec::<bool>::new());
|
||||
assert_eq!(approvals_of(&Dave), vec![true, false, true]);
|
||||
assert_eq!(approvals_of(*Alice), Vec::<bool>::new());
|
||||
assert_eq!(approvals_of(*Bob), Vec::<bool>::new());
|
||||
assert_eq!(approvals_of(*Charlie), Vec::<bool>::new());
|
||||
assert_eq!(approvals_of(*Dave), vec![true, false, true]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -901,8 +823,8 @@ mod tests {
|
||||
assert!(!is_a_candidate(&Bob));
|
||||
assert!(!is_a_candidate(&Eve));
|
||||
assert_eq!(vote_index(), 1);
|
||||
assert_eq!(voter_last_active(&Bob), Some(0));
|
||||
assert_eq!(voter_last_active(&Eve), Some(0));
|
||||
assert_eq!(voter_last_active(*Bob), Some(0));
|
||||
assert_eq!(voter_last_active(*Eve), Some(0));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -957,7 +879,7 @@ mod tests {
|
||||
);
|
||||
|
||||
assert_eq!(voters(), vec![Eve.to_raw_public()]);
|
||||
assert_eq!(approvals_of(&Bob).len(), 0);
|
||||
assert_eq!(approvals_of(*Bob).len(), 0);
|
||||
assert_eq!(staking::balance(&Bob), 17);
|
||||
assert_eq!(staking::balance(&Eve), 53);
|
||||
});
|
||||
@@ -1017,7 +939,7 @@ mod tests {
|
||||
);
|
||||
|
||||
assert_eq!(voters(), vec![Eve.to_raw_public()]);
|
||||
assert_eq!(approvals_of(&Bob).len(), 0);
|
||||
assert_eq!(approvals_of(*Bob).len(), 0);
|
||||
assert_eq!(staking::balance(&Bob), 17);
|
||||
assert_eq!(staking::balance(&Eve), 53);
|
||||
});
|
||||
@@ -1120,7 +1042,7 @@ mod tests {
|
||||
);
|
||||
|
||||
assert_eq!(voters(), vec![Bob.to_raw_public(), Charlie.into(), Eve.into()]);
|
||||
assert_eq!(approvals_of(&Dave).len(), 0);
|
||||
assert_eq!(approvals_of(*Dave).len(), 0);
|
||||
assert_eq!(staking::balance(&Dave), 37);
|
||||
});
|
||||
}
|
||||
@@ -1327,13 +1249,13 @@ mod tests {
|
||||
assert!(is_a_candidate(&Charlie));
|
||||
assert!(is_a_candidate(&Dave));
|
||||
assert_eq!(vote_index(), 1);
|
||||
assert_eq!(voter_last_active(&Bob), Some(0));
|
||||
assert_eq!(voter_last_active(&Charlie), Some(0));
|
||||
assert_eq!(voter_last_active(&Dave), Some(0));
|
||||
assert_eq!(voter_last_active(&Eve), Some(0));
|
||||
assert_eq!(voter_last_active(&Ferdie), Some(0));
|
||||
assert_eq!(candidate_reg_info(&Charlie), Some((0, 2)));
|
||||
assert_eq!(candidate_reg_info(&Dave), Some((0, 3)));
|
||||
assert_eq!(voter_last_active(*Bob), Some(0));
|
||||
assert_eq!(voter_last_active(*Charlie), Some(0));
|
||||
assert_eq!(voter_last_active(*Dave), Some(0));
|
||||
assert_eq!(voter_last_active(*Eve), Some(0));
|
||||
assert_eq!(voter_last_active(*Ferdie), Some(0));
|
||||
assert_eq!(candidate_reg_info(*Charlie), Some((0, 2)));
|
||||
assert_eq!(candidate_reg_info(*Dave), Some((0, 3)));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1379,13 +1301,13 @@ mod tests {
|
||||
assert!(!is_a_candidate(&Eve));
|
||||
assert!(is_a_candidate(&Dave));
|
||||
assert_eq!(vote_index(), 2);
|
||||
assert_eq!(voter_last_active(&Bob), Some(0));
|
||||
assert_eq!(voter_last_active(&Charlie), Some(0));
|
||||
assert_eq!(voter_last_active(&Dave), Some(0));
|
||||
assert_eq!(voter_last_active(&Eve), Some(0));
|
||||
assert_eq!(voter_last_active(&Ferdie), Some(1));
|
||||
assert_eq!(voter_last_active(*Bob), Some(0));
|
||||
assert_eq!(voter_last_active(*Charlie), Some(0));
|
||||
assert_eq!(voter_last_active(*Dave), Some(0));
|
||||
assert_eq!(voter_last_active(*Eve), Some(0));
|
||||
assert_eq!(voter_last_active(*Ferdie), Some(1));
|
||||
|
||||
assert_eq!(candidate_reg_info(&Dave), Some((0, 3)));
|
||||
assert_eq!(candidate_reg_info(*Dave), Some((0, 3)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,10 @@
|
||||
//! Council voting system.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::borrow::Borrow;
|
||||
use codec::{KeyedVec, Slicable, Input, NonTrivialSlicable};
|
||||
use runtime_support::Hashable;
|
||||
use runtime_support::storage;
|
||||
use runtime_support::{StorageValue, StorageMap};
|
||||
use demo_primitives::{AccountId, Hash, BlockNumber};
|
||||
use runtime::{system, democracy, council};
|
||||
use runtime::staking::{PublicPass, Balance};
|
||||
@@ -28,43 +29,28 @@ use dispatch::PrivCall as Proposal;
|
||||
|
||||
type ProposalHash = [u8; 32];
|
||||
|
||||
pub const COOLOFF_PERIOD: &[u8] = b"cov:cooloff"; // BlockNumber
|
||||
pub const VOTING_PERIOD: &[u8] = b"cov:period"; // BlockNumber
|
||||
|
||||
pub const PROPOSALS: &[u8] = b"cov:prs"; // Vec<(expiry: BlockNumber, ProposalHash)> ordered by expiry.
|
||||
pub const PROPOSAL_OF: &[u8] = b"cov:pro"; // ProposalHash -> Proposal
|
||||
pub const PROPOSAL_VOTERS: &[u8] = b"cov:voters:"; // ProposalHash -> Vec<AccountId>
|
||||
pub const COUNCIL_VOTE_OF: &[u8] = b"cov:vote:"; // (ProposalHash, AccountId) -> bool
|
||||
pub const VETOED_PROPOSAL: &[u8] = b"cov:veto:"; // ProposalHash -> (BlockNumber, sorted_vetoers: Vec<AccountId>)
|
||||
|
||||
pub fn cooloff_period() -> BlockNumber {
|
||||
storage::get(COOLOFF_PERIOD).expect("all parameters must be defined")
|
||||
storage_items! {
|
||||
pub CooloffPeriod get(cooloff_period): b"cov:cooloff" => required BlockNumber;
|
||||
pub VotingPeriod get(voting_period): b"cov:period" => required BlockNumber;
|
||||
pub Proposals get(proposals): b"cov:prs" => default Vec<(BlockNumber, ProposalHash)>; // ordered by expiry.
|
||||
pub ProposalOf get(proposal_of): b"cov:pro" => map [ ProposalHash => Proposal ];
|
||||
pub ProposalVoters get(proposal_voters): b"cov:voters:" => default map [ ProposalHash => Vec<AccountId> ];
|
||||
pub CouncilVoteOf get(vote_of): b"cov:vote:" => map [ (ProposalHash, AccountId) => bool ];
|
||||
pub VetoedProposal get(veto_of): b"cov:veto:" => map [ ProposalHash => (BlockNumber, Vec<AccountId>) ];
|
||||
}
|
||||
|
||||
pub fn voting_period() -> BlockNumber {
|
||||
storage::get(VOTING_PERIOD).expect("all parameters must be defined")
|
||||
}
|
||||
|
||||
pub fn proposals() -> Vec<(BlockNumber, ProposalHash)> {
|
||||
storage::get_or_default(PROPOSALS)
|
||||
}
|
||||
|
||||
pub fn is_vetoed(proposal: &ProposalHash) -> bool {
|
||||
storage::get(&proposal.to_keyed_vec(VETOED_PROPOSAL))
|
||||
pub fn is_vetoed<B: Borrow<ProposalHash>>(proposal: B) -> bool {
|
||||
VetoedProposal::get(proposal.borrow())
|
||||
.map(|(expiry, _): (BlockNumber, Vec<AccountId>)| system::block_number() < expiry)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn veto_of(proposal: &ProposalHash) -> Option<(BlockNumber, Vec<AccountId>)> {
|
||||
storage::get(&proposal.to_keyed_vec(VETOED_PROPOSAL))
|
||||
}
|
||||
|
||||
fn set_veto_of(proposal: &ProposalHash, expiry: BlockNumber, vetoers: Vec<AccountId>) {
|
||||
storage::put(&proposal.to_keyed_vec(VETOED_PROPOSAL), &(expiry, vetoers))
|
||||
VetoedProposal::insert(proposal, (expiry, vetoers));
|
||||
}
|
||||
|
||||
fn kill_veto_of(proposal: &ProposalHash) {
|
||||
storage::kill(&proposal.to_keyed_vec(VETOED_PROPOSAL))
|
||||
VetoedProposal::remove(proposal);
|
||||
}
|
||||
|
||||
pub fn will_still_be_councillor_at(who: &AccountId, n: BlockNumber) -> bool {
|
||||
@@ -79,20 +65,12 @@ pub fn is_councillor(who: &AccountId) -> bool {
|
||||
.any(|&(ref a, _)| a == who)
|
||||
}
|
||||
|
||||
pub fn vote_of(who: &AccountId, proposal: &ProposalHash) -> Option<bool> {
|
||||
storage::get(&(*proposal, *who).to_keyed_vec(COUNCIL_VOTE_OF))
|
||||
}
|
||||
|
||||
pub fn proposal_voters(proposal: &ProposalHash) -> Vec<AccountId> {
|
||||
storage::get_or_default(&proposal.to_keyed_vec(PROPOSAL_VOTERS))
|
||||
}
|
||||
|
||||
pub fn tally(proposal_hash: &ProposalHash) -> (u32, u32, u32) {
|
||||
generic_tally(proposal_hash, |w: &AccountId, p: &ProposalHash| storage::get(&(*p, *w).to_keyed_vec(COUNCIL_VOTE_OF)))
|
||||
generic_tally(proposal_hash, |w: &AccountId, p: &ProposalHash| CouncilVoteOf::get((*p, *w)))
|
||||
}
|
||||
|
||||
fn take_tally(proposal_hash: &ProposalHash) -> (u32, u32, u32) {
|
||||
generic_tally(proposal_hash, |w: &AccountId, p: &ProposalHash| storage::get(&(*p, *w).to_keyed_vec(COUNCIL_VOTE_OF)))
|
||||
generic_tally(proposal_hash, |w: &AccountId, p: &ProposalHash| CouncilVoteOf::take((*p, *w)))
|
||||
}
|
||||
|
||||
fn generic_tally<F: Fn(&AccountId, &ProposalHash) -> Option<bool>>(proposal_hash: &ProposalHash, vote_of: F) -> (u32, u32, u32) {
|
||||
@@ -105,7 +83,7 @@ fn generic_tally<F: Fn(&AccountId, &ProposalHash) -> Option<bool>>(proposal_hash
|
||||
}
|
||||
|
||||
fn set_proposals(p: &Vec<(BlockNumber, ProposalHash)>) {
|
||||
storage::put(PROPOSALS, p)
|
||||
Proposals::put(p);
|
||||
}
|
||||
|
||||
fn take_proposal_if_expiring_at(n: BlockNumber) -> Option<(Proposal, ProposalHash)> {
|
||||
@@ -114,7 +92,7 @@ fn take_proposal_if_expiring_at(n: BlockNumber) -> Option<(Proposal, ProposalHas
|
||||
Some(&(expiry, hash)) if expiry == n => {
|
||||
// yes this is horrible, but fixing it will need substantial work in storage.
|
||||
set_proposals(&proposals[1..].to_vec());
|
||||
let proposal = storage::take(&hash.to_keyed_vec(PROPOSAL_OF)).expect("all queued proposal hashes must have associated proposals");
|
||||
let proposal = ProposalOf::take(hash).expect("all queued proposal hashes must have associated proposals");
|
||||
Some((proposal, hash))
|
||||
}
|
||||
_ => None,
|
||||
@@ -142,23 +120,23 @@ impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
proposals.sort_by_key(|&(expiry, _)| expiry);
|
||||
set_proposals(&proposals);
|
||||
|
||||
storage::put(&proposal_hash.to_keyed_vec(PROPOSAL_OF), &proposal);
|
||||
storage::put(&proposal_hash.to_keyed_vec(PROPOSAL_VOTERS), &vec![*self]);
|
||||
storage::put(&(proposal_hash, *self).to_keyed_vec(COUNCIL_VOTE_OF), &true);
|
||||
ProposalOf::insert(proposal_hash, *proposal);
|
||||
ProposalVoters::insert(proposal_hash, vec![*self]);
|
||||
CouncilVoteOf::insert((proposal_hash, *self), true);
|
||||
}
|
||||
|
||||
fn vote(self, proposal: ProposalHash, approve: bool) {
|
||||
if vote_of(&self, &proposal).is_none() {
|
||||
if vote_of((*self, proposal)).is_none() {
|
||||
let mut voters = proposal_voters(&proposal);
|
||||
voters.push(*self);
|
||||
storage::put(&proposal.to_keyed_vec(PROPOSAL_VOTERS), &voters);
|
||||
ProposalVoters::insert(proposal, voters);
|
||||
}
|
||||
storage::put(&(proposal, *self).to_keyed_vec(COUNCIL_VOTE_OF), &approve);
|
||||
CouncilVoteOf::insert((proposal, *self), approve);
|
||||
}
|
||||
|
||||
fn veto(self, proposal_hash: ProposalHash) {
|
||||
assert!(is_councillor(&self), "only councillors may veto council proposals");
|
||||
assert!(storage::exists(&proposal_hash.to_keyed_vec(PROPOSAL_VOTERS)), "proposal must exist to be vetoed");
|
||||
assert!(ProposalVoters::exists(&proposal_hash), "proposal must exist to be vetoed");
|
||||
|
||||
let mut existing_vetoers = veto_of(&proposal_hash)
|
||||
.map(|pair| pair.1)
|
||||
@@ -169,10 +147,10 @@ impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
set_veto_of(&proposal_hash, system::block_number() + cooloff_period(), existing_vetoers);
|
||||
|
||||
set_proposals(&proposals().into_iter().filter(|&(_, h)| h != proposal_hash).collect::<Vec<_>>());
|
||||
storage::kill(&proposal_hash.to_keyed_vec(PROPOSAL_VOTERS));
|
||||
storage::kill(&proposal_hash.to_keyed_vec(PROPOSAL_OF));
|
||||
ProposalVoters::remove(proposal_hash);
|
||||
ProposalOf::remove(proposal_hash);
|
||||
for (c, _) in council::active_council() {
|
||||
storage::kill(&(proposal_hash, c).to_keyed_vec(COUNCIL_VOTE_OF));
|
||||
CouncilVoteOf::remove((proposal_hash, c));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,11 +163,11 @@ impl_dispatch! {
|
||||
|
||||
impl privileged::Dispatch for PrivPass {
|
||||
fn set_cooloff_period(self, blocks: BlockNumber) {
|
||||
storage::put(COOLOFF_PERIOD, &blocks);
|
||||
CooloffPeriod::put(blocks);
|
||||
}
|
||||
|
||||
fn set_voting_period(self, blocks: BlockNumber) {
|
||||
storage::put(VOTING_PERIOD, &blocks);
|
||||
VotingPeriod::put(blocks);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,14 +208,14 @@ pub mod testing {
|
||||
pub fn externalities() -> TestExternalities {
|
||||
let expiry: BlockNumber = 10;
|
||||
let extras: TestExternalities = map![
|
||||
twox_128(council::ACTIVE_COUNCIL).to_vec() => vec![].and(&vec![
|
||||
twox_128(council::ActiveCouncil::key()).to_vec() => vec![].and(&vec![
|
||||
(Alice.to_raw_public(), expiry),
|
||||
(Bob.into(), expiry),
|
||||
(Charlie.into(), expiry)
|
||||
]),
|
||||
twox_128(COOLOFF_PERIOD).to_vec() => vec![].and(&2u64),
|
||||
twox_128(VOTING_PERIOD).to_vec() => vec![].and(&1u64),
|
||||
twox_128(democracy::VOTING_PERIOD).to_vec() => vec![].and(&3u64)
|
||||
twox_128(CooloffPeriod::key()).to_vec() => vec![].and(&2u64),
|
||||
twox_128(VotingPeriod::key()).to_vec() => vec![].and(&1u64),
|
||||
twox_128(democracy::VotingPeriod::key()).to_vec() => vec![].and(&3u64)
|
||||
];
|
||||
council::testing::externalities()
|
||||
.into_iter().chain(extras.into_iter()).collect()
|
||||
@@ -274,9 +252,9 @@ mod tests {
|
||||
assert_eq!(is_councillor(&Alice), true);
|
||||
assert_eq!(is_councillor(&Dave), false);
|
||||
assert_eq!(proposals(), Vec::<(BlockNumber, ProposalHash)>::new());
|
||||
assert_eq!(proposal_voters(&ProposalHash::default()), Vec::<AccountId>::new());
|
||||
assert_eq!(proposal_voters(ProposalHash::default()), Vec::<AccountId>::new());
|
||||
assert_eq!(is_vetoed(&ProposalHash::default()), false);
|
||||
assert_eq!(vote_of(&Alice, &ProposalHash::default()), None);
|
||||
assert_eq!(vote_of((*Alice, ProposalHash::default())), None);
|
||||
assert_eq!(tally(&ProposalHash::default()), (0, 0, 3));
|
||||
});
|
||||
}
|
||||
@@ -447,7 +425,7 @@ mod tests {
|
||||
PublicPass::new(&Alice).propose(Box::new(proposal.clone()));
|
||||
assert_eq!(proposals().len(), 1);
|
||||
assert_eq!(proposal_voters(&hash), vec![Alice.to_raw_public()]);
|
||||
assert_eq!(vote_of(&Alice, &hash), Some(true));
|
||||
assert_eq!(vote_of((hash, *Alice)), Some(true));
|
||||
assert_eq!(tally(&hash), (1, 0, 2));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
use rstd::prelude::*;
|
||||
use integer_sqrt::IntegerSquareRoot;
|
||||
use codec::{KeyedVec, Slicable, Input, NonTrivialSlicable};
|
||||
use runtime_support::storage;
|
||||
use runtime_support::{StorageValue, StorageMap};
|
||||
use demo_primitives::{AccountId, Hash, BlockNumber};
|
||||
use dispatch::PrivCall as Proposal;
|
||||
use runtime::{staking, system, session};
|
||||
@@ -95,48 +95,36 @@ impl Approved for VoteThreshold {
|
||||
}
|
||||
}
|
||||
|
||||
storage_items! {
|
||||
// The number of (public) proposals that have been made so far.
|
||||
pub PublicPropCount get(public_prop_count): b"dem:ppc" => default PropIndex;
|
||||
// The public proposals. Unsorted.
|
||||
pub PublicProps get(public_props): b"dem:pub" => default Vec<(PropIndex, Proposal, AccountId)>;
|
||||
// Those who have locked a deposit.
|
||||
pub DepositOf get(deposit_lockers): b"dem:dep:" => map [ PropIndex => (Balance, Vec<AccountId>) ];
|
||||
// How often (in blocks) new public referenda are launched.
|
||||
pub LaunchPeriod get(launch_period): b"dem:lau" => required BlockNumber;
|
||||
// The minimum amount to be used as a deposit for a public referendum proposal.
|
||||
pub MinimumDeposit get(minimum_deposit): b"dem:min" => required Balance;
|
||||
|
||||
// How often (in blocks) to check for new votes.
|
||||
pub VotingPeriod get(voting_period): b"dem:per" => required BlockNumber;
|
||||
|
||||
// The next free referendum index, aka the number of referendums started so far.
|
||||
pub ReferendumCount get(next_free_ref_index): b"dem:rco" => default ReferendumIndex;
|
||||
// The next referendum index that should be tallied.
|
||||
pub NextTally get(next_tally): b"dem:nxt" => default ReferendumIndex;
|
||||
// Information concerning any given referendum.
|
||||
pub ReferendumInfoOf get(referendum_info): b"dem:pro:" => map [ ReferendumIndex => (BlockNumber, Proposal, VoteThreshold) ];
|
||||
|
||||
// Get the voters for the current proposal.
|
||||
pub VotersFor get(voters_for): b"dem:vtr:" => default map [ ReferendumIndex => Vec<AccountId> ];
|
||||
|
||||
// Get the vote, if Some, of `who`.
|
||||
pub VoteOf get(vote_of): b"dem:vot:" => map [ (ReferendumIndex, AccountId) => bool ];
|
||||
}
|
||||
|
||||
// public proposals
|
||||
pub const PUBLIC_PROP_COUNT: &[u8] = b"dem:ppc"; // PropIndex
|
||||
pub const PUBLIC_PROPS: &[u8] = b"dem:pub"; // Vec<(PropIndex, Proposal)>
|
||||
pub const DEPOSIT_OF: &[u8] = b"dem:dep:"; // PropIndex -> (Balance, Vec<AccountId>)
|
||||
pub const LAUNCH_PERIOD: &[u8] = b"dem:lau"; // BlockNumber
|
||||
pub const MINIMUM_DEPOSIT: &[u8] = b"dem:min"; // Balance
|
||||
|
||||
// referenda
|
||||
pub const VOTING_PERIOD: &[u8] = b"dem:per"; // BlockNumber
|
||||
pub const REFERENDUM_COUNT: &[u8] = b"dem:rco"; // ReferendumIndex
|
||||
pub const NEXT_TALLY: &[u8] = b"dem:nxt"; // ReferendumIndex
|
||||
pub const REFERENDUM_INFO_OF: &[u8] = b"dem:pro:"; // ReferendumIndex -> (BlockNumber, Proposal, VoteThreshold)
|
||||
pub const VOTERS_FOR: &[u8] = b"dem:vtr:"; // ReferendumIndex -> Vec<AccountId>
|
||||
pub const VOTE_OF: &[u8] = b"dem:vot:"; // (ReferendumIndex, AccountId) -> bool
|
||||
|
||||
/// The minimum amount to be used as a deposit for a public referendum proposal.
|
||||
pub fn minimum_deposit() -> Balance {
|
||||
storage::get(MINIMUM_DEPOSIT)
|
||||
.expect("all core parameters of council module must be in place")
|
||||
}
|
||||
|
||||
/// How often (in blocks) to check for new votes.
|
||||
pub fn voting_period() -> BlockNumber {
|
||||
storage::get(VOTING_PERIOD)
|
||||
.expect("all core parameters of council module must be in place")
|
||||
}
|
||||
|
||||
/// How often (in blocks) new public referenda are launched.
|
||||
pub fn launch_period() -> BlockNumber {
|
||||
storage::get(LAUNCH_PERIOD)
|
||||
.expect("all core parameters of council module must be in place")
|
||||
}
|
||||
|
||||
/// The public proposals. Unsorted.
|
||||
pub fn public_props() -> Vec<(PropIndex, Proposal, AccountId)> {
|
||||
storage::get_or_default(PUBLIC_PROPS)
|
||||
}
|
||||
|
||||
/// Those who have locked a deposit.
|
||||
pub fn deposit_lockers(proposal: PropIndex) -> Option<(Balance, Vec<AccountId>)> {
|
||||
storage::get(&proposal.to_keyed_vec(DEPOSIT_OF))
|
||||
}
|
||||
|
||||
/// Get the amount locked in support of `proposal`; false if proposal isn't a valid proposal
|
||||
/// index.
|
||||
@@ -146,28 +134,13 @@ pub fn locked_for(proposal: PropIndex) -> Option<Balance> {
|
||||
|
||||
/// Return true if `ref_index` is an on-going referendum.
|
||||
pub fn is_active_referendum(ref_index: ReferendumIndex) -> bool {
|
||||
storage::exists(&ref_index.to_keyed_vec(REFERENDUM_INFO_OF))
|
||||
}
|
||||
|
||||
/// Get the voters for the current proposal.
|
||||
pub fn voters_for(ref_index: ReferendumIndex) -> Vec<AccountId> {
|
||||
storage::get_or_default(&ref_index.to_keyed_vec(VOTERS_FOR))
|
||||
}
|
||||
|
||||
/// Get the vote, if Some, of `who`.
|
||||
pub fn vote_of(who: &AccountId, ref_index: ReferendumIndex) -> Option<bool> {
|
||||
storage::get(&(*who, ref_index).to_keyed_vec(VOTE_OF))
|
||||
}
|
||||
|
||||
/// Get the info concerning the next referendum.
|
||||
pub fn referendum_info(ref_index: ReferendumIndex) -> Option<(BlockNumber, Proposal, VoteThreshold)> {
|
||||
storage::get(&ref_index.to_keyed_vec(REFERENDUM_INFO_OF))
|
||||
ReferendumInfoOf::exists(ref_index)
|
||||
}
|
||||
|
||||
/// Get all referendums currently active.
|
||||
pub fn active_referendums() -> Vec<(ReferendumIndex, BlockNumber, Proposal, VoteThreshold)> {
|
||||
let next: ReferendumIndex = storage::get_or_default(NEXT_TALLY);
|
||||
let last: ReferendumIndex = storage::get_or_default(REFERENDUM_COUNT);
|
||||
let next = NextTally::get();
|
||||
let last = ReferendumCount::get();
|
||||
(next..last).into_iter()
|
||||
.filter_map(|i| referendum_info(i).map(|(n, p, t)| (i, n, p, t)))
|
||||
.collect()
|
||||
@@ -175,8 +148,8 @@ pub fn active_referendums() -> Vec<(ReferendumIndex, BlockNumber, Proposal, Vote
|
||||
|
||||
/// Get all referendums ready for tally at block `n`.
|
||||
pub fn maturing_referendums_at(n: BlockNumber) -> Vec<(ReferendumIndex, BlockNumber, Proposal, VoteThreshold)> {
|
||||
let next: ReferendumIndex = storage::get_or_default(NEXT_TALLY);
|
||||
let last: ReferendumIndex = storage::get_or_default(REFERENDUM_COUNT);
|
||||
let next = NextTally::get();
|
||||
let last = ReferendumCount::get();
|
||||
(next..last).into_iter()
|
||||
.filter_map(|i| referendum_info(i).map(|(n, p, t)| (i, n, p, t)))
|
||||
.take_while(|&(_, block_number, _, _)| block_number == n)
|
||||
@@ -186,16 +159,11 @@ pub fn maturing_referendums_at(n: BlockNumber) -> Vec<(ReferendumIndex, BlockNum
|
||||
/// Get the voters for the current proposal.
|
||||
pub fn tally(ref_index: ReferendumIndex) -> (staking::Balance, staking::Balance) {
|
||||
voters_for(ref_index).iter()
|
||||
.map(|a| (staking::balance(a), vote_of(a, ref_index).expect("all items come from `voters`; for an item to be in `voters` there must be a vote registered; qed")))
|
||||
.map(|a| (staking::balance(a), vote_of((ref_index, *a)).expect("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, 0) } else { (0, bal) })
|
||||
.fold((0, 0), |(a, b), (c, d)| (a + c, b + d))
|
||||
}
|
||||
|
||||
/// Get the next free referendum index, aka the number of referendums started so far.
|
||||
pub fn next_free_ref_index() -> ReferendumIndex {
|
||||
storage::get_or_default(REFERENDUM_COUNT)
|
||||
}
|
||||
|
||||
impl_dispatch! {
|
||||
pub mod public;
|
||||
fn propose(proposal: Box<Proposal>, value: Balance) = 0;
|
||||
@@ -209,24 +177,22 @@ impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
assert!(value >= minimum_deposit());
|
||||
assert!(staking::internal::deduct_unbonded(&self, value));
|
||||
|
||||
let index: PropIndex = storage::get_or_default(PUBLIC_PROP_COUNT);
|
||||
storage::put(PUBLIC_PROP_COUNT, &(index + 1));
|
||||
storage::put(&index.to_keyed_vec(DEPOSIT_OF), &(value, vec![*self]));
|
||||
let index = PublicPropCount::get();
|
||||
PublicPropCount::put(index + 1);
|
||||
DepositOf::insert(index, (value, vec![*self]));
|
||||
|
||||
let mut props = public_props();
|
||||
props.push((index, (*proposal).clone(), *self));
|
||||
storage::put(PUBLIC_PROPS, &props);
|
||||
PublicProps::put(props);
|
||||
}
|
||||
|
||||
/// Propose a sensitive action to be taken.
|
||||
fn second(self, proposal: PropIndex) {
|
||||
let key = proposal.to_keyed_vec(DEPOSIT_OF);
|
||||
let mut deposit: (Balance, Vec<AccountId>) =
|
||||
storage::get(&key).expect("can only second an existing proposal");
|
||||
let mut deposit = DepositOf::get(proposal).expect("can only second an existing proposal");
|
||||
assert!(staking::internal::deduct_unbonded(&self, deposit.0));
|
||||
|
||||
deposit.1.push(*self);
|
||||
storage::put(&key, &deposit);
|
||||
DepositOf::insert(proposal, deposit);
|
||||
}
|
||||
|
||||
/// Vote in a referendum. If `approve_proposal` is true, the vote is to enact the proposal;
|
||||
@@ -238,13 +204,12 @@ impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
if staking::balance(&self) == 0 {
|
||||
panic!("transactor must have balance to signal approval.");
|
||||
}
|
||||
let key = (*self, ref_index).to_keyed_vec(VOTE_OF);
|
||||
if !storage::exists(&key) {
|
||||
if !VoteOf::exists(&(ref_index, *self)) {
|
||||
let mut voters = voters_for(ref_index);
|
||||
voters.push(self.clone());
|
||||
storage::put(&ref_index.to_keyed_vec(VOTERS_FOR), &voters);
|
||||
VotersFor::insert(ref_index, voters);
|
||||
}
|
||||
storage::put(&key, &approve_proposal);
|
||||
VoteOf::insert(&(ref_index, *self), approve_proposal);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,13 +256,12 @@ pub mod internal {
|
||||
{
|
||||
let (prop_index, proposal, _) = public_props.swap_remove(winner_index);
|
||||
let (deposit, depositors): (Balance, Vec<AccountId>) =
|
||||
storage::take(&prop_index.to_keyed_vec(DEPOSIT_OF))
|
||||
.expect("depositors always exist for current proposals");
|
||||
DepositOf::take(prop_index).expect("depositors always exist for current proposals");
|
||||
// refund depositors
|
||||
for d in &depositors {
|
||||
staking::internal::refund(d, deposit);
|
||||
}
|
||||
storage::put(PUBLIC_PROPS, &public_props);
|
||||
PublicProps::put(public_props);
|
||||
inject_referendum(now + voting_period(), proposal, VoteThreshold::SuperMajorityApprove);
|
||||
}
|
||||
}
|
||||
@@ -310,7 +274,7 @@ pub mod internal {
|
||||
if vote_threshold.approved(approve, against, total_stake) {
|
||||
proposal.dispatch(PrivPass::new());
|
||||
}
|
||||
storage::put(NEXT_TALLY, &(index + 1));
|
||||
NextTally::put(index + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -326,17 +290,17 @@ fn inject_referendum(
|
||||
panic!("Cannot inject a referendum that ends earlier than preceeding referendum");
|
||||
}
|
||||
|
||||
storage::put(REFERENDUM_COUNT, &(ref_index + 1));
|
||||
storage::put(&ref_index.to_keyed_vec(REFERENDUM_INFO_OF), &(end, proposal, vote_threshold));
|
||||
ReferendumCount::put(ref_index + 1);
|
||||
ReferendumInfoOf::insert(ref_index, (end, proposal, vote_threshold));
|
||||
ref_index
|
||||
}
|
||||
|
||||
/// Remove all info on a referendum.
|
||||
fn clear_referendum(ref_index: ReferendumIndex) {
|
||||
storage::kill(&ref_index.to_keyed_vec(REFERENDUM_INFO_OF));
|
||||
storage::kill(&ref_index.to_keyed_vec(VOTERS_FOR));
|
||||
ReferendumInfoOf::remove(ref_index);
|
||||
VotersFor::remove(ref_index);
|
||||
for v in voters_for(ref_index) {
|
||||
storage::kill(&(v, ref_index).to_keyed_vec(VOTE_OF));
|
||||
VoteOf::remove((ref_index, v));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,37 +308,36 @@ fn clear_referendum(ref_index: ReferendumIndex) {
|
||||
pub mod testing {
|
||||
use super::*;
|
||||
use runtime_io::{twox_128, TestExternalities};
|
||||
use runtime_support::{StorageList, StorageValue, StorageMap};
|
||||
use codec::Joiner;
|
||||
use keyring::Keyring::*;
|
||||
use runtime::{session, staking};
|
||||
|
||||
pub fn externalities() -> TestExternalities {
|
||||
map![
|
||||
twox_128(session::SESSION_LENGTH).to_vec() => vec![].and(&1u64),
|
||||
twox_128(session::VALIDATOR_COUNT).to_vec() => vec![].and(&3u32),
|
||||
twox_128(&0u32.to_keyed_vec(session::VALIDATOR_AT)).to_vec() => Alice.to_raw_public_vec(),
|
||||
twox_128(&1u32.to_keyed_vec(session::VALIDATOR_AT)).to_vec() => Bob.to_raw_public_vec(),
|
||||
twox_128(&2u32.to_keyed_vec(session::VALIDATOR_AT)).to_vec() => Charlie.to_raw_public_vec(),
|
||||
twox_128(staking::INTENTION_COUNT).to_vec() => vec![].and(&3u32),
|
||||
twox_128(&0u32.to_keyed_vec(staking::INTENTION_AT)).to_vec() => Alice.to_raw_public_vec(),
|
||||
twox_128(&1u32.to_keyed_vec(staking::INTENTION_AT)).to_vec() => Bob.to_raw_public_vec(),
|
||||
twox_128(&2u32.to_keyed_vec(staking::INTENTION_AT)).to_vec() => Charlie.to_raw_public_vec(),
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(staking::BALANCE_OF)).to_vec() => vec![].and(&10u64),
|
||||
twox_128(&Bob.to_raw_public().to_keyed_vec(staking::BALANCE_OF)).to_vec() => vec![].and(&20u64),
|
||||
twox_128(&Charlie.to_raw_public().to_keyed_vec(staking::BALANCE_OF)).to_vec() => vec![].and(&30u64),
|
||||
twox_128(&Dave.to_raw_public().to_keyed_vec(staking::BALANCE_OF)).to_vec() => vec![].and(&40u64),
|
||||
twox_128(&Eve.to_raw_public().to_keyed_vec(staking::BALANCE_OF)).to_vec() => vec![].and(&50u64),
|
||||
twox_128(&Ferdie.to_raw_public().to_keyed_vec(staking::BALANCE_OF)).to_vec() => vec![].and(&60u64),
|
||||
twox_128(&One.to_raw_public().to_keyed_vec(staking::BALANCE_OF)).to_vec() => vec![].and(&1u64),
|
||||
twox_128(staking::TOTAL_STAKE).to_vec() => vec![].and(&210u64),
|
||||
twox_128(staking::SESSIONS_PER_ERA).to_vec() => vec![].and(&1u64),
|
||||
twox_128(staking::VALIDATOR_COUNT).to_vec() => vec![].and(&3u64),
|
||||
twox_128(staking::CURRENT_ERA).to_vec() => vec![].and(&1u64),
|
||||
twox_128(staking::TRANSACTION_FEE).to_vec() => vec![].and(&1u64),
|
||||
twox_128(session::SessionLength::key()).to_vec() => vec![].and(&1u64),
|
||||
twox_128(session::Validators::key()).to_vec() => vec![].and(&vec![Alice.to_raw_public(), Bob.into(), Charlie.into()]),
|
||||
twox_128(&staking::Intention::len_key()).to_vec() => vec![].and(&3u32),
|
||||
twox_128(&staking::Intention::key_for(0)).to_vec() => Alice.to_raw_public_vec(),
|
||||
twox_128(&staking::Intention::key_for(1)).to_vec() => Bob.to_raw_public_vec(),
|
||||
twox_128(&staking::Intention::key_for(2)).to_vec() => Charlie.to_raw_public_vec(),
|
||||
twox_128(&staking::FreeBalanceOf::key_for(*Alice)).to_vec() => vec![].and(&10u64),
|
||||
twox_128(&staking::FreeBalanceOf::key_for(*Bob)).to_vec() => vec![].and(&20u64),
|
||||
twox_128(&staking::FreeBalanceOf::key_for(*Charlie)).to_vec() => vec![].and(&30u64),
|
||||
twox_128(&staking::FreeBalanceOf::key_for(*Dave)).to_vec() => vec![].and(&40u64),
|
||||
twox_128(&staking::FreeBalanceOf::key_for(*Eve)).to_vec() => vec![].and(&50u64),
|
||||
twox_128(&staking::FreeBalanceOf::key_for(*Ferdie)).to_vec() => vec![].and(&60u64),
|
||||
twox_128(&staking::FreeBalanceOf::key_for(*One)).to_vec() => vec![].and(&1u64),
|
||||
twox_128(staking::TotalStake::key()).to_vec() => vec![].and(&210u64),
|
||||
twox_128(staking::SessionsPerEra::key()).to_vec() => vec![].and(&1u64),
|
||||
twox_128(staking::ValidatorCount::key()).to_vec() => vec![].and(&3u64),
|
||||
twox_128(staking::CurrentEra::key()).to_vec() => vec![].and(&1u64),
|
||||
twox_128(staking::TransactionFee::key()).to_vec() => vec![].and(&1u64),
|
||||
twox_128(staking::BondingDuration::key()).to_vec() => vec![].and(&0u64),
|
||||
|
||||
twox_128(LAUNCH_PERIOD).to_vec() => vec![].and(&1u64),
|
||||
twox_128(VOTING_PERIOD).to_vec() => vec![].and(&1u64),
|
||||
twox_128(MINIMUM_DEPOSIT).to_vec() => vec![].and(&1u64)
|
||||
twox_128(LaunchPeriod::key()).to_vec() => vec![].and(&1u64),
|
||||
twox_128(VotingPeriod::key()).to_vec() => vec![].and(&1u64),
|
||||
twox_128(MinimumDeposit::key()).to_vec() => vec![].and(&1u64)
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -442,7 +405,7 @@ mod tests {
|
||||
|
||||
assert_eq!(next_free_ref_index(), 1);
|
||||
assert_eq!(voters_for(r), vec![Alice.to_raw_public()]);
|
||||
assert_eq!(vote_of(&Alice, r), Some(true));
|
||||
assert_eq!(vote_of((r, *Alice)), Some(true));
|
||||
assert_eq!(tally(r), (10, 0));
|
||||
|
||||
democracy::internal::end_block(system::block_number());
|
||||
@@ -557,7 +520,7 @@ mod tests {
|
||||
PublicPass::test(&Alice).vote(r, true);
|
||||
|
||||
assert_eq!(voters_for(r), vec![Alice.to_raw_public()]);
|
||||
assert_eq!(vote_of(&Alice, r), Some(true));
|
||||
assert_eq!(vote_of((r, *Alice)), Some(true));
|
||||
assert_eq!(tally(r), (10, 0));
|
||||
|
||||
democracy::internal::end_block(system::block_number());
|
||||
@@ -590,7 +553,7 @@ mod tests {
|
||||
PublicPass::test(&Alice).vote(r, false);
|
||||
|
||||
assert_eq!(voters_for(r), vec![Alice.to_raw_public()]);
|
||||
assert_eq!(vote_of(&Alice, r), Some(false));
|
||||
assert_eq!(vote_of((r, *Alice)), Some(false));
|
||||
assert_eq!(tally(r), (0, 10));
|
||||
|
||||
democracy::internal::end_block(system::block_number());
|
||||
|
||||
@@ -19,49 +19,31 @@
|
||||
|
||||
use rstd::prelude::*;
|
||||
use codec::KeyedVec;
|
||||
use runtime_support::{storage, StorageVec};
|
||||
use runtime_support::{storage, StorageValue, StorageMap};
|
||||
use demo_primitives::{AccountId, SessionKey, BlockNumber};
|
||||
use runtime::{system, staking, consensus};
|
||||
use runtime::democracy::PrivPass;
|
||||
use runtime::staking::PublicPass;
|
||||
|
||||
pub const SESSION_LENGTH: &[u8] = b"ses:len";
|
||||
pub const CURRENT_INDEX: &[u8] = b"ses:ind";
|
||||
pub const LAST_LENGTH_CHANGE: &[u8] = b"ses:llc";
|
||||
pub const NEXT_KEY_FOR: &[u8] = b"ses:nxt:";
|
||||
pub const NEXT_SESSION_LENGTH: &[u8] = b"ses:nln";
|
||||
pub const VALIDATOR_AT: &[u8] = b"ses:val:";
|
||||
pub const VALIDATOR_COUNT: &[u8] = b"ses:val:len";
|
||||
storage_items!{
|
||||
// The current set of validators.
|
||||
pub Validators get(validators): b"ses:val" => required Vec<AccountId>;
|
||||
// Current length of the session.
|
||||
pub SessionLength get(length): b"ses:len" => required BlockNumber;
|
||||
// Current index of the session.
|
||||
pub CurrentIndex get(current_index): b"ses:ind" => required BlockNumber;
|
||||
|
||||
struct ValidatorStorageVec {}
|
||||
impl StorageVec for ValidatorStorageVec {
|
||||
type Item = AccountId;
|
||||
const PREFIX: &'static[u8] = VALIDATOR_AT;
|
||||
}
|
||||
|
||||
/// Get the current set of validators.
|
||||
pub fn validators() -> Vec<AccountId> {
|
||||
ValidatorStorageVec::items()
|
||||
}
|
||||
|
||||
/// The number of blocks in each session.
|
||||
pub fn length() -> BlockNumber {
|
||||
storage::get_or(SESSION_LENGTH, 0)
|
||||
// Block at which the session length last changed.
|
||||
LastLengthChange: b"ses:llc" => default BlockNumber;
|
||||
// The next key for a given validator.
|
||||
NextKeyFor: b"ses:nxt:" => map [ AccountId => SessionKey ];
|
||||
// The next session length.
|
||||
NextSessionLength: b"ses:nln" => BlockNumber;
|
||||
}
|
||||
|
||||
/// The number of validators currently.
|
||||
pub fn validator_count() -> u32 {
|
||||
ValidatorStorageVec::count() as u32
|
||||
}
|
||||
|
||||
/// The current era index.
|
||||
pub fn current_index() -> BlockNumber {
|
||||
storage::get_or(CURRENT_INDEX, 0)
|
||||
}
|
||||
|
||||
/// The block number at which the era length last changed.
|
||||
pub fn last_length_change() -> BlockNumber {
|
||||
storage::get_or(LAST_LENGTH_CHANGE, 0)
|
||||
Validators::get().len() as u32 // TODO: can probably optimised
|
||||
}
|
||||
|
||||
impl_dispatch! {
|
||||
@@ -74,7 +56,7 @@ impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
/// session.
|
||||
fn set_key(self, key: SessionKey) {
|
||||
// set new value for next session
|
||||
storage::put(&self.to_keyed_vec(NEXT_KEY_FOR), &key);
|
||||
NextKeyFor::insert(*self, key)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +69,7 @@ impl_dispatch! {
|
||||
impl privileged::Dispatch for PrivPass {
|
||||
/// Set a new era length. Won't kick in until the next era change (at current length).
|
||||
fn set_length(self, new: BlockNumber) {
|
||||
storage::put(NEXT_SESSION_LENGTH, &new);
|
||||
NextSessionLength::put(new);
|
||||
}
|
||||
|
||||
/// Forces a new session.
|
||||
@@ -103,10 +85,10 @@ pub mod internal {
|
||||
|
||||
/// Set the current set of validators.
|
||||
///
|
||||
/// Called by staking::next_era() only. `next_session` should be called after this in order to
|
||||
/// Called by `staking::next_era()` only. `next_session` should be called after this in order to
|
||||
/// update the session keys to the next validator set.
|
||||
pub fn set_validators(new: &[AccountId]) {
|
||||
ValidatorStorageVec::set_items(new);
|
||||
Validators::put(&new.to_vec()); // TODO: optimise.
|
||||
consensus::internal::set_authorities(new);
|
||||
}
|
||||
|
||||
@@ -115,7 +97,7 @@ pub mod internal {
|
||||
// do this last, after the staking system has had chance to switch out the authorities for the
|
||||
// new set.
|
||||
// check block number and call next_session if necessary.
|
||||
if (system::block_number() - last_length_change()) % length() == 0 {
|
||||
if (system::block_number() - LastLengthChange::get()) % length() == 0 {
|
||||
rotate_session();
|
||||
}
|
||||
}
|
||||
@@ -123,19 +105,17 @@ pub mod internal {
|
||||
/// Move onto next session: register the new authority set.
|
||||
pub fn rotate_session() {
|
||||
// Increment current session index.
|
||||
storage::put(CURRENT_INDEX, &(current_index() + 1));
|
||||
CurrentIndex::put(CurrentIndex::get() + 1);
|
||||
|
||||
// Enact era length change.
|
||||
if let Some(next_len) = storage::get::<u64>(NEXT_SESSION_LENGTH) {
|
||||
storage::put(SESSION_LENGTH, &next_len);
|
||||
storage::put(LAST_LENGTH_CHANGE, &system::block_number());
|
||||
storage::kill(NEXT_SESSION_LENGTH);
|
||||
if let Some(next_len) = NextSessionLength::take() {
|
||||
SessionLength::put(next_len);
|
||||
LastLengthChange::put(system::block_number());
|
||||
}
|
||||
|
||||
// Update any changes in session keys.
|
||||
validators().iter().enumerate().for_each(|(i, v)| {
|
||||
let k = v.to_keyed_vec(NEXT_KEY_FOR);
|
||||
if let Some(n) = storage::take(&k) {
|
||||
if let Some(n) = NextKeyFor::take(v) {
|
||||
consensus::internal::set_authority(i as u32, &n);
|
||||
}
|
||||
});
|
||||
@@ -147,20 +127,16 @@ pub mod testing {
|
||||
use super::*;
|
||||
use runtime_io::{twox_128, TestExternalities};
|
||||
use codec::{Joiner, KeyedVec};
|
||||
use keyring::Keyring;
|
||||
use keyring::Keyring::*;
|
||||
use runtime::system;
|
||||
|
||||
pub fn externalities(session_length: u64) -> TestExternalities {
|
||||
let one = Keyring::One.to_raw_public();
|
||||
let two = Keyring::Two.to_raw_public();
|
||||
let three = [3u8; 32];
|
||||
|
||||
let extras: TestExternalities = map![
|
||||
twox_128(SESSION_LENGTH).to_vec() => vec![].and(&session_length),
|
||||
twox_128(VALIDATOR_COUNT).to_vec() => vec![].and(&3u32),
|
||||
twox_128(&0u32.to_keyed_vec(VALIDATOR_AT)).to_vec() => one.to_vec(),
|
||||
twox_128(&1u32.to_keyed_vec(VALIDATOR_AT)).to_vec() => two.to_vec(),
|
||||
twox_128(&2u32.to_keyed_vec(VALIDATOR_AT)).to_vec() => three.to_vec()
|
||||
twox_128(SessionLength::key()).to_vec() => vec![].and(&session_length),
|
||||
twox_128(CurrentIndex::key()).to_vec() => vec![].and(&0u64),
|
||||
twox_128(Validators::key()).to_vec() => vec![].and(&vec![One.into(), Two.into(), three])
|
||||
];
|
||||
system::testing::externalities().into_iter().chain(extras.into_iter()).collect()
|
||||
}
|
||||
@@ -181,11 +157,10 @@ mod tests {
|
||||
|
||||
fn simple_setup() -> TestExternalities {
|
||||
map![
|
||||
twox_128(SESSION_LENGTH).to_vec() => vec![].and(&2u64),
|
||||
twox_128(SessionLength::key()).to_vec() => vec![].and(&2u64),
|
||||
twox_128(CurrentIndex::key()).to_vec() => vec![].and(&0u64),
|
||||
// the validators (10, 20, ...)
|
||||
twox_128(b"ses:val:len").to_vec() => vec![].and(&2u32),
|
||||
twox_128(&0u32.to_keyed_vec(ValidatorStorageVec::PREFIX)).to_vec() => vec![10; 32],
|
||||
twox_128(&1u32.to_keyed_vec(ValidatorStorageVec::PREFIX)).to_vec() => vec![20; 32],
|
||||
twox_128(Validators::key()).to_vec() => vec![].and(&vec![[10u8; 32], [20; 32]]),
|
||||
// initial session keys (11, 21, ...)
|
||||
b":auth:len".to_vec() => vec![].and(&2u32),
|
||||
0u32.to_keyed_vec(b":auth:") => vec![11; 32],
|
||||
|
||||
@@ -22,7 +22,7 @@ use rstd::cell::RefCell;
|
||||
use rstd::collections::btree_map::{BTreeMap, Entry};
|
||||
use runtime_io::{print, blake2_256};
|
||||
use codec::{Slicable, Input, KeyedVec};
|
||||
use runtime_support::{storage, StorageVec};
|
||||
use runtime_support::{storage, StorageValue, StorageList, StorageMap};
|
||||
use demo_primitives::{BlockNumber, AccountId};
|
||||
use runtime::{system, session, democracy};
|
||||
|
||||
@@ -32,78 +32,52 @@ pub type Balance = u64;
|
||||
/// The amount of bonding period left in an account. Measured in eras.
|
||||
pub type Bondage = u64;
|
||||
|
||||
pub const BONDING_DURATION: &[u8] = b"sta:loc";
|
||||
pub const VALIDATOR_COUNT: &[u8] = b"sta:vac";
|
||||
pub const SESSIONS_PER_ERA: &[u8] = b"sta:spe";
|
||||
pub const NEXT_SESSIONS_PER_ERA: &[u8] = b"sta:nse";
|
||||
pub const CURRENT_ERA: &[u8] = b"sta:era";
|
||||
pub const LAST_ERA_LENGTH_CHANGE: &[u8] = b"sta:lec";
|
||||
pub const TOTAL_STAKE: &[u8] = b"sta:tot";
|
||||
pub const INTENTION_AT: &[u8] = b"sta:wil:";
|
||||
pub const INTENTION_COUNT: &[u8] = b"sta:wil:len";
|
||||
pub const TRANSACTION_FEE: &[u8] = b"sta:fee";
|
||||
storage_items! {
|
||||
// The length of the bonding duration in eras.
|
||||
pub BondingDuration get(bonding_duration): b"sta:loc" => required BlockNumber;
|
||||
// The length of a staking era in sessions.
|
||||
pub ValidatorCount get(validator_count): b"sta:vac" => required u32;
|
||||
// The length of a staking era in sessions.
|
||||
pub SessionsPerEra get(sessions_per_era): b"sta:spe" => required BlockNumber;
|
||||
// The total amount of stake on the system.
|
||||
pub TotalStake get(total_stake): b"sta:tot" => required Balance;
|
||||
// The fee to be paid for making a transaction.
|
||||
pub TransactionFee get(transaction_fee): b"sta:fee" => required Balance;
|
||||
|
||||
pub const BALANCE_OF: &[u8] = b"sta:bal:";
|
||||
pub const RESERVED_BALANCE_OF: &[u8] = b"sta:lbo:";
|
||||
pub const BONDAGE_OF: &[u8] = b"sta:bon:";
|
||||
pub const CODE_OF: &[u8] = b"sta:cod:";
|
||||
pub const STORAGE_OF: &[u8] = b"sta:sto:";
|
||||
// The current era index.
|
||||
pub CurrentEra get(current_era): b"sta:era" => required BlockNumber;
|
||||
// All the accounts with a desire to stake.
|
||||
pub Intention: b"sta:wil:" => list [ AccountId ];
|
||||
// The next value of sessions per era.
|
||||
pub NextSessionsPerEra get(next_sessions_per_era): b"sta:nse" => BlockNumber;
|
||||
// The block number at which the era length last changed.
|
||||
pub LastEraLengthChange get(last_era_length_change): b"sta:lec" => default BlockNumber;
|
||||
|
||||
pub struct IntentionStorageVec {}
|
||||
impl StorageVec for IntentionStorageVec {
|
||||
type Item = AccountId;
|
||||
const PREFIX: &'static[u8] = INTENTION_AT;
|
||||
}
|
||||
// The balance of a given account.
|
||||
pub FreeBalanceOf get(free_balance_of): b"sta:bal:" => default map [ AccountId => Balance ];
|
||||
|
||||
/// The fee to be paid for making a transaction.
|
||||
pub fn transaction_fee() -> Balance {
|
||||
storage::get(TRANSACTION_FEE).expect("All basic parameters should be defined")
|
||||
}
|
||||
// The amount of the balance of a given account that is exterally reserved; this can still get
|
||||
// slashed, but gets slashed last of all.
|
||||
pub ReservedBalanceOf get(reserved_balance_of): b"sta:lbo:" => default map [ AccountId => Balance ];
|
||||
|
||||
/// The length of the bonding duration in eras.
|
||||
pub fn bonding_duration() -> BlockNumber {
|
||||
storage::get_or_default(BONDING_DURATION)
|
||||
}
|
||||
// The block at which the `who`'s funds become entirely liquid.
|
||||
pub BondageOf get(bondage_of): b"sta:bon:" => default map [ AccountId => Bondage ];
|
||||
|
||||
/// The length of a staking era in sessions.
|
||||
pub fn validator_count() -> usize {
|
||||
storage::get_or_default::<u32>(VALIDATOR_COUNT) as usize
|
||||
// The code associated with an account.
|
||||
pub CodeOf: b"sta:cod:" => default map [ AccountId => Vec<u8> ]; // TODO Vec<u8> values should be optimised to not do a length prefix.
|
||||
|
||||
// The storage items associated with an account/key.
|
||||
pub StorageOf: b"sta:sto:" => map [ (AccountId, Vec<u8>) => Vec<u8> ]; // TODO: keys should also be able to take AsRef<KeyType> to ensure Vec<u8>s can be passed as &[u8]
|
||||
}
|
||||
|
||||
/// The length of a staking era in blocks.
|
||||
pub fn era_length() -> BlockNumber {
|
||||
sessions_per_era() * session::length()
|
||||
SessionsPerEra::get() * session::length()
|
||||
}
|
||||
|
||||
/// The length of a staking era in sessions.
|
||||
pub fn sessions_per_era() -> BlockNumber {
|
||||
storage::get_or_default(SESSIONS_PER_ERA)
|
||||
}
|
||||
|
||||
/// The current era index.
|
||||
pub fn current_era() -> BlockNumber {
|
||||
storage::get_or_default(CURRENT_ERA)
|
||||
}
|
||||
|
||||
/// The block number at which the era length last changed.
|
||||
pub fn last_era_length_change() -> BlockNumber {
|
||||
storage::get_or_default(LAST_ERA_LENGTH_CHANGE)
|
||||
}
|
||||
|
||||
/// The balance of a given account.
|
||||
/// The combined balance of `who`.
|
||||
pub fn balance(who: &AccountId) -> Balance {
|
||||
free_balance(who) + reserved_balance(who)
|
||||
}
|
||||
|
||||
/// The balance of a given account.
|
||||
pub fn free_balance(who: &AccountId) -> Balance {
|
||||
storage::get_or_default(&who.to_keyed_vec(BALANCE_OF))
|
||||
}
|
||||
|
||||
/// The amount of the balance of a given account that is exterally reserved; this can still get
|
||||
/// slashed, but gets slashed last of all.
|
||||
pub fn reserved_balance(who: &AccountId) -> Balance {
|
||||
storage::get_or_default(&who.to_keyed_vec(RESERVED_BALANCE_OF))
|
||||
FreeBalanceOf::get(who) + ReservedBalanceOf::get(who)
|
||||
}
|
||||
|
||||
/// Some result as `slash(who, value)` (but without the side-effects) asuming there are no
|
||||
@@ -112,11 +86,6 @@ pub fn can_slash(who: &AccountId, value: Balance) -> bool {
|
||||
balance(who) >= value
|
||||
}
|
||||
|
||||
/// The block at which the `who`'s funds become entirely liquid.
|
||||
pub fn bondage(who: &AccountId) -> Bondage {
|
||||
storage::get_or_default(&who.to_keyed_vec(BONDAGE_OF))
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
pub enum LockStatus {
|
||||
@@ -127,28 +96,23 @@ pub enum LockStatus {
|
||||
|
||||
/// The block at which the `who`'s funds become entirely liquid.
|
||||
pub fn unlock_block(who: &AccountId) -> LockStatus {
|
||||
match bondage(who) {
|
||||
match BondageOf::get(who) {
|
||||
i if i == Bondage::max_value() => LockStatus::Staked,
|
||||
i if i <= system::block_number() => LockStatus::Liquid,
|
||||
i => LockStatus::LockedUntil(i),
|
||||
}
|
||||
}
|
||||
|
||||
/// The total amount of stake on the system.
|
||||
pub fn total_stake() -> Balance {
|
||||
storage::get_or(TOTAL_STAKE, 0)
|
||||
}
|
||||
|
||||
pub struct PublicPass<'a> (&'a AccountId);
|
||||
|
||||
const NOBODY: AccountId = [0u8; 32];
|
||||
|
||||
impl<'a> PublicPass<'a> {
|
||||
pub fn new(transactor: &AccountId) -> PublicPass {
|
||||
let b = free_balance(&transactor);
|
||||
let transaction_fee = transaction_fee();
|
||||
let b = FreeBalanceOf::get(transactor);
|
||||
let transaction_fee = TransactionFee::get();
|
||||
assert!(b >= transaction_fee, "attempt to transact without enough funds to pay fee");
|
||||
internal::set_free_balance(&transactor, b - transaction_fee);
|
||||
FreeBalanceOf::insert(transactor, b - transaction_fee);
|
||||
PublicPass(transactor)
|
||||
}
|
||||
|
||||
@@ -199,26 +163,26 @@ impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
///
|
||||
/// Effects will be felt at the beginning of the next era.
|
||||
fn stake(self) {
|
||||
let mut intentions = IntentionStorageVec::items();
|
||||
let mut intentions = Intention::items();
|
||||
// can't be in the list twice.
|
||||
assert!(intentions.iter().find(|&t| *t == *self).is_none(), "Cannot stake if already staked.");
|
||||
intentions.push(self.clone());
|
||||
IntentionStorageVec::set_items(&intentions);
|
||||
storage::put(&self.to_keyed_vec(BONDAGE_OF), &u64::max_value());
|
||||
Intention::set_items(&intentions);
|
||||
BondageOf::insert(*self, u64::max_value());
|
||||
}
|
||||
|
||||
/// Retract the desire to stake for the transactor.
|
||||
///
|
||||
/// Effects will be felt at the beginning of the next era.
|
||||
fn unstake(self) {
|
||||
let mut intentions = IntentionStorageVec::items();
|
||||
let mut intentions = Intention::items();
|
||||
if let Some(position) = intentions.iter().position(|&t| t == *self) {
|
||||
intentions.swap_remove(position);
|
||||
} else {
|
||||
panic!("Cannot unstake if not already staked.");
|
||||
}
|
||||
IntentionStorageVec::set_items(&intentions);
|
||||
storage::put(&self.to_keyed_vec(BONDAGE_OF), &(current_era() + bonding_duration()));
|
||||
Intention::set_items(&intentions);
|
||||
BondageOf::insert(*self, CurrentEra::get() + BondingDuration::get());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,17 +197,17 @@ impl_dispatch! {
|
||||
impl privileged::Dispatch for democracy::PrivPass {
|
||||
/// Set the number of sessions in an era.
|
||||
fn set_sessions_per_era(self, new: BlockNumber) {
|
||||
storage::put(NEXT_SESSIONS_PER_ERA, &new);
|
||||
NextSessionsPerEra::put(&new);
|
||||
}
|
||||
|
||||
/// The length of the bonding duration in eras.
|
||||
fn set_bonding_duration(self, new: BlockNumber) {
|
||||
storage::put(BONDING_DURATION, &new);
|
||||
BondingDuration::put(&new);
|
||||
}
|
||||
|
||||
/// The length of a staking era in sessions.
|
||||
fn set_validator_count(self, new: u32) {
|
||||
storage::put(VALIDATOR_COUNT, &new);
|
||||
ValidatorCount::put(&new);
|
||||
}
|
||||
|
||||
/// Force there to be a new era. This also forces a new session immediately after.
|
||||
@@ -254,9 +218,9 @@ impl privileged::Dispatch for democracy::PrivPass {
|
||||
}
|
||||
|
||||
// Each identity's stake may be in one of three bondage states, given by an integer:
|
||||
// - n | n <= current_era(): inactive: free to be transferred.
|
||||
// - n | n <= CurrentEra::get(): inactive: free to be transferred.
|
||||
// - ~0: active: currently representing a validator.
|
||||
// - n | n > current_era(): deactivating: recently representing a validator and not yet
|
||||
// - n | n > CurrentEra::get(): deactivating: recently representing a validator and not yet
|
||||
// ready for transfer.
|
||||
|
||||
mod private {
|
||||
@@ -296,15 +260,13 @@ mod private {
|
||||
pub struct DirectExt;
|
||||
impl Externalities for DirectExt {
|
||||
fn get_storage(&self, account: &AccountId, location: &[u8]) -> Option<Vec<u8>> {
|
||||
let mut v = account.to_keyed_vec(STORAGE_OF);
|
||||
v.extend(location);
|
||||
storage::get_raw(&v)
|
||||
StorageOf::get(&(*account, location.to_vec()))
|
||||
}
|
||||
fn get_code(&self, account: &AccountId) -> Vec<u8> {
|
||||
storage::get_raw(&account.to_keyed_vec(CODE_OF)).unwrap_or_default()
|
||||
CodeOf::get(account)
|
||||
}
|
||||
fn get_balance(&self, account: &AccountId) -> Balance {
|
||||
storage::get_or_default::<Balance>(&account.to_keyed_vec(BALANCE_OF))
|
||||
FreeBalanceOf::get(account)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,19 +289,16 @@ mod private {
|
||||
pub fn commit_state(s: State) {
|
||||
for (address, changed) in s.into_iter() {
|
||||
if let Some(balance) = changed.balance {
|
||||
storage::put(&address.to_keyed_vec(BALANCE_OF), &balance);
|
||||
FreeBalanceOf::insert(address, balance);
|
||||
}
|
||||
if let Some(code) = changed.code {
|
||||
storage::put(&address.to_keyed_vec(CODE_OF), &code);
|
||||
CodeOf::insert(&address, &code);
|
||||
}
|
||||
let storage_key = address.to_keyed_vec(STORAGE_OF);
|
||||
for (k, v) in changed.storage.into_iter() {
|
||||
let mut key = storage_key.clone();
|
||||
key.extend(k);
|
||||
if let Some(value) = v {
|
||||
storage::put_raw(&key, &value);
|
||||
StorageOf::insert(&(address, k), &value);
|
||||
} else {
|
||||
storage::kill(&key);
|
||||
StorageOf::remove(&(address, k));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -404,7 +363,7 @@ mod private {
|
||||
assert!(from_balance >= value);
|
||||
|
||||
let to_balance = ext.get_balance(dest);
|
||||
assert!(bondage(transactor) <= bondage(dest));
|
||||
assert!(BondageOf::get(transactor) <= BondageOf::get(dest));
|
||||
assert!(to_balance + value > to_balance); // no overflow
|
||||
|
||||
// TODO: a fee, based upon gaslimit/gasprice.
|
||||
@@ -470,17 +429,10 @@ mod private {
|
||||
pub mod internal {
|
||||
use super::*;
|
||||
|
||||
/// Set the balance of an account.
|
||||
/// Needless to say, this is super low-level and accordingly dangerous. Ensure any modules that
|
||||
/// use it are auditted to the hilt.
|
||||
pub fn set_free_balance(who: &AccountId, value: Balance) {
|
||||
storage::put(&who.to_keyed_vec(BALANCE_OF), &value);
|
||||
}
|
||||
|
||||
/// Hook to be called after to transaction processing.
|
||||
pub fn check_new_era() {
|
||||
// check block number and call new_era if necessary.
|
||||
if (system::block_number() - last_era_length_change()) % era_length() == 0 {
|
||||
if (system::block_number() - LastEraLengthChange::get()) % era_length() == 0 {
|
||||
new_era();
|
||||
}
|
||||
}
|
||||
@@ -488,9 +440,9 @@ pub mod internal {
|
||||
/// Deduct from an unbonded balance. true if it happened.
|
||||
pub fn deduct_unbonded(who: &AccountId, value: Balance) -> bool {
|
||||
if let LockStatus::Liquid = unlock_block(who) {
|
||||
let b = free_balance(who);
|
||||
let b = FreeBalanceOf::get(who);
|
||||
if b >= value {
|
||||
set_free_balance(who, b - value);
|
||||
FreeBalanceOf::insert(who, &(b - value));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -499,14 +451,14 @@ pub mod internal {
|
||||
|
||||
/// Refund some balance.
|
||||
pub fn refund(who: &AccountId, value: Balance) {
|
||||
set_free_balance(who, free_balance(who) + value)
|
||||
FreeBalanceOf::insert(who, &(FreeBalanceOf::get(who) + value))
|
||||
}
|
||||
|
||||
/// Will slash any balance, but prefer free over reserved.
|
||||
pub fn slash(who: &AccountId, value: Balance) -> bool {
|
||||
let free_balance = free_balance(who);
|
||||
let free_balance = FreeBalanceOf::get(who);
|
||||
let free_slash = cmp::min(free_balance, value);
|
||||
set_free_balance(who, free_balance - free_slash);
|
||||
FreeBalanceOf::insert(who, &(free_balance - free_slash));
|
||||
if free_slash < value {
|
||||
slash_reserved(who, value - free_slash)
|
||||
} else {
|
||||
@@ -516,63 +468,59 @@ pub mod internal {
|
||||
|
||||
/// Moves `value` from balance to reserved balance.
|
||||
pub fn reserve_balance(who: &AccountId, value: Balance) {
|
||||
let b = free_balance(who);
|
||||
let b = FreeBalanceOf::get(who);
|
||||
assert!(b >= value);
|
||||
set_free_balance(who, b - value);
|
||||
set_reserved_balance(who, reserved_balance(who) + value);
|
||||
FreeBalanceOf::insert(who, &(b - value));
|
||||
ReservedBalanceOf::insert(who, &(ReservedBalanceOf::get(who) + value));
|
||||
}
|
||||
|
||||
/// Moves `value` from reserved balance to balance.
|
||||
pub fn unreserve_balance(who: &AccountId, value: Balance) {
|
||||
let b = reserved_balance(who);
|
||||
let b = ReservedBalanceOf::get(who);
|
||||
let value = cmp::min(b, value);
|
||||
set_reserved_balance(who, b - value);
|
||||
set_free_balance(who, free_balance(who) + value);
|
||||
ReservedBalanceOf::insert(who, &(b - value));
|
||||
FreeBalanceOf::insert(who, &(FreeBalanceOf::get(who) + value));
|
||||
}
|
||||
|
||||
/// Moves `value` from reserved balance to balance.
|
||||
pub fn slash_reserved(who: &AccountId, value: Balance) -> bool {
|
||||
let b = reserved_balance(who);
|
||||
let b = ReservedBalanceOf::get(who);
|
||||
let slash = cmp::min(b, value);
|
||||
set_reserved_balance(who, b - slash);
|
||||
ReservedBalanceOf::insert(who, &(b - slash));
|
||||
value == slash
|
||||
}
|
||||
|
||||
/// Moves `value` from reserved balance to balance.
|
||||
pub fn transfer_reserved_balance(slashed: &AccountId, beneficiary: &AccountId, value: Balance) -> bool {
|
||||
let b = reserved_balance(slashed);
|
||||
let b = ReservedBalanceOf::get(slashed);
|
||||
let slash = cmp::min(b, value);
|
||||
set_reserved_balance(slashed, b - slash);
|
||||
set_free_balance(beneficiary, free_balance(beneficiary) + slash);
|
||||
ReservedBalanceOf::insert(slashed, &(b - slash));
|
||||
FreeBalanceOf::insert(beneficiary, &(FreeBalanceOf::get(beneficiary) + slash));
|
||||
slash == value
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the reserved portion of `who`'s balance.
|
||||
fn set_reserved_balance(who: &AccountId, value: Balance) {
|
||||
storage::put(&who.to_keyed_vec(RESERVED_BALANCE_OF), &value);
|
||||
}
|
||||
|
||||
/// The era has changed - enact new staking set.
|
||||
///
|
||||
/// NOTE: This always happens immediately before a session change to ensure that new validators
|
||||
/// get a chance to set their session keys.
|
||||
fn new_era() {
|
||||
// Increment current era.
|
||||
storage::put(CURRENT_ERA, &(current_era() + 1));
|
||||
CurrentEra::put(&(CurrentEra::get() + 1));
|
||||
|
||||
// Enact era length change.
|
||||
let next_spe: u64 = storage::get_or_default(NEXT_SESSIONS_PER_ERA);
|
||||
if next_spe > 0 && next_spe != sessions_per_era() {
|
||||
storage::put(SESSIONS_PER_ERA, &next_spe);
|
||||
storage::put(LAST_ERA_LENGTH_CHANGE, &system::block_number());
|
||||
if let Some(next_spe) = NextSessionsPerEra::get() {
|
||||
if next_spe != SessionsPerEra::get() {
|
||||
SessionsPerEra::put(&next_spe);
|
||||
LastEraLengthChange::put(&system::block_number());
|
||||
}
|
||||
}
|
||||
|
||||
// evaluate desired staking amounts and nominations and optimise to find the best
|
||||
// combination of validators, then use session::internal::set_validators().
|
||||
// for now, this just orders would-be stakers by their balances and chooses the top-most
|
||||
// validator_count() of them.
|
||||
let mut intentions = IntentionStorageVec::items()
|
||||
// ValidatorCount::get() of them.
|
||||
let mut intentions = Intention::items()
|
||||
.into_iter()
|
||||
.map(|v| (balance(&v), v))
|
||||
.collect::<Vec<_>>();
|
||||
@@ -580,7 +528,7 @@ fn new_era() {
|
||||
session::internal::set_validators(
|
||||
&intentions.into_iter()
|
||||
.map(|(_, v)| v)
|
||||
.take(validator_count())
|
||||
.take(ValidatorCount::get() as usize)
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
@@ -597,15 +545,16 @@ pub mod testing {
|
||||
|
||||
pub fn externalities(session_length: u64, sessions_per_era: u64, current_era: u64) -> TestExternalities {
|
||||
let extras: TestExternalities = map![
|
||||
twox_128(INTENTION_COUNT).to_vec() => vec![].and(&3u32),
|
||||
twox_128(&0u32.to_keyed_vec(INTENTION_AT)).to_vec() => Alice.to_raw_public_vec(),
|
||||
twox_128(&1u32.to_keyed_vec(INTENTION_AT)).to_vec() => Bob.to_raw_public_vec(),
|
||||
twox_128(&2u32.to_keyed_vec(INTENTION_AT)).to_vec() => Charlie.to_raw_public_vec(),
|
||||
twox_128(SESSIONS_PER_ERA).to_vec() => vec![].and(&sessions_per_era),
|
||||
twox_128(VALIDATOR_COUNT).to_vec() => vec![].and(&3u64),
|
||||
twox_128(TRANSACTION_FEE).to_vec() => vec![].and(&1u64),
|
||||
twox_128(CURRENT_ERA).to_vec() => vec![].and(¤t_era),
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
twox_128(&Intention::len_key()).to_vec() => vec![].and(&3u32),
|
||||
twox_128(&Intention::key_for(0)).to_vec() => Alice.to_raw_public_vec(),
|
||||
twox_128(&Intention::key_for(1)).to_vec() => Bob.to_raw_public_vec(),
|
||||
twox_128(&Intention::key_for(2)).to_vec() => Charlie.to_raw_public_vec(),
|
||||
twox_128(SessionsPerEra::key()).to_vec() => vec![].and(&sessions_per_era),
|
||||
twox_128(ValidatorCount::key()).to_vec() => vec![].and(&3u64),
|
||||
twox_128(BondingDuration::key()).to_vec() => vec![].and(&0u64),
|
||||
twox_128(TransactionFee::key()).to_vec() => vec![].and(&1u64),
|
||||
twox_128(CurrentEra::key()).to_vec() => vec![].and(¤t_era),
|
||||
twox_128(&FreeBalanceOf::key_for(*Alice)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
];
|
||||
session::testing::externalities(session_length).into_iter().chain(extras.into_iter()).collect()
|
||||
}
|
||||
@@ -630,25 +579,24 @@ mod tests {
|
||||
#[test]
|
||||
fn staking_should_work() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(session::SESSION_LENGTH).to_vec() => vec![].and(&1u64),
|
||||
twox_128(session::VALIDATOR_COUNT).to_vec() => vec![].and(&2u32),
|
||||
twox_128(&0u32.to_keyed_vec(session::VALIDATOR_AT)).to_vec() => vec![10; 32],
|
||||
twox_128(&1u32.to_keyed_vec(session::VALIDATOR_AT)).to_vec() => vec![20; 32],
|
||||
twox_128(SESSIONS_PER_ERA).to_vec() => vec![].and(&2u64),
|
||||
twox_128(VALIDATOR_COUNT).to_vec() => vec![].and(&2u32),
|
||||
twox_128(BONDING_DURATION).to_vec() => vec![].and(&3u64),
|
||||
twox_128(TOTAL_STAKE).to_vec() => vec![].and(&100u64),
|
||||
twox_128(TRANSACTION_FEE).to_vec() => vec![].and(&0u64),
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&10u64),
|
||||
twox_128(&Bob.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&20u64),
|
||||
twox_128(&Charlie.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&30u64),
|
||||
twox_128(&Dave.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&40u64)
|
||||
twox_128(session::SessionLength::key()).to_vec() => vec![].and(&1u64),
|
||||
twox_128(session::Validators::key()).to_vec() => vec![].and(&vec![[10u8; 32], [20; 32]]),
|
||||
twox_128(CurrentEra::key()).to_vec() => vec![].and(&0u64),
|
||||
twox_128(SessionsPerEra::key()).to_vec() => vec![].and(&2u64),
|
||||
twox_128(ValidatorCount::key()).to_vec() => vec![].and(&2u32),
|
||||
twox_128(BondingDuration::key()).to_vec() => vec![].and(&3u64),
|
||||
twox_128(TotalStake::key()).to_vec() => vec![].and(&100u64),
|
||||
twox_128(TransactionFee::key()).to_vec() => vec![].and(&0u64),
|
||||
twox_128(&FreeBalanceOf::key_for(*Alice)).to_vec() => vec![].and(&10u64),
|
||||
twox_128(&FreeBalanceOf::key_for(*Bob)).to_vec() => vec![].and(&20u64),
|
||||
twox_128(&FreeBalanceOf::key_for(*Charlie)).to_vec() => vec![].and(&30u64),
|
||||
twox_128(&FreeBalanceOf::key_for(*Dave)).to_vec() => vec![].and(&40u64)
|
||||
];
|
||||
|
||||
with_externalities(&mut t, || {
|
||||
assert_eq!(era_length(), 2u64);
|
||||
assert_eq!(validator_count(), 2usize);
|
||||
assert_eq!(bonding_duration(), 3u64);
|
||||
assert_eq!(ValidatorCount::get(), 2);
|
||||
assert_eq!(BondingDuration::get(), 3);
|
||||
assert_eq!(session::validators(), vec![[10u8; 32], [20u8; 32]]);
|
||||
|
||||
// Block 1: Add three validators. No obvious change.
|
||||
@@ -701,76 +649,78 @@ mod tests {
|
||||
#[test]
|
||||
fn staking_eras_work() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(session::SESSION_LENGTH).to_vec() => vec![].and(&1u64),
|
||||
twox_128(SESSIONS_PER_ERA).to_vec() => vec![].and(&2u64)
|
||||
twox_128(session::SessionLength::key()).to_vec() => vec![].and(&1u64),
|
||||
twox_128(SessionsPerEra::key()).to_vec() => vec![].and(&2u64),
|
||||
twox_128(ValidatorCount::key()).to_vec() => vec![].and(&2u32),
|
||||
twox_128(CurrentEra::key()).to_vec() => vec![].and(&0u64)
|
||||
];
|
||||
with_externalities(&mut t, || {
|
||||
assert_eq!(era_length(), 2u64);
|
||||
assert_eq!(sessions_per_era(), 2u64);
|
||||
assert_eq!(last_era_length_change(), 0u64);
|
||||
assert_eq!(current_era(), 0u64);
|
||||
assert_eq!(SessionsPerEra::get(), 2u64);
|
||||
assert_eq!(LastEraLengthChange::get(), 0u64);
|
||||
assert_eq!(CurrentEra::get(), 0u64);
|
||||
|
||||
// Block 1: No change.
|
||||
with_env(|e| e.block_number = 1);
|
||||
check_new_era();
|
||||
assert_eq!(sessions_per_era(), 2u64);
|
||||
assert_eq!(last_era_length_change(), 0u64);
|
||||
assert_eq!(current_era(), 0u64);
|
||||
assert_eq!(SessionsPerEra::get(), 2u64);
|
||||
assert_eq!(LastEraLengthChange::get(), 0u64);
|
||||
assert_eq!(CurrentEra::get(), 0u64);
|
||||
|
||||
// Block 2: Simple era change.
|
||||
with_env(|e| e.block_number = 2);
|
||||
check_new_era();
|
||||
assert_eq!(sessions_per_era(), 2u64);
|
||||
assert_eq!(last_era_length_change(), 0u64);
|
||||
assert_eq!(current_era(), 1u64);
|
||||
assert_eq!(SessionsPerEra::get(), 2u64);
|
||||
assert_eq!(LastEraLengthChange::get(), 0u64);
|
||||
assert_eq!(CurrentEra::get(), 1u64);
|
||||
|
||||
// Block 3: Schedule an era length change; no visible changes.
|
||||
with_env(|e| e.block_number = 3);
|
||||
PrivPass::test().set_sessions_per_era(3);
|
||||
check_new_era();
|
||||
assert_eq!(sessions_per_era(), 2u64);
|
||||
assert_eq!(last_era_length_change(), 0u64);
|
||||
assert_eq!(current_era(), 1u64);
|
||||
assert_eq!(SessionsPerEra::get(), 2u64);
|
||||
assert_eq!(LastEraLengthChange::get(), 0u64);
|
||||
assert_eq!(CurrentEra::get(), 1u64);
|
||||
|
||||
// Block 4: Era change kicks in.
|
||||
with_env(|e| e.block_number = 4);
|
||||
check_new_era();
|
||||
assert_eq!(sessions_per_era(), 3u64);
|
||||
assert_eq!(last_era_length_change(), 4u64);
|
||||
assert_eq!(current_era(), 2u64);
|
||||
assert_eq!(SessionsPerEra::get(), 3u64);
|
||||
assert_eq!(LastEraLengthChange::get(), 4u64);
|
||||
assert_eq!(CurrentEra::get(), 2u64);
|
||||
|
||||
// Block 5: No change.
|
||||
with_env(|e| e.block_number = 5);
|
||||
check_new_era();
|
||||
assert_eq!(sessions_per_era(), 3u64);
|
||||
assert_eq!(last_era_length_change(), 4u64);
|
||||
assert_eq!(current_era(), 2u64);
|
||||
assert_eq!(SessionsPerEra::get(), 3u64);
|
||||
assert_eq!(LastEraLengthChange::get(), 4u64);
|
||||
assert_eq!(CurrentEra::get(), 2u64);
|
||||
|
||||
// Block 6: No change.
|
||||
with_env(|e| e.block_number = 6);
|
||||
check_new_era();
|
||||
assert_eq!(sessions_per_era(), 3u64);
|
||||
assert_eq!(last_era_length_change(), 4u64);
|
||||
assert_eq!(current_era(), 2u64);
|
||||
assert_eq!(SessionsPerEra::get(), 3u64);
|
||||
assert_eq!(LastEraLengthChange::get(), 4u64);
|
||||
assert_eq!(CurrentEra::get(), 2u64);
|
||||
|
||||
// Block 7: Era increment.
|
||||
with_env(|e| e.block_number = 7);
|
||||
check_new_era();
|
||||
assert_eq!(sessions_per_era(), 3u64);
|
||||
assert_eq!(last_era_length_change(), 4u64);
|
||||
assert_eq!(current_era(), 3u64);
|
||||
assert_eq!(SessionsPerEra::get(), 3u64);
|
||||
assert_eq!(LastEraLengthChange::get(), 4u64);
|
||||
assert_eq!(CurrentEra::get(), 3u64);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn staking_balance_works() {
|
||||
with_externalities(&mut TestExternalities::default(), || {
|
||||
set_free_balance(&Alice, 42);
|
||||
assert_eq!(free_balance(&Alice), 42);
|
||||
assert_eq!(reserved_balance(&Alice), 0);
|
||||
FreeBalanceOf::insert(*Alice, 42);
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 42);
|
||||
assert_eq!(ReservedBalanceOf::get(*Alice), 0);
|
||||
assert_eq!(balance(&Alice), 42);
|
||||
assert_eq!(free_balance(&Bob), 0);
|
||||
assert_eq!(reserved_balance(&Bob), 0);
|
||||
assert_eq!(FreeBalanceOf::get(*Bob), 0);
|
||||
assert_eq!(ReservedBalanceOf::get(*Bob), 0);
|
||||
assert_eq!(balance(&Bob), 0);
|
||||
});
|
||||
}
|
||||
@@ -778,7 +728,7 @@ mod tests {
|
||||
#[test]
|
||||
fn staking_balance_transfer_works() {
|
||||
with_externalities(&mut testing::externalities(1, 3, 1), || {
|
||||
set_free_balance(&Alice, 112);
|
||||
FreeBalanceOf::insert(*Alice, 112);
|
||||
PublicPass::new(&Alice).transfer(Bob.to_raw_public(), 69);
|
||||
assert_eq!(balance(&Alice), 42);
|
||||
assert_eq!(balance(&Bob), 69);
|
||||
@@ -789,7 +739,7 @@ mod tests {
|
||||
#[should_panic]
|
||||
fn staking_balance_transfer_when_bonded_panics() {
|
||||
with_externalities(&mut TestExternalities::default(), || {
|
||||
set_free_balance(&Alice, 111);
|
||||
FreeBalanceOf::insert(*Alice, 111);
|
||||
PublicPass::new(&Alice).stake();
|
||||
PublicPass::new(&Alice).transfer(Bob.to_raw_public(), 69);
|
||||
});
|
||||
@@ -798,17 +748,17 @@ mod tests {
|
||||
#[test]
|
||||
fn reserving_balance_should_work() {
|
||||
with_externalities(&mut TestExternalities::default(), || {
|
||||
set_free_balance(&Alice, 111);
|
||||
FreeBalanceOf::insert(*Alice, 111);
|
||||
|
||||
assert_eq!(balance(&Alice), 111);
|
||||
assert_eq!(free_balance(&Alice), 111);
|
||||
assert_eq!(reserved_balance(&Alice), 0);
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 111);
|
||||
assert_eq!(ReservedBalanceOf::get(*Alice), 0);
|
||||
|
||||
reserve_balance(&Alice, 69);
|
||||
|
||||
assert_eq!(balance(&Alice), 111);
|
||||
assert_eq!(free_balance(&Alice), 42);
|
||||
assert_eq!(reserved_balance(&Alice), 69);
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 42);
|
||||
assert_eq!(ReservedBalanceOf::get(*Alice), 69);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -816,7 +766,7 @@ mod tests {
|
||||
#[should_panic]
|
||||
fn staking_balance_transfer_when_reserved_panics() {
|
||||
with_externalities(&mut TestExternalities::default(), || {
|
||||
set_free_balance(&Alice, 111);
|
||||
FreeBalanceOf::insert(*Alice, 111);
|
||||
reserve_balance(&Alice, 69);
|
||||
PublicPass::new(&Alice).transfer(Bob.to_raw_public(), 69);
|
||||
});
|
||||
@@ -825,17 +775,17 @@ mod tests {
|
||||
#[test]
|
||||
fn deducting_balance_should_work() {
|
||||
with_externalities(&mut TestExternalities::default(), || {
|
||||
set_free_balance(&Alice, 111);
|
||||
FreeBalanceOf::insert(*Alice, 111);
|
||||
assert!(deduct_unbonded(&Alice, 69));
|
||||
assert_eq!(free_balance(&Alice), 42);
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 42);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deducting_balance_should_fail_when_bonded() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&111u64),
|
||||
twox_128(&Alice.to_raw_public().to_keyed_vec(BONDAGE_OF)).to_vec() => vec![].and(&2u64)
|
||||
twox_128(&FreeBalanceOf::key_for(*Alice)).to_vec() => vec![].and(&111u64),
|
||||
twox_128(&BondageOf::key_for(*Alice)).to_vec() => vec![].and(&2u64)
|
||||
];
|
||||
with_externalities(&mut t, || {
|
||||
with_env(|e| e.block_number = 1);
|
||||
@@ -847,90 +797,90 @@ mod tests {
|
||||
#[test]
|
||||
fn refunding_balance_should_work() {
|
||||
with_externalities(&mut TestExternalities::default(), || {
|
||||
set_free_balance(&Alice, 42);
|
||||
FreeBalanceOf::insert(*Alice, 42);
|
||||
refund(&Alice, 69);
|
||||
assert_eq!(free_balance(&Alice), 111);
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 111);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slashing_balance_should_work() {
|
||||
with_externalities(&mut TestExternalities::default(), || {
|
||||
set_free_balance(&Alice, 111);
|
||||
FreeBalanceOf::insert(*Alice, 111);
|
||||
reserve_balance(&Alice, 69);
|
||||
assert!(slash(&Alice, 69));
|
||||
assert_eq!(free_balance(&Alice), 0);
|
||||
assert_eq!(reserved_balance(&Alice), 42);
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 0);
|
||||
assert_eq!(ReservedBalanceOf::get(*Alice), 42);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slashing_incomplete_balance_should_work() {
|
||||
with_externalities(&mut TestExternalities::default(), || {
|
||||
set_free_balance(&Alice, 42);
|
||||
FreeBalanceOf::insert(*Alice, 42);
|
||||
reserve_balance(&Alice, 21);
|
||||
assert!(!slash(&Alice, 69));
|
||||
assert_eq!(free_balance(&Alice), 0);
|
||||
assert_eq!(reserved_balance(&Alice), 0);
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 0);
|
||||
assert_eq!(ReservedBalanceOf::get(*Alice), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unreserving_balance_should_work() {
|
||||
with_externalities(&mut TestExternalities::default(), || {
|
||||
set_free_balance(&Alice, 111);
|
||||
FreeBalanceOf::insert(*Alice, 111);
|
||||
reserve_balance(&Alice, 111);
|
||||
unreserve_balance(&Alice, 42);
|
||||
assert_eq!(reserved_balance(&Alice), 69);
|
||||
assert_eq!(free_balance(&Alice), 42);
|
||||
assert_eq!(ReservedBalanceOf::get(*Alice), 69);
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 42);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slashing_reserved_balance_should_work() {
|
||||
with_externalities(&mut TestExternalities::default(), || {
|
||||
set_free_balance(&Alice, 111);
|
||||
FreeBalanceOf::insert(*Alice, 111);
|
||||
reserve_balance(&Alice, 111);
|
||||
assert!(slash_reserved(&Alice, 42));
|
||||
assert_eq!(reserved_balance(&Alice), 69);
|
||||
assert_eq!(free_balance(&Alice), 0);
|
||||
assert_eq!(ReservedBalanceOf::get(*Alice), 69);
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slashing_incomplete_reserved_balance_should_work() {
|
||||
with_externalities(&mut TestExternalities::default(), || {
|
||||
set_free_balance(&Alice, 111);
|
||||
FreeBalanceOf::insert(*Alice, 111);
|
||||
reserve_balance(&Alice, 42);
|
||||
assert!(!slash_reserved(&Alice, 69));
|
||||
assert_eq!(free_balance(&Alice), 69);
|
||||
assert_eq!(reserved_balance(&Alice), 0);
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 69);
|
||||
assert_eq!(ReservedBalanceOf::get(*Alice), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transferring_reserved_balance_should_work() {
|
||||
with_externalities(&mut TestExternalities::default(), || {
|
||||
set_free_balance(&Alice, 111);
|
||||
FreeBalanceOf::insert(*Alice, 111);
|
||||
reserve_balance(&Alice, 111);
|
||||
assert!(transfer_reserved_balance(&Alice, &Bob, 42));
|
||||
assert_eq!(reserved_balance(&Alice), 69);
|
||||
assert_eq!(free_balance(&Alice), 0);
|
||||
assert_eq!(reserved_balance(&Bob), 0);
|
||||
assert_eq!(free_balance(&Bob), 42);
|
||||
assert_eq!(ReservedBalanceOf::get(*Alice), 69);
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 0);
|
||||
assert_eq!(ReservedBalanceOf::get(*Bob), 0);
|
||||
assert_eq!(FreeBalanceOf::get(*Bob), 42);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transferring_incomplete_reserved_balance_should_work() {
|
||||
with_externalities(&mut TestExternalities::default(), || {
|
||||
set_free_balance(&Alice, 111);
|
||||
FreeBalanceOf::insert(*Alice, 111);
|
||||
reserve_balance(&Alice, 42);
|
||||
assert!(!transfer_reserved_balance(&Alice, &Bob, 69));
|
||||
assert_eq!(reserved_balance(&Alice), 0);
|
||||
assert_eq!(free_balance(&Alice), 69);
|
||||
assert_eq!(reserved_balance(&Bob), 0);
|
||||
assert_eq!(free_balance(&Bob), 42);
|
||||
assert_eq!(ReservedBalanceOf::get(*Alice), 0);
|
||||
assert_eq!(FreeBalanceOf::get(*Alice), 69);
|
||||
assert_eq!(ReservedBalanceOf::get(*Bob), 0);
|
||||
assert_eq!(FreeBalanceOf::get(*Bob), 42);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ use rstd::prelude::*;
|
||||
use rstd::mem;
|
||||
use runtime_io::{print, storage_root, enumerated_trie_root};
|
||||
use codec::{KeyedVec, Slicable};
|
||||
use runtime_support::{Hashable, storage};
|
||||
use runtime_support::{Hashable, storage, StorageValue, StorageMap};
|
||||
use environment::with_env;
|
||||
use demo_primitives::{AccountId, Hash, TxOrder, BlockNumber, Header, Log};
|
||||
use block::Block;
|
||||
@@ -30,9 +30,12 @@ use runtime::{staking, session};
|
||||
use runtime::democracy::PrivPass;
|
||||
use dispatch;
|
||||
|
||||
pub const NONCE_OF: &[u8] = b"sys:non:";
|
||||
pub const BLOCK_HASH_AT: &[u8] = b"sys:old:";
|
||||
pub const CODE: &[u8] = b"sys:cod";
|
||||
storage_items! {
|
||||
pub Nonce: b"sys:non" => default map [ AccountId => TxOrder ];
|
||||
pub BlockHashAt: b"sys:old" => required map [ BlockNumber => Hash ];
|
||||
}
|
||||
|
||||
pub const CODE: &'static[u8] = b":code";
|
||||
|
||||
|
||||
/// The current block number being processed. Set by `execute_block`.
|
||||
@@ -40,11 +43,6 @@ pub fn block_number() -> BlockNumber {
|
||||
with_env(|e| e.block_number)
|
||||
}
|
||||
|
||||
/// Get the block hash of a given block (uses storage).
|
||||
pub fn block_hash(number: BlockNumber) -> Hash {
|
||||
storage::get_or_default(&number.to_keyed_vec(BLOCK_HASH_AT))
|
||||
}
|
||||
|
||||
impl_dispatch! {
|
||||
pub mod privileged;
|
||||
fn set_code(new: Vec<u8>) = 0;
|
||||
@@ -145,12 +143,11 @@ fn execute_transaction(utx: UncheckedTransaction) {
|
||||
|
||||
{
|
||||
// check nonce
|
||||
let nonce_key = tx.signed.to_keyed_vec(NONCE_OF);
|
||||
let expected_nonce: TxOrder = storage::get_or(&nonce_key, 0);
|
||||
let expected_nonce: TxOrder = Nonce::get(&tx.signed);
|
||||
assert!(tx.nonce == expected_nonce, "All transactions should have the correct nonce");
|
||||
|
||||
// increment nonce in storage
|
||||
storage::put(&nonce_key, &(expected_nonce + 1));
|
||||
Nonce::insert(&tx.signed, &(expected_nonce + 1));
|
||||
}
|
||||
|
||||
// decode parameters and dispatch
|
||||
@@ -163,7 +160,7 @@ fn initial_checks(block: &Block) {
|
||||
|
||||
// check parent_hash is correct.
|
||||
assert!(
|
||||
header.number > 0 && block_hash(header.number - 1) == header.parent_hash,
|
||||
header.number > 0 && BlockHashAt::get(&(header.number - 1)) == header.parent_hash,
|
||||
"Parent hash should be valid."
|
||||
);
|
||||
|
||||
@@ -192,7 +189,7 @@ fn final_checks(block: &Block) {
|
||||
fn post_finalise(header: &Header) {
|
||||
// store the header hash in storage; we can't do it before otherwise there would be a
|
||||
// cyclic dependency.
|
||||
storage::put(&header.number.to_keyed_vec(BLOCK_HASH_AT), &header.blake2_256());
|
||||
BlockHashAt::insert(&header.number, &header.blake2_256().into());
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
@@ -220,7 +217,7 @@ pub mod testing {
|
||||
|
||||
pub fn externalities() -> TestExternalities {
|
||||
map![
|
||||
twox_128(&0u64.to_keyed_vec(BLOCK_HASH_AT)).to_vec() => [69u8; 32].encode()
|
||||
twox_128(&BlockHashAt::key_for(&0)).to_vec() => [69u8; 32].encode()
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -231,6 +228,7 @@ mod tests {
|
||||
use super::internal::*;
|
||||
|
||||
use runtime_io::{with_externalities, twox_128, TestExternalities};
|
||||
use runtime_support::StorageValue;
|
||||
use codec::{Joiner, KeyedVec, Slicable};
|
||||
use keyring::Keyring::*;
|
||||
use environment::with_env;
|
||||
@@ -244,8 +242,8 @@ mod tests {
|
||||
#[test]
|
||||
fn staking_balance_transfer_dispatch_works() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(&One.to_raw_public().to_keyed_vec(staking::BALANCE_OF)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TRANSACTION_FEE).to_vec() => vec![10u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
twox_128(&staking::FreeBalanceOf::key_for(*One)).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0],
|
||||
twox_128(staking::TransactionFee::key()).to_vec() => vec![10u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
];
|
||||
|
||||
let tx = UncheckedTransaction {
|
||||
@@ -275,7 +273,7 @@ mod tests {
|
||||
let h = Header {
|
||||
parent_hash: [69u8; 32].into(),
|
||||
number: 1,
|
||||
state_root: hex!("584e0c1f4d4b96153591e3906d756762493dffeb5fa7159e7107014aec8d9c3d").into(),
|
||||
state_root: hex!("cc3f1f5db826013193e502c76992b5e933b12367e37a269a9822b89218323e9f").into(),
|
||||
transaction_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(),
|
||||
digest: Digest { logs: vec![], },
|
||||
};
|
||||
|
||||
@@ -16,16 +16,13 @@
|
||||
|
||||
//! Timestamp manager: just handles the current timestamp.
|
||||
|
||||
use runtime_support::storage;
|
||||
use runtime_support::storage::StorageValue;
|
||||
use runtime::staking::PublicPass;
|
||||
|
||||
pub type Timestamp = u64;
|
||||
|
||||
pub const CURRENT_TIMESTAMP: &[u8] = b"tim:val";
|
||||
|
||||
/// Get the current time.
|
||||
pub fn get() -> Timestamp {
|
||||
storage::get_or_default(CURRENT_TIMESTAMP)
|
||||
storage_items! {
|
||||
pub Now: b"tim:val" => required Timestamp;
|
||||
}
|
||||
|
||||
impl_dispatch! {
|
||||
@@ -36,7 +33,7 @@ impl_dispatch! {
|
||||
impl<'a> public::Dispatch for PublicPass<'a> {
|
||||
/// Set the current time.
|
||||
fn set(self, now: Timestamp) {
|
||||
storage::put(CURRENT_TIMESTAMP, &now);
|
||||
Now::put(&now);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +43,7 @@ mod tests {
|
||||
use super::public::*;
|
||||
|
||||
use runtime_io::{with_externalities, twox_128, TestExternalities};
|
||||
use runtime_support::storage::StorageValue;
|
||||
use runtime::timestamp;
|
||||
use codec::{Joiner, KeyedVec};
|
||||
use demo_primitives::AccountId;
|
||||
@@ -54,13 +52,13 @@ mod tests {
|
||||
#[test]
|
||||
fn timestamp_works() {
|
||||
let mut t: TestExternalities = map![
|
||||
twox_128(CURRENT_TIMESTAMP).to_vec() => vec![].and(&42u64)
|
||||
twox_128(Now::key()).to_vec() => vec![].and(&42u64)
|
||||
];
|
||||
|
||||
with_externalities(&mut t, || {
|
||||
assert_eq!(get(), 42);
|
||||
assert_eq!(Now::get(), 42);
|
||||
PublicPass::nobody().set(69);
|
||||
assert_eq!(get(), 69);
|
||||
assert_eq!(Now::get(), 69);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -29,5 +29,5 @@ pub use self::storage::generator::Storage as GenericStorage;
|
||||
pub mod storage;
|
||||
mod hashable;
|
||||
|
||||
pub use self::storage::StorageVec;
|
||||
pub use self::storage::{StorageVec, StorageList, StorageValue, StorageMap};
|
||||
pub use self::hashable::Hashable;
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
//!
|
||||
//! storage_items! {
|
||||
//! // public value
|
||||
//! pub Value: b"stored_key" => SessionKey;
|
||||
//! pub Value: b"putd_key" => SessionKey;
|
||||
//! // private map.
|
||||
//! Balances: b"private_map:" => map [AuthorityId => Balance];
|
||||
//! // private list.
|
||||
@@ -48,38 +48,73 @@
|
||||
|
||||
use codec;
|
||||
use rstd::vec::Vec;
|
||||
#[doc(hidden)]
|
||||
pub use rstd::borrow::Borrow;
|
||||
|
||||
/// Abstraction around storage.
|
||||
pub trait Storage {
|
||||
/// true if the key exists in storage.
|
||||
fn exists(&self, key: &[u8]) -> bool;
|
||||
|
||||
/// Load the bytes of a key from storage. Can panic if the type is incorrect.
|
||||
fn load<T: codec::Slicable>(&self, key: &[u8]) -> Option<T>;
|
||||
fn get<T: codec::Slicable>(&self, key: &[u8]) -> Option<T>;
|
||||
|
||||
/// Load the bytes of a key from storage. Can panic if the type is incorrect. Will panic if
|
||||
/// it's not there.
|
||||
fn require<T: codec::Slicable>(&self, key: &[u8]) -> T { self.get(key).expect("Required values must be in storage") }
|
||||
|
||||
/// Load the bytes of a key from storage. Can panic if the type is incorrect. The type's
|
||||
/// default is returned if it's not there.
|
||||
fn get_or_default<T: codec::Slicable + Default>(&self, key: &[u8]) -> T { self.get(key).unwrap_or_default() }
|
||||
|
||||
/// Put a value in under a key.
|
||||
fn store<T: codec::Slicable>(&self, key: &[u8], val: &T);
|
||||
fn put<T: codec::Slicable>(&self, key: &[u8], val: &T);
|
||||
|
||||
/// Remove the bytes of a key from storage.
|
||||
fn kill(&self, key: &[u8]);
|
||||
|
||||
/// Take a value from storage, deleting it after reading.
|
||||
fn take<T: codec::Slicable>(&self, key: &[u8]) -> Option<T> {
|
||||
let value = self.load(key);
|
||||
let value = self.get(key);
|
||||
self.kill(key);
|
||||
value
|
||||
}
|
||||
|
||||
/// Take a value from storage, deleting it after reading.
|
||||
fn take_or_panic<T: codec::Slicable>(&self, key: &[u8]) -> T { self.take(key).expect("Required values must be in storage") }
|
||||
|
||||
/// Take a value from storage, deleting it after reading.
|
||||
fn take_or_default<T: codec::Slicable + Default>(&self, key: &[u8]) -> T { self.take(key).unwrap_or_default() }
|
||||
}
|
||||
|
||||
/// A strongly-typed value kept in storage.
|
||||
pub trait StorageValue<T: codec::Slicable> {
|
||||
/// The type that get/take returns.
|
||||
type Query;
|
||||
|
||||
/// Get the storage key.
|
||||
fn key() -> &'static [u8];
|
||||
|
||||
/// true if the value is defined in storage.
|
||||
fn exists<S: Storage>(storage: &S) -> bool {
|
||||
storage.exists(Self::key())
|
||||
}
|
||||
|
||||
/// Load the value from the provided storage instance.
|
||||
fn load<S: Storage>(storage: &S) -> Option<T>;
|
||||
/// Store a value under this key into the provded storage instance.
|
||||
fn store<S: Storage>(val: &T, storage: &S);
|
||||
/// Clear the storage value.
|
||||
fn kill<S: Storage>(storage: &S);
|
||||
fn get<S: Storage>(storage: &S) -> Self::Query;
|
||||
|
||||
/// Take a value from storage, removing it afterwards.
|
||||
fn take<S: Storage>(storage: &S) -> Option<T>;
|
||||
fn take<S: Storage>(storage: &S) -> Self::Query;
|
||||
|
||||
/// Store a value under this key into the provded storage instance.
|
||||
fn put<S: Storage>(val: &T, storage: &S) {
|
||||
storage.put(Self::key(), val)
|
||||
}
|
||||
|
||||
/// Clear the storage value.
|
||||
fn kill<S: Storage>(storage: &S) {
|
||||
storage.kill(Self::key())
|
||||
}
|
||||
}
|
||||
|
||||
/// A strongly-typed list in storage.
|
||||
@@ -87,7 +122,7 @@ pub trait StorageList<T: codec::Slicable> {
|
||||
/// Get the prefix key in storage.
|
||||
fn prefix() -> &'static [u8];
|
||||
|
||||
/// Get the key used to store the length field.
|
||||
/// Get the key used to put the length field.
|
||||
fn len_key() -> Vec<u8>;
|
||||
|
||||
/// Get the storage key used to fetch a value at a given index.
|
||||
@@ -114,225 +149,119 @@ pub trait StorageList<T: codec::Slicable> {
|
||||
|
||||
/// A strongly-typed map in storage.
|
||||
pub trait StorageMap<K: codec::Slicable, V: codec::Slicable> {
|
||||
/// The type that get/take returns.
|
||||
type Query;
|
||||
|
||||
/// Get the prefix key in storage.
|
||||
fn prefix() -> &'static [u8];
|
||||
|
||||
/// Get the storage key used to fetch a value corresponding to a specific key.
|
||||
fn key_for(x: &K) -> Vec<u8>;
|
||||
|
||||
/// true if the value is defined in storage.
|
||||
fn exists<S: Storage>(key: &K, storage: &S) -> bool {
|
||||
storage.exists(&Self::key_for(key)[..])
|
||||
}
|
||||
|
||||
/// Load the value associated with the given key from the map.
|
||||
fn get<S: Storage>(key: &K, storage: &S) -> Option<V>;
|
||||
|
||||
/// Store a value to be associated with the given key from the map.
|
||||
fn insert<S: Storage>(key: &K, val: &V, storage: &S);
|
||||
|
||||
/// Remove the value under a key.
|
||||
fn remove<S: Storage>(key: &K, storage: &S);
|
||||
fn get<S: Storage>(key: &K, storage: &S) -> Self::Query;
|
||||
|
||||
/// Take the value under a key.
|
||||
fn take<S: Storage>(key: &K, storage: &S) -> Option<V>;
|
||||
fn take<S: Storage>(key: &K, storage: &S) -> Self::Query;
|
||||
|
||||
/// Store a value to be associated with the given key from the map.
|
||||
fn insert<S: Storage>(key: &K, val: &V, storage: &S) {
|
||||
storage.put(&Self::key_for(key)[..], val);
|
||||
}
|
||||
|
||||
/// Remove the value under a key.
|
||||
fn remove<S: Storage>(key: &K, storage: &S) {
|
||||
storage.kill(&Self::key_for(key)[..]);
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __storage_items_internal {
|
||||
// generator for values.
|
||||
(($($vis:tt)*) $name: ident: $key: expr => $ty:ty) => {
|
||||
(($($vis:tt)*) ($get_fn:ident) ($gettype:ty) ($getter:ident) ($taker:ident) $name:ident : $key:expr => $ty:ty) => {
|
||||
__storage_items_internal!{ ($($vis)*) () ($gettype) ($getter) ($taker) $name : $key => $ty }
|
||||
pub fn $get_fn() -> $gettype { <$name as $crate::storage::generator::StorageValue<$ty>> :: get(&$crate::storage::RuntimeStorage) }
|
||||
};
|
||||
(($($vis:tt)*) () ($gettype:ty) ($getter:ident) ($taker:ident) $name:ident : $key:expr => $ty:ty) => {
|
||||
$($vis)* struct $name;
|
||||
|
||||
#[allow(unused)]
|
||||
impl $name {
|
||||
/// Get the storage key.
|
||||
$($vis)* fn key() -> &'static [u8] {
|
||||
$key
|
||||
}
|
||||
|
||||
/// Load the value from the provided storage instance.
|
||||
$($vis)* fn load<S: $crate::GenericStorage>(storage: &S) -> Option<$ty> {
|
||||
storage.load($key)
|
||||
}
|
||||
|
||||
/// Store a value under this key into the provded storage instance.
|
||||
$($vis)* fn store<S: $crate::GenericStorage>(val: &$ty, storage: &S) {
|
||||
storage.store($key, val)
|
||||
}
|
||||
|
||||
/// Kill the value.
|
||||
$($vis)* fn kill<S: $crate::GenericStorage>(storage: &S) {
|
||||
storage.kill($key)
|
||||
}
|
||||
|
||||
/// Take and remove the value from the provided storage instance.
|
||||
$($vis)* fn take<S: $crate::GenericStorage>(storage: &S) -> Option<$ty> {
|
||||
storage.take($key)
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::storage::generator::StorageValue<$ty> for $name {
|
||||
type Query = $gettype;
|
||||
|
||||
/// Get the storage key.
|
||||
fn key() -> &'static [u8] {
|
||||
$key
|
||||
}
|
||||
|
||||
fn load<S: $crate::GenericStorage>(storage: &S) -> Option<$ty> {
|
||||
$name::load(storage)
|
||||
/// Load the value from the provided storage instance.
|
||||
fn get<S: $crate::GenericStorage>(storage: &S) -> Self::Query {
|
||||
storage.$getter($key)
|
||||
}
|
||||
|
||||
fn store<S: $crate::GenericStorage>(val: &$ty, storage: &S) {
|
||||
$name::store(val, storage)
|
||||
}
|
||||
|
||||
fn kill<S: $crate::GenericStorage>(storage: &S) {
|
||||
$name::kill(storage)
|
||||
}
|
||||
|
||||
fn take<S: $crate::GenericStorage>(storage: &S) -> Option<$ty> {
|
||||
$name::take(storage)
|
||||
/// Take a value from storage, removing it afterwards.
|
||||
fn take<S: $crate::GenericStorage>(storage: &S) -> Self::Query {
|
||||
storage.$taker($key)
|
||||
}
|
||||
}
|
||||
};
|
||||
// generator for maps.
|
||||
(($($vis:tt)*) $name: ident: $prefix: expr => map [$kty: ty => $ty:ty]) => {
|
||||
(($($vis:tt)*) ($get_fn:ident) ($gettype:ty) ($getter:ident) ($taker:ident) $name:ident : $prefix:expr => map [$kty:ty => $ty:ty]) => {
|
||||
__storage_items_internal!{ ($($vis)*) () ($gettype) ($getter) ($taker) $name : $prefix => map [$kty => $ty] }
|
||||
pub fn $get_fn<K: $crate::storage::generator::Borrow<$kty>>(key: K) -> $gettype {
|
||||
<$name as $crate::storage::generator::StorageMap<$kty, $ty>> :: get(key.borrow(), &$crate::storage::RuntimeStorage)
|
||||
}
|
||||
};
|
||||
(($($vis:tt)*) () ($gettype:ty) ($getter:ident) ($taker:ident) $name:ident : $prefix:expr => map [$kty:ty => $ty:ty]) => {
|
||||
$($vis)* struct $name;
|
||||
|
||||
#[allow(unused)]
|
||||
impl $name {
|
||||
impl $crate::storage::generator::StorageMap<$kty, $ty> for $name {
|
||||
type Query = $gettype;
|
||||
|
||||
/// Get the prefix key in storage.
|
||||
$($vis)* fn prefix() -> &'static [u8] {
|
||||
fn prefix() -> &'static [u8] {
|
||||
$prefix
|
||||
}
|
||||
|
||||
/// Get the storage key used to fetch a value corresponding to a specific key.
|
||||
$($vis)* fn key_for(x: &$kty) -> Vec<u8> {
|
||||
fn key_for(x: &$kty) -> Vec<u8> {
|
||||
let mut key = $prefix.to_vec();
|
||||
key.extend($crate::codec::Slicable::encode(x));
|
||||
key
|
||||
}
|
||||
|
||||
/// Load the value associated with the given key from the map.
|
||||
$($vis)* fn get<S: $crate::GenericStorage>(key: &$kty, storage: &S) -> Option<$ty> {
|
||||
let key = $name::key_for(key);
|
||||
storage.load(&key[..])
|
||||
}
|
||||
|
||||
/// Store a value to be associated with the given key from the map.
|
||||
$($vis)* fn insert<S: $crate::GenericStorage>(key: &$kty, val: &$ty, storage: &S) {
|
||||
let key = $name::key_for(key);
|
||||
storage.store(&key[..], val);
|
||||
}
|
||||
|
||||
/// Remove the value from storage.
|
||||
$($vis)* fn remove<S: $crate::GenericStorage>(key: &$kty, storage: &S) {
|
||||
storage.kill(&$name::key_for(key)[..]);
|
||||
fn get<S: $crate::GenericStorage>(key: &$kty, storage: &S) -> Self::Query {
|
||||
let key = <$name as $crate::storage::generator::StorageMap<$kty, $ty>>::key_for(key);
|
||||
storage.$getter(&key[..])
|
||||
}
|
||||
|
||||
/// Take the value, reading and removing it.
|
||||
$($vis)* fn take<S: $crate::GenericStorage>(key: &$kty, storage: &S) -> Option<$ty> {
|
||||
let key = $name::key_for(key);
|
||||
storage.take(&key[..])
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::storage::generator::StorageMap<$kty, $ty> for $name {
|
||||
fn prefix() -> &'static [u8] {
|
||||
$prefix
|
||||
}
|
||||
|
||||
fn key_for(x: &$kty) -> Vec<u8> {
|
||||
$name::key_for(x)
|
||||
}
|
||||
|
||||
fn get<S: $crate::GenericStorage>(key: &$kty, storage: &S) -> Option<$ty> {
|
||||
$name::get(key, storage)
|
||||
}
|
||||
|
||||
fn insert<S: $crate::GenericStorage>(key: &$kty, val: &$ty, storage: &S) {
|
||||
$name::insert(key, val, storage)
|
||||
}
|
||||
|
||||
fn remove<S: $crate::GenericStorage>(key: &$kty, storage: &S) {
|
||||
$name::remove(key, storage)
|
||||
}
|
||||
|
||||
fn take<S: $crate::GenericStorage>(key: &$kty, storage: &S) -> Option<$ty> {
|
||||
$name::take(key, storage)
|
||||
fn take<S: $crate::GenericStorage>(key: &$kty, storage: &S) -> Self::Query {
|
||||
let key = <$name as $crate::storage::generator::StorageMap<$kty, $ty>>::key_for(key);
|
||||
storage.$taker(&key[..])
|
||||
}
|
||||
}
|
||||
};
|
||||
// generator for lists.
|
||||
(($($vis:tt)*) $name: ident: $prefix: expr => list [$ty:ty]) => {
|
||||
(($($vis:tt)*) $name:ident : $prefix:expr => list [$ty:ty]) => {
|
||||
$($vis)* struct $name;
|
||||
|
||||
#[allow(unused)]
|
||||
impl $name {
|
||||
/// Get the prefix key in storage.
|
||||
$($vis)* fn prefix() -> &'static [u8] {
|
||||
$prefix
|
||||
}
|
||||
|
||||
/// Get the key used to store the length field.
|
||||
// TODO: concat macro should accept byte literals.
|
||||
$($vis)* fn len_key() -> Vec<u8> {
|
||||
let mut key = $prefix.to_vec();
|
||||
key.extend(b"len");
|
||||
key
|
||||
}
|
||||
|
||||
/// Get the storage key used to fetch a value at a given index.
|
||||
$($vis)* fn key_for(index: u32) -> Vec<u8> {
|
||||
let mut key = $prefix.to_vec();
|
||||
key.extend($crate::codec::Slicable::encode(&index));
|
||||
key
|
||||
}
|
||||
|
||||
/// Read out all the items.
|
||||
$($vis)* fn items<S: $crate::GenericStorage>(storage: &S) -> Vec<$ty> {
|
||||
(0..$name::len(storage))
|
||||
.map(|i| $name::get(i, storage).expect("all items within length are set; qed"))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Set the current set of items.
|
||||
$($vis)* fn set_items<S: $crate::GenericStorage>(items: &[$ty], storage: &S) {
|
||||
$name::set_len(items.len() as u32, storage);
|
||||
items.iter()
|
||||
.enumerate()
|
||||
.for_each(|(i, item)| $name::set_item(i as u32, item, storage));
|
||||
}
|
||||
|
||||
$($vis)* fn set_item<S: $crate::GenericStorage>(index: u32, item: &$ty, storage: &S) {
|
||||
if index < $name::len(storage) {
|
||||
storage.store(&$name::key_for(index)[..], item);
|
||||
}
|
||||
}
|
||||
|
||||
/// Load the value at given index. Returns `None` if the index is out-of-bounds.
|
||||
$($vis)* fn get<S: $crate::GenericStorage>(index: u32, storage: &S) -> Option<$ty> {
|
||||
storage.load(&$name::key_for(index)[..])
|
||||
}
|
||||
|
||||
/// Load the length of the list.
|
||||
$($vis)* fn len<S: $crate::GenericStorage>(storage: &S) -> u32 {
|
||||
storage.load(&$name::len_key()).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Clear the list.
|
||||
$($vis)* fn clear<S: $crate::GenericStorage>(storage: &S) {
|
||||
for i in 0..$name::len(storage) {
|
||||
$name::clear_item(i, storage);
|
||||
}
|
||||
|
||||
storage.kill(&$name::len_key()[..])
|
||||
}
|
||||
|
||||
fn clear_item<S: $crate::GenericStorage>(index: u32, storage: &S) {
|
||||
if index < $name::len(storage) {
|
||||
storage.kill(&$name::key_for(index));
|
||||
if index < <$name as $crate::storage::generator::StorageList<$ty>>::len(storage) {
|
||||
storage.kill(&<$name as $crate::storage::generator::StorageList<$ty>>::key_for(index));
|
||||
}
|
||||
}
|
||||
|
||||
fn set_len<S: $crate::GenericStorage>(count: u32, storage: &S) {
|
||||
(count..$name::len(storage)).for_each(|i| $name::clear_item(i, storage));
|
||||
storage.store(&$name::len_key(), &count);
|
||||
(count..<$name as $crate::storage::generator::StorageList<$ty>>::len(storage)).for_each(|i| $name::clear_item(i, storage));
|
||||
storage.put(&<$name as $crate::storage::generator::StorageList<$ty>>::len_key(), &count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,42 +271,59 @@ macro_rules! __storage_items_internal {
|
||||
$prefix
|
||||
}
|
||||
|
||||
/// Get the key used to store the length field.
|
||||
/// Get the key used to put the length field.
|
||||
// TODO: concat macro should accept byte literals.
|
||||
fn len_key() -> Vec<u8> {
|
||||
$name::len_key()
|
||||
let mut key = $prefix.to_vec();
|
||||
key.extend(b"len");
|
||||
key
|
||||
}
|
||||
|
||||
/// Get the storage key used to fetch a value at a given index.
|
||||
fn key_for(index: u32) -> Vec<u8> {
|
||||
$name::key_for(index)
|
||||
let mut key = $prefix.to_vec();
|
||||
key.extend($crate::codec::Slicable::encode(&index));
|
||||
key
|
||||
}
|
||||
|
||||
/// Read out all the items.
|
||||
fn items<S: $crate::GenericStorage>(storage: &S) -> Vec<$ty> {
|
||||
$name::items(storage)
|
||||
(0..<$name as $crate::storage::generator::StorageList<$ty>>::len(storage))
|
||||
.map(|i| <$name as $crate::storage::generator::StorageList<$ty>>::get(i, storage).expect("all items within length are set; qed"))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Set the current set of items.
|
||||
fn set_items<S: $crate::GenericStorage>(items: &[$ty], storage: &S) {
|
||||
$name::set_items(items, storage)
|
||||
$name::set_len(items.len() as u32, storage);
|
||||
items.iter()
|
||||
.enumerate()
|
||||
.for_each(|(i, item)| <$name as $crate::storage::generator::StorageList<$ty>>::set_item(i as u32, item, storage));
|
||||
}
|
||||
|
||||
fn set_item<S: $crate::GenericStorage>(index: u32, item: &$ty, storage: &S) {
|
||||
$name::set_item(index, item, storage)
|
||||
if index < <$name as $crate::storage::generator::StorageList<$ty>>::len(storage) {
|
||||
storage.put(&<$name as $crate::storage::generator::StorageList<$ty>>::key_for(index)[..], item);
|
||||
}
|
||||
}
|
||||
|
||||
/// Load the value at given index. Returns `None` if the index is out-of-bounds.
|
||||
fn get<S: $crate::GenericStorage>(index: u32, storage: &S) -> Option<$ty> {
|
||||
$name::get(index, storage)
|
||||
storage.get(&<$name as $crate::storage::generator::StorageList<$ty>>::key_for(index)[..])
|
||||
}
|
||||
|
||||
/// Load the length of the list.
|
||||
fn len<S: $crate::GenericStorage>(storage: &S) -> u32 {
|
||||
$name::len(storage)
|
||||
storage.get(&<$name as $crate::storage::generator::StorageList<$ty>>::len_key()).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Clear the list.
|
||||
fn clear<S: $crate::GenericStorage>(storage: &S) {
|
||||
$name::clear(storage)
|
||||
for i in 0..<$name as $crate::storage::generator::StorageList<$ty>>::len(storage) {
|
||||
$name::clear_item(i, storage);
|
||||
}
|
||||
|
||||
storage.kill(&<$name as $crate::storage::generator::StorageList<$ty>>::len_key()[..])
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -387,29 +333,114 @@ macro_rules! __storage_items_internal {
|
||||
#[macro_export]
|
||||
macro_rules! storage_items {
|
||||
// simple values
|
||||
($name: ident: $key: expr => $ty:ty; $($t:tt)*) => {
|
||||
__storage_items_internal!(() $name: $key => $ty);
|
||||
($name:ident : $key:expr => $ty:ty; $($t:tt)*) => {
|
||||
__storage_items_internal!(() () (Option<$ty>) (get) (take) $name: $key => $ty);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
(pub $name: ident: $key: expr => $ty:ty; $($t:tt)*) => {
|
||||
__storage_items_internal!((pub) $name: $key => $ty);
|
||||
(pub $name:ident : $key:expr => $ty:ty; $($t:tt)*) => {
|
||||
__storage_items_internal!((pub) () (Option<$ty>) (get) (take) $name: $key => $ty);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
($name:ident : $key:expr => default $ty:ty; $($t:tt)*) => {
|
||||
__storage_items_internal!(() () ($ty) (get_or_default) (take_or_default) $name: $key => $ty);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident : $key:expr => default $ty:ty; $($t:tt)*) => {
|
||||
__storage_items_internal!((pub) () ($ty) (get_or_default) (take_or_default) $name: $key => $ty);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
($name:ident : $key:expr => required $ty:ty; $($t:tt)*) => {
|
||||
__storage_items_internal!(() () ($ty) (require) (take_or_panic) $name: $key => $ty);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident : $key:expr => required $ty:ty; $($t:tt)*) => {
|
||||
__storage_items_internal!((pub) () ($ty) (require) (take_or_panic) $name: $key => $ty);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
|
||||
($name:ident get($getfn:ident) : $key:expr => $ty:ty; $($t:tt)*) => {
|
||||
__storage_items_internal!(() ($getfn) (Option<$ty>) (get) (take) $name: $key => $ty);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident get($getfn:ident) : $key:expr => $ty:ty; $($t:tt)*) => {
|
||||
__storage_items_internal!((pub) ($getfn) (Option<$ty>) (get) (take) $name: $key => $ty);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
($name:ident get($getfn:ident) : $key:expr => default $ty:ty; $($t:tt)*) => {
|
||||
__storage_items_internal!(() ($getfn) ($ty) (get_or_default) (take_or_default) $name: $key => $ty);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident get($getfn:ident) : $key:expr => default $ty:ty; $($t:tt)*) => {
|
||||
__storage_items_internal!((pub) ($getfn) ($ty) (get_or_default) (take_or_default) $name: $key => $ty);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
($name:ident get($getfn:ident) : $key:expr => required $ty:ty; $($t:tt)*) => {
|
||||
__storage_items_internal!(() ($getfn) ($ty) (require) (take_or_panic) $name: $key => $ty);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident get($getfn:ident) : $key:expr => required $ty:ty; $($t:tt)*) => {
|
||||
__storage_items_internal!((pub) ($getfn) ($ty) (require) (take_or_panic) $name: $key => $ty);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
|
||||
// maps
|
||||
($name: ident: $prefix: expr => map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!(() $name: $prefix => map [$kty => $ty]);
|
||||
($name:ident : $prefix:expr => map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!(() () (Option<$ty>) (get) (take) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
(pub $name: ident: $prefix: expr => map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!((pub) $name: $prefix => map [$kty => $ty]);
|
||||
(pub $name:ident : $prefix:expr => map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!((pub) () (Option<$ty>) (get) (take) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
($name:ident : $prefix:expr => default map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!(() () ($ty) (get_or_default) (take_or_default) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident : $prefix:expr => default map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!((pub) () ($ty) (get_or_default) (take_or_default) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
($name:ident : $prefix:expr => required map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!(() () ($ty) (require) (take_or_panic) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident : $prefix:expr => required map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!((pub) () ($ty) (require) (take_or_panic) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
|
||||
($name:ident get($getfn:ident) : $prefix:expr => map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!(() ($getfn) (Option<$ty>) (get) (take) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident get($getfn:ident) : $prefix:expr => map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!((pub) ($getfn) (Option<$ty>) (get) (take) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
($name:ident get($getfn:ident) : $prefix:expr => default map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!(() ($getfn) ($ty) (get_or_default) (take_or_default) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident get($getfn:ident) : $prefix:expr => default map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!((pub) ($getfn) ($ty) (get_or_default) (take_or_default) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
($name:ident get($getfn:ident) : $prefix:expr => required map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!(() ($getfn) ($ty) (require) (take_or_panic) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
(pub $name:ident get($getfn:ident) : $prefix:expr => required map [$kty: ty => $ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!((pub) ($getfn) ($ty) (require) (take_or_panic) $name: $prefix => map [$kty => $ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
|
||||
|
||||
// lists
|
||||
($name: ident: $prefix: expr => list [$ty:ty]; $($t:tt)*) => {
|
||||
($name:ident : $prefix:expr => list [$ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!(() $name: $prefix => list [$ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
(pub $name: ident: $prefix: expr => list [$ty:ty]; $($t:tt)*) => {
|
||||
(pub $name:ident : $prefix:expr => list [$ty:ty]; $($t:tt)*) => {
|
||||
__storage_items_internal!((pub) $name: $prefix => list [$ty]);
|
||||
storage_items!($($t)*);
|
||||
};
|
||||
@@ -424,11 +455,15 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
impl Storage for RefCell<HashMap<Vec<u8>, Vec<u8>>> {
|
||||
fn load<T: Slicable>(&self, key: &[u8]) -> Option<T> {
|
||||
fn exists(&self, key: &[u8]) -> bool {
|
||||
self.borrow_mut().get(key).is_some()
|
||||
}
|
||||
|
||||
fn get<T: Slicable>(&self, key: &[u8]) -> Option<T> {
|
||||
self.borrow_mut().get(key).map(|v| T::decode(&mut &v[..]).unwrap())
|
||||
}
|
||||
|
||||
fn store<T: Slicable>(&self, key: &[u8], val: &T) {
|
||||
fn put<T: Slicable>(&self, key: &[u8], val: &T) {
|
||||
self.borrow_mut().insert(key.to_owned(), val.encode());
|
||||
}
|
||||
|
||||
@@ -443,14 +478,14 @@ mod tests {
|
||||
Map: b"c:" => map [u32 => [u8; 32]];
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[test]
|
||||
fn value() {
|
||||
let storage = RefCell::new(HashMap::new());
|
||||
assert!(Value::load(&storage).is_none());
|
||||
Value::store(&100_000, &storage);
|
||||
assert_eq!(Value::load(&storage), Some(100_000));
|
||||
assert!(Value::get(&storage).is_none());
|
||||
Value::put(&100_000, &storage);
|
||||
assert_eq!(Value::get(&storage), Some(100_000));
|
||||
Value::kill(&storage);
|
||||
assert!(Value::load(&storage).is_none());
|
||||
assert!(Value::get(&storage).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -47,7 +47,7 @@ pub fn get<T: Slicable + Sized>(key: &[u8]) -> Option<T> {
|
||||
key: &key[..],
|
||||
pos: 0,
|
||||
};
|
||||
Slicable::decode(&mut input).expect("stroage is not null, therefore must be a valid type")
|
||||
Slicable::decode(&mut input).expect("storage is not null, therefore must be a valid type")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -122,58 +122,78 @@ pub fn put_raw(key: &[u8], value: &[u8]) {
|
||||
runtime_io::set_storage(&twox_128(key)[..], value)
|
||||
}
|
||||
|
||||
struct RuntimeStorage;
|
||||
/// The underlying runtime storage.
|
||||
pub struct RuntimeStorage;
|
||||
|
||||
impl ::GenericStorage for RuntimeStorage {
|
||||
fn exists(&self, key: &[u8]) -> bool {
|
||||
super::storage::exists(key)
|
||||
}
|
||||
|
||||
/// Load the bytes of a key from storage. Can panic if the type is incorrect.
|
||||
fn load<T: Slicable>(&self, key: &[u8]) -> Option<T> {
|
||||
get(key)
|
||||
fn get<T: Slicable>(&self, key: &[u8]) -> Option<T> {
|
||||
super::storage::get(key)
|
||||
}
|
||||
|
||||
/// Put a value in under a key.
|
||||
fn store<T: Slicable>(&self, key: &[u8], val: &T) {
|
||||
put(key, val)
|
||||
fn put<T: Slicable>(&self, key: &[u8], val: &T) {
|
||||
super::storage::put(key, val)
|
||||
}
|
||||
|
||||
/// Remove the bytes of a key from storage.
|
||||
fn kill(&self, key: &[u8]) {
|
||||
kill(key)
|
||||
super::storage::kill(key)
|
||||
}
|
||||
|
||||
/// Take a value from storage, deleting it after reading.
|
||||
fn take<T: Slicable>(&self, key: &[u8]) -> Option<T> {
|
||||
take(key)
|
||||
super::storage::take(key)
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for working with macro-generated storage values under the substrate storage API.
|
||||
pub trait StorageValue<T: Slicable> {
|
||||
/// The type that get/take return.
|
||||
type Query;
|
||||
|
||||
/// Get the storage key.
|
||||
fn key() -> &'static [u8];
|
||||
|
||||
/// Does the value (explicitly) exist in storage?
|
||||
fn exists() -> bool;
|
||||
|
||||
/// Load the value from the provided storage instance.
|
||||
fn load() -> Option<T>;
|
||||
fn get() -> Self::Query;
|
||||
|
||||
/// Store a value under this key into the provded storage instance.
|
||||
fn store(val: &T);
|
||||
fn put<Arg: Borrow<T>>(val: Arg);
|
||||
|
||||
/// Clear the storage value.
|
||||
fn kill();
|
||||
|
||||
/// Take a value from storage, removing it afterwards.
|
||||
fn take() -> Option<T>;
|
||||
fn take() -> Self::Query;
|
||||
}
|
||||
|
||||
impl<T: Slicable, U> StorageValue<T> for U where U: generator::StorageValue<T> {
|
||||
type Query = U::Query;
|
||||
|
||||
fn key() -> &'static [u8] {
|
||||
<U as generator::StorageValue<T>>::key()
|
||||
}
|
||||
fn load() -> Option<T> {
|
||||
U::load(&RuntimeStorage)
|
||||
fn exists() -> bool {
|
||||
U::exists(&RuntimeStorage)
|
||||
}
|
||||
fn store(val: &T) {
|
||||
U::store(val, &RuntimeStorage)
|
||||
fn get() -> Self::Query {
|
||||
U::get(&RuntimeStorage)
|
||||
}
|
||||
fn put<Arg: Borrow<T>>(val: Arg) {
|
||||
U::put(val.borrow(), &RuntimeStorage)
|
||||
}
|
||||
fn kill() {
|
||||
U::kill(&RuntimeStorage)
|
||||
}
|
||||
fn take() -> Option<T> {
|
||||
fn take() -> Self::Query {
|
||||
U::take(&RuntimeStorage)
|
||||
}
|
||||
}
|
||||
@@ -196,7 +216,7 @@ pub trait StorageList<T: Slicable> {
|
||||
fn set_items(items: &[T]);
|
||||
|
||||
/// Set the item at the given index.
|
||||
fn set_item(index: u32, item: &T);
|
||||
fn set_item<Arg: Borrow<T>>(index: u32, val: Arg);
|
||||
|
||||
/// Load the value at given index. Returns `None` if the index is out-of-bounds.
|
||||
fn get(index: u32) -> Option<T>;
|
||||
@@ -229,8 +249,8 @@ impl<T: Slicable, U> StorageList<T> for U where U: generator::StorageList<T> {
|
||||
U::set_items(items, &RuntimeStorage)
|
||||
}
|
||||
|
||||
fn set_item(index: u32, item: &T) {
|
||||
U::set_item(index, item, &RuntimeStorage)
|
||||
fn set_item<Arg: Borrow<T>>(index: u32, val: Arg) {
|
||||
U::set_item(index, val.borrow(), &RuntimeStorage)
|
||||
}
|
||||
|
||||
fn get(index: u32) -> Option<T> {
|
||||
@@ -248,48 +268,60 @@ impl<T: Slicable, U> StorageList<T> for U where U: generator::StorageList<T> {
|
||||
|
||||
/// A strongly-typed map in storage.
|
||||
pub trait StorageMap<K: Slicable, V: Slicable> {
|
||||
/// The type that get/take return.
|
||||
type Query;
|
||||
|
||||
/// Get the prefix key in storage.
|
||||
fn prefix() -> &'static [u8];
|
||||
|
||||
/// Get the storage key used to fetch a value corresponding to a specific key.
|
||||
fn key_for(x: &K) -> Vec<u8>;
|
||||
fn key_for<KeyArg: Borrow<K>>(key: KeyArg) -> Vec<u8>;
|
||||
|
||||
/// Does the value (explicitly) exist in storage?
|
||||
fn exists<KeyArg: Borrow<K>>(key: KeyArg) -> bool;
|
||||
|
||||
/// Load the value associated with the given key from the map.
|
||||
fn get(key: &K) -> Option<V>;
|
||||
fn get<KeyArg: Borrow<K>>(key: KeyArg) -> Self::Query;
|
||||
|
||||
/// Store a value to be associated with the given key from the map.
|
||||
fn insert(key: &K, val: &V);
|
||||
fn insert<KeyArg: Borrow<K>, ValArg: Borrow<V>>(key: KeyArg, val: ValArg);
|
||||
|
||||
/// Remove the value under a key.
|
||||
fn remove(key: &K);
|
||||
fn remove<KeyArg: Borrow<K>>(key: KeyArg);
|
||||
|
||||
/// Take the value under a key.
|
||||
fn take(key: &K) -> Option<V>;
|
||||
fn take<KeyArg: Borrow<K>>(key: KeyArg) -> Self::Query;
|
||||
}
|
||||
|
||||
impl<K: Slicable, V: Slicable, U> StorageMap<K, V> for U where U: generator::StorageMap<K, V> {
|
||||
type Query = U::Query;
|
||||
|
||||
fn prefix() -> &'static [u8] {
|
||||
<U as generator::StorageMap<K, V>>::prefix()
|
||||
}
|
||||
|
||||
fn key_for(item: &K) -> Vec<u8> {
|
||||
<U as generator::StorageMap<K, V>>::key_for(item)
|
||||
fn key_for<KeyArg: Borrow<K>>(key: KeyArg) -> Vec<u8> {
|
||||
<U as generator::StorageMap<K, V>>::key_for(key.borrow())
|
||||
}
|
||||
|
||||
fn get(key: &K) -> Option<V> {
|
||||
U::get(key, &RuntimeStorage)
|
||||
fn exists<KeyArg: Borrow<K>>(key: KeyArg) -> bool {
|
||||
U::exists(key.borrow(), &RuntimeStorage)
|
||||
}
|
||||
|
||||
fn insert(key: &K, val: &V) {
|
||||
U::insert(key, val, &RuntimeStorage)
|
||||
fn get<KeyArg: Borrow<K>>(key: KeyArg) -> Self::Query {
|
||||
U::get(key.borrow(), &RuntimeStorage)
|
||||
}
|
||||
|
||||
fn remove(key: &K) {
|
||||
U::remove(key, &RuntimeStorage)
|
||||
fn insert<KeyArg: Borrow<K>, ValArg: Borrow<V>>(key: KeyArg, val: ValArg) {
|
||||
U::insert(key.borrow(), val.borrow(), &RuntimeStorage)
|
||||
}
|
||||
|
||||
fn take(key: &K) -> Option<V> {
|
||||
U::take(key, &RuntimeStorage)
|
||||
fn remove<KeyArg: Borrow<K>>(key: KeyArg) {
|
||||
U::remove(key.borrow(), &RuntimeStorage)
|
||||
}
|
||||
|
||||
fn take<KeyArg: Borrow<K>>(key: KeyArg) -> Self::Query {
|
||||
U::take(key.borrow(), &RuntimeStorage)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
Reference in New Issue
Block a user