diff --git a/substrate/wasm-runtime/polkadot/src/lib.rs b/substrate/wasm-runtime/polkadot/src/lib.rs index 43830a05b0..1eeff3c649 100644 --- a/substrate/wasm-runtime/polkadot/src/lib.rs +++ b/substrate/wasm-runtime/polkadot/src/lib.rs @@ -38,14 +38,14 @@ use primitives::{Block, UncheckedTransaction}; /// Execute a block, with `input` being the canonical serialisation of the block. Returns the /// empty vector. pub fn execute_block(input: &[u8]) -> Vec { - runtime::system::execute_block(Block::from_slice(input).unwrap()); + runtime::system::internal::execute_block(Block::from_slice(input).unwrap()); Vec::new() } /// Execute a given, serialised, transaction. Returns the empty vector. pub fn execute_transaction(input: &[u8]) -> Vec { let utx = UncheckedTransaction::from_slice(input).unwrap(); - runtime::system::execute_transaction(&utx); + runtime::system::internal::execute_transaction(&utx); Vec::new() } diff --git a/substrate/wasm-runtime/polkadot/src/primitives/function.rs b/substrate/wasm-runtime/polkadot/src/primitives/function.rs index 112c6e8b80..35273e2cfa 100644 --- a/substrate/wasm-runtime/polkadot/src/primitives/function.rs +++ b/substrate/wasm-runtime/polkadot/src/primitives/function.rs @@ -23,14 +23,15 @@ use runtime::{staking, session, timestamp, governance}; /// Public functions that can be dispatched to. #[derive(Clone, Copy)] #[cfg_attr(feature = "with-std", derive(PartialEq, Debug))] +#[repr(u8)] pub enum Function { - StakingStake, - StakingUnstake, - StakingTransfer, - SessionSetKey, - TimestampSet, - GovernancePropose, - GovernanceApprove, + StakingStake = 0, + StakingUnstake = 1, + StakingTransfer = 2, + SessionSetKey = 3, + TimestampSet = 4, + GovernancePropose = 5, + GovernanceApprove = 6, } impl Function { @@ -55,31 +56,31 @@ impl Function { let mut params = StreamReader::new(data); match *self { Function::StakingStake => { - staking::stake(transactor); + staking::public::stake(transactor); } Function::StakingUnstake => { - staking::unstake(transactor); + staking::public::unstake(transactor); } Function::StakingTransfer => { let dest = params.read().unwrap(); let value = params.read().unwrap(); - staking::transfer(transactor, &dest, value); + staking::public::transfer(transactor, &dest, value); } Function::SessionSetKey => { let session = params.read().unwrap(); - session::set_key(transactor, &session); + session::public::set_key(transactor, &session); } Function::TimestampSet => { let t = params.read().unwrap(); - timestamp::set(t); + timestamp::public::set(t); } Function::GovernancePropose => { let proposal = params.read().unwrap(); - governance::propose(transactor, &proposal); + governance::public::propose(transactor, &proposal); } Function::GovernanceApprove => { let era_index = params.read().unwrap(); - governance::approve(transactor, era_index); + governance::public::approve(transactor, era_index); } } } diff --git a/substrate/wasm-runtime/polkadot/src/primitives/proposal.rs b/substrate/wasm-runtime/polkadot/src/primitives/proposal.rs index a4a2a3106b..33539c2f17 100644 --- a/substrate/wasm-runtime/polkadot/src/primitives/proposal.rs +++ b/substrate/wasm-runtime/polkadot/src/primitives/proposal.rs @@ -25,13 +25,14 @@ use runtime::{system, governance, staking, session}; /// Internal functions that can be dispatched to. #[derive(Clone, Copy)] #[cfg_attr(feature = "with-std", derive(PartialEq, Debug))] +#[repr(u8)] pub enum InternalFunction { - SystemSetCode, - StakingSetSessionsPerEra, - StakingSetBondingDuration, - StakingSetValidatorCount, - GovernanceSetApprovalPpmRequired, - SessionSetLength, + SystemSetCode = 0, + StakingSetSessionsPerEra = 1, + StakingSetBondingDuration = 2, + StakingSetValidatorCount = 3, + GovernanceSetApprovalPpmRequired = 4, + SessionSetLength = 5, } impl InternalFunction { @@ -85,27 +86,27 @@ impl Proposal { match self.function { InternalFunction::SystemSetCode => { let code: Vec = params.read().unwrap(); - system::set_code(&code); + system::privileged::set_code(&code); } InternalFunction::StakingSetSessionsPerEra => { let value = params.read().unwrap(); - staking::set_sessions_per_era(value); + staking::privileged::set_sessions_per_era(value); } InternalFunction::StakingSetBondingDuration => { let value = params.read().unwrap(); - staking::set_bonding_duration(value); + staking::privileged::set_bonding_duration(value); } InternalFunction::StakingSetValidatorCount => { let value = params.read().unwrap(); - staking::set_validator_count(value); + staking::privileged::set_validator_count(value); } InternalFunction::GovernanceSetApprovalPpmRequired => { let value = params.read().unwrap(); - governance::set_approval_ppm_required(value); + governance::privileged::set_approval_ppm_required(value); } InternalFunction::SessionSetLength => { let value = params.read().unwrap(); - session::set_length(value); + session::privileged::set_length(value); } } } @@ -114,7 +115,7 @@ impl Proposal { #[cfg(test)] mod test { use super::*; - use statichex::StaticHexInto; + use support::StaticHexInto; #[test] fn slicing_should_work() { diff --git a/substrate/wasm-runtime/polkadot/src/runtime/consensus.rs b/substrate/wasm-runtime/polkadot/src/runtime/consensus.rs index 7d7a0f5ec5..51666666cf 100644 --- a/substrate/wasm-runtime/polkadot/src/runtime/consensus.rs +++ b/substrate/wasm-runtime/polkadot/src/runtime/consensus.rs @@ -31,14 +31,18 @@ pub fn authorities() -> Vec { AuthorityStorageVec::items() } -/// Set the current set of authorities' session keys. -/// -/// Called by `next_session` only. -pub fn set_authorities(authorities: &[SessionKey]) { - AuthorityStorageVec::set_items(authorities); -} +pub mod internal { + use super::*; -/// Set a single authority by index. -pub fn set_authority(index: u32, key: &SessionKey) { - AuthorityStorageVec::set_item(index, key); + /// Set the current set of authorities' session keys. + /// + /// Called by `next_session` only. + pub fn set_authorities(authorities: &[SessionKey]) { + AuthorityStorageVec::set_items(authorities); + } + + /// Set a single authority by index. + pub fn set_authority(index: u32, key: &SessionKey) { + AuthorityStorageVec::set_item(index, key); + } } diff --git a/substrate/wasm-runtime/polkadot/src/runtime/governance.rs b/substrate/wasm-runtime/polkadot/src/runtime/governance.rs index 85a24196bb..4b78e4944f 100644 --- a/substrate/wasm-runtime/polkadot/src/runtime/governance.rs +++ b/substrate/wasm-runtime/polkadot/src/runtime/governance.rs @@ -31,48 +31,6 @@ use support::{Storable, StorageVec, kill}; use primitives::{AccountID, Hash, BlockNumber, Proposal}; use runtime::{staking, system, session}; -// TRANSACTION API - -/// Propose a sensitive action to be taken. Any action that is enactable by `Proposal` is valid. -/// Proposal is by the `transactor` and will automatically count as an approval. Transactor must -/// be a current validator. It is illegal to propose when there is already a proposal in effect. -pub fn propose(validator: &AccountID, proposal: &Proposal) { - if Proposal::lookup(b"gov:pro").is_some() { - panic!("there may only be one proposal per era."); - } - proposal.store(b"gov:pro"); - approve(validator, staking::current_era()); -} - -/// Approve the current era's proposal. Transactor must be a validator. This may not be done more -/// than once for any validator in an era. -pub fn approve(validator: &AccountID, era_index: BlockNumber) { - if era_index != staking::current_era() { - panic!("approval vote applied on non-current era.") - } - if Proposal::lookup(b"gov:pro").is_none() { - panic!("there must be a proposal in order to approve."); - } - if session::validators().into_iter().position(|v| &v == validator).is_none() { - panic!("transactor must be a validator to approve."); - } - let key = validator.to_keyed_vec(b"gov:app:"); - if bool::lookup(&key).is_some() { - panic!("transactor may not approve a proposal twice in one era."); - } - true.store(&key); -} - -/// Set the proportion of validators that must approve for a proposal to be enacted at the end of -/// its era. The value, `ppm`, is measured as a fraction of 1000 rounded down to the nearest whole -/// validator. `1000` would require the approval of all validators; `667` would require two-thirds -/// (or there abouts) of validators. -pub fn set_approval_ppm_required(ppm: u32) { - ppm.store(b"gov:apr"); -} - -// INSPECTION API - /// The proportion of validators required for a propsal to be approved measured as the number out /// of 1000. pub fn approval_ppm_required() -> u32 { @@ -84,20 +42,68 @@ pub fn approvals_required() -> u32 { approval_ppm_required() * session::validator_count() as u32 / 1000 } -// PUBLIC API +pub mod public { + use super::*; -/// Current era is ending; we should finish up any proposals. -pub fn end_of_an_era() { - // tally up votes for the current proposal, if any. enact if there are sufficient approvals. - if let Some(proposal) = Proposal::lookup(b"gov:pro") { - kill(b"gov:pro"); - let approvals_required = approvals_required(); - let approved = session::validators().into_iter() - .filter_map(|v| bool::take(&v.to_keyed_vec(b"gov:app:"))) - .take(approvals_required as usize) - .count() as u32; - if approved == approvals_required { - proposal.enact(); + /// Propose a sensitive action to be taken. Any action that is enactable by `Proposal` is valid. + /// Proposal is by the `transactor` and will automatically count as an approval. Transactor must + /// be a current validator. It is illegal to propose when there is already a proposal in effect. + pub fn propose(validator: &AccountID, proposal: &Proposal) { + if Proposal::lookup(b"gov:pro").is_some() { + panic!("there may only be one proposal per era."); + } + proposal.store(b"gov:pro"); + approve(validator, staking::current_era()); + } + + /// Approve the current era's proposal. Transactor must be a validator. This may not be done more + /// than once for any validator in an era. + pub fn approve(validator: &AccountID, era_index: BlockNumber) { + if era_index != staking::current_era() { + panic!("approval vote applied on non-current era.") + } + if Proposal::lookup(b"gov:pro").is_none() { + panic!("there must be a proposal in order to approve."); + } + if session::validators().into_iter().position(|v| &v == validator).is_none() { + panic!("transactor must be a validator to approve."); + } + let key = validator.to_keyed_vec(b"gov:app:"); + if bool::lookup(&key).is_some() { + panic!("transactor may not approve a proposal twice in one era."); + } + true.store(&key); + } +} + +pub mod privileged { + use super::*; + + /// Set the proportion of validators that must approve for a proposal to be enacted at the end of + /// its era. The value, `ppm`, is measured as a fraction of 1000 rounded down to the nearest whole + /// validator. `1000` would require the approval of all validators; `667` would require two-thirds + /// (or there abouts) of validators. + pub fn set_approval_ppm_required(ppm: u32) { + ppm.store(b"gov:apr"); + } +} + +pub mod internal { + use super::*; + + /// Current era is ending; we should finish up any proposals. + pub fn end_of_an_era() { + // tally up votes for the current proposal, if any. enact if there are sufficient approvals. + if let Some(proposal) = Proposal::lookup(b"gov:pro") { + kill(b"gov:pro"); + let approvals_required = approvals_required(); + let approved = session::validators().into_iter() + .filter_map(|v| bool::take(&v.to_keyed_vec(b"gov:app:"))) + .take(approvals_required as usize) + .count() as u32; + if approved == approvals_required { + proposal.enact(); + } } } } @@ -106,13 +112,10 @@ pub fn end_of_an_era() { mod tests { use super::*; use runtime_std::{with_externalities, twox_128}; - use keyedvec::KeyedVec; - use joiner::Joiner; - use testing::{one, two, TestExternalities}; - use primitives::AccountID; - use proposal::InternalFunction; + use codec::{KeyedVec, Joiner}; + use support::{one, two, TestExternalities, with_env}; + use primitives::{AccountID, InternalFunction}; use runtime::{staking, session}; - use environment::with_env; fn new_test_ext() -> TestExternalities { let one = one(); @@ -152,12 +155,12 @@ mod tests { // Block 1: Make proposal. Approve it. Era length changes. with_env(|e| e.block_number = 1); - propose(&one, &Proposal { + public::propose(&one, &Proposal { function: InternalFunction::StakingSetSessionsPerEra, input_data: vec![].join(&2u64), }); - approve(&two, 1); - staking::check_new_era(); + public::approve(&two, 1); + staking::internal::check_new_era(); assert_eq!(staking::era_length(), 2); }); } @@ -178,21 +181,21 @@ mod tests { // Block 1: Make proposal. Fail it. with_env(|e| e.block_number = 1); - propose(&one, &Proposal { + public::propose(&one, &Proposal { function: InternalFunction::StakingSetSessionsPerEra, input_data: vec![].join(&2u64), }); - staking::check_new_era(); + staking::internal::check_new_era(); assert_eq!(staking::era_length(), 1); // Block 2: Make proposal. Approve it. It should change era length. with_env(|e| e.block_number = 2); - propose(&one, &Proposal { + public::propose(&one, &Proposal { function: InternalFunction::StakingSetSessionsPerEra, input_data: vec![].join(&2u64), }); - approve(&two, 2); - staking::check_new_era(); + public::approve(&two, 2); + staking::internal::check_new_era(); assert_eq!(staking::era_length(), 2); }); } @@ -213,11 +216,11 @@ mod tests { // Block 1: Make proposal. Will have only 1 vote. No change. with_env(|e| e.block_number = 1); - propose(&one, &Proposal { + public::propose(&one, &Proposal { function: InternalFunction::StakingSetSessionsPerEra, input_data: vec![].join(&2u64), }); - staking::check_new_era(); + staking::internal::check_new_era(); assert_eq!(staking::era_length(), 1); }); } @@ -239,12 +242,12 @@ mod tests { // Block 1: Make proposal. Will have only 1 vote. No change. with_env(|e| e.block_number = 1); - propose(&one, &Proposal { + public::propose(&one, &Proposal { function: InternalFunction::StakingSetSessionsPerEra, input_data: vec![].join(&2u64), }); - approve(&two, 0); - staking::check_new_era(); + public::approve(&two, 0); + staking::internal::check_new_era(); assert_eq!(staking::era_length(), 1); }); } @@ -266,13 +269,13 @@ mod tests { // Block 1: Make proposal. Will have only 1 vote. No change. with_env(|e| e.block_number = 1); - propose(&one, &Proposal { + public::propose(&one, &Proposal { function: InternalFunction::StakingSetSessionsPerEra, input_data: vec![].join(&2u64), }); - approve(&two, 1); - approve(&two, 1); - staking::check_new_era(); + public::approve(&two, 1); + public::approve(&two, 1); + staking::internal::check_new_era(); assert_eq!(staking::era_length(), 1); }); } @@ -294,15 +297,15 @@ mod tests { // Block 1: Make proposal. Will have only 1 vote. No change. with_env(|e| e.block_number = 1); - propose(&one, &Proposal { + public::propose(&one, &Proposal { function: InternalFunction::StakingSetSessionsPerEra, input_data: vec![].join(&2u64), }); - propose(&two, &Proposal { + public::propose(&two, &Proposal { function: InternalFunction::StakingSetSessionsPerEra, input_data: vec![].join(&2u64), }); - staking::check_new_era(); + staking::internal::check_new_era(); assert_eq!(staking::era_length(), 1); }); } @@ -324,8 +327,8 @@ mod tests { // Block 1: Make proposal. Will have only 1 vote. No change. with_env(|e| e.block_number = 1); - approve(&two, 1); - staking::check_new_era(); + public::approve(&two, 1); + staking::internal::check_new_era(); assert_eq!(staking::era_length(), 1); }); } @@ -348,12 +351,12 @@ mod tests { // Block 1: Make proposal. Will have only 1 vote. No change. with_env(|e| e.block_number = 1); - propose(&one, &Proposal { + public::propose(&one, &Proposal { function: InternalFunction::StakingSetSessionsPerEra, input_data: vec![].join(&2u64), }); - approve(&four, 1); - staking::check_new_era(); + public::approve(&four, 1); + staking::internal::check_new_era(); assert_eq!(staking::era_length(), 1); }); } diff --git a/substrate/wasm-runtime/polkadot/src/runtime/session.rs b/substrate/wasm-runtime/polkadot/src/runtime/session.rs index d6dc1e3764..c88aabbbe6 100644 --- a/substrate/wasm-runtime/polkadot/src/runtime/session.rs +++ b/substrate/wasm-runtime/polkadot/src/runtime/session.rs @@ -23,50 +23,6 @@ use support::{kill, Storable, StorageVec}; use primitives::{AccountID, SessionKey, BlockNumber}; use runtime::{system, staking, consensus}; -struct ValidatorStorageVec {} -impl StorageVec for ValidatorStorageVec { - type Item = AccountID; - const PREFIX: &'static[u8] = b"ses:val:"; -} - -// TRANSACTION API (available to all transactors) - -/// Sets the session key of `_validator` to `_key`. This doesn't take effect until the next -/// session. -pub fn set_key(validator: &AccountID, key: &SessionKey) { - // set new value for next session - key.store(&validator.to_keyed_vec(b"ses:nxt:")); -} -// PRIVILEGED API (available to proposals) - -/// Set a new era length. Won't kick in until the next era change (at current length). -pub fn set_length(new: BlockNumber) { - new.store(b"ses:nln"); -} - -// INTERNAL API (available to other runtime modules) - -/// Set the current set of validators. -/// -/// 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); - consensus::set_authorities(new); -} - -/// Hook to be called after transaction processing. -pub fn check_rotate_session() { - // 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 { - rotate_session(); - } -} - -// Inspection API - /// Get the current set of authorities. These are the session keys. pub fn validators() -> Vec { ValidatorStorageVec::items() @@ -92,7 +48,56 @@ pub fn last_length_change() -> BlockNumber { Storable::lookup_default(b"ses:llc") } -// PRIVATE (not available for use externally) +pub mod public { + use super::*; + + /// Sets the session key of `_validator` to `_key`. This doesn't take effect until the next + /// session. + pub fn set_key(validator: &AccountID, key: &SessionKey) { + // set new value for next session + key.store(&validator.to_keyed_vec(b"ses:nxt:")); + } +} + +pub mod privileged { + use super::*; + + /// Set a new era length. Won't kick in until the next era change (at current length). + pub fn set_length(new: BlockNumber) { + new.store(b"ses:nln"); + } +} + +// INTERNAL API (available to other runtime modules) + +pub mod internal { + use super::*; + + /// Set the current set of validators. + /// + /// 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); + consensus::internal::set_authorities(new); + } + + /// Hook to be called after transaction processing. + pub fn check_rotate_session() { + // 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 { + rotate_session(); + } + } +} + +struct ValidatorStorageVec {} +impl StorageVec for ValidatorStorageVec { + type Item = AccountID; + const PREFIX: &'static[u8] = b"ses:val:"; +} /// Move onto next session: register the new authority set. fn rotate_session() { @@ -110,7 +115,7 @@ fn rotate_session() { validators().iter().enumerate().for_each(|(i, v)| { let k = v.to_keyed_vec(b"ses:nxt:"); if let Some(n) = Storable::lookup(&k) { - consensus::set_authority(i as u32, &n); + consensus::internal::set_authority(i as u32, &n); kill(&k); } }); @@ -118,13 +123,15 @@ fn rotate_session() { #[cfg(test)] mod tests { + use super::*; + use super::public::*; + use super::privileged::*; + use super::internal::*; use runtime_std::{with_externalities, twox_128}; - use keyedvec::KeyedVec; - use joiner::Joiner; - use testing::{one, two, TestExternalities}; + use codec::{KeyedVec, Joiner}; + use support::{one, two, TestExternalities, with_env}; use primitives::AccountID; use runtime::{consensus, session}; - use environment::with_env; fn simple_setup() -> TestExternalities { TestExternalities { storage: map![ @@ -145,8 +152,8 @@ mod tests { let mut t = simple_setup(); with_externalities(&mut t, || { assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]); - assert_eq!(session::length(), 2u64); - assert_eq!(session::validators(), vec![[10u8; 32], [20u8; 32]]); + assert_eq!(length(), 2u64); + assert_eq!(validators(), vec![[10u8; 32], [20u8; 32]]); }); } @@ -156,48 +163,48 @@ mod tests { with_externalities(&mut t, || { // Block 1: Change to length 3; no visible change. with_env(|e| e.block_number = 1); - session::set_length(3); - session::check_rotate_session(); - assert_eq!(session::length(), 2); - assert_eq!(session::current_index(), 0); + set_length(3); + check_rotate_session(); + assert_eq!(length(), 2); + assert_eq!(current_index(), 0); // Block 2: Length now changed to 3. Index incremented. with_env(|e| e.block_number = 2); - session::set_length(3); - session::check_rotate_session(); - assert_eq!(session::length(), 3); - assert_eq!(session::current_index(), 1); + set_length(3); + check_rotate_session(); + assert_eq!(length(), 3); + assert_eq!(current_index(), 1); // Block 3: Length now changed to 3. Index incremented. with_env(|e| e.block_number = 3); - session::check_rotate_session(); - assert_eq!(session::length(), 3); - assert_eq!(session::current_index(), 1); + check_rotate_session(); + assert_eq!(length(), 3); + assert_eq!(current_index(), 1); // Block 4: Change to length 2; no visible change. with_env(|e| e.block_number = 4); - session::set_length(2); - session::check_rotate_session(); - assert_eq!(session::length(), 3); - assert_eq!(session::current_index(), 1); + set_length(2); + check_rotate_session(); + assert_eq!(length(), 3); + assert_eq!(current_index(), 1); // Block 5: Length now changed to 2. Index incremented. with_env(|e| e.block_number = 5); - session::check_rotate_session(); - assert_eq!(session::length(), 2); - assert_eq!(session::current_index(), 2); + check_rotate_session(); + assert_eq!(length(), 2); + assert_eq!(current_index(), 2); // Block 6: No change. with_env(|e| e.block_number = 6); - session::check_rotate_session(); - assert_eq!(session::length(), 2); - assert_eq!(session::current_index(), 2); + check_rotate_session(); + assert_eq!(length(), 2); + assert_eq!(current_index(), 2); // Block 7: Next index. with_env(|e| e.block_number = 7); - session::check_rotate_session(); - assert_eq!(session::length(), 2); - assert_eq!(session::current_index(), 3); + check_rotate_session(); + assert_eq!(length(), 2); + assert_eq!(current_index(), 3); }); } @@ -207,25 +214,25 @@ mod tests { with_externalities(&mut t, || { // Block 1: No change with_env(|e| e.block_number = 1); - session::check_rotate_session(); + check_rotate_session(); assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]); // Block 2: Session rollover, but no change. with_env(|e| e.block_number = 2); - session::check_rotate_session(); + check_rotate_session(); assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]); // Block 3: Set new key for validator 2; no visible change. with_env(|e| e.block_number = 3); - session::set_key(&[20; 32], &[22; 32]); + set_key(&[20; 32], &[22; 32]); assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]); - session::check_rotate_session(); + check_rotate_session(); assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]); // Block 4: Session rollover, authority 2 changes. with_env(|e| e.block_number = 4); - session::check_rotate_session(); + check_rotate_session(); assert_eq!(consensus::authorities(), vec![[11u8; 32], [22u8; 32]]); }); } diff --git a/substrate/wasm-runtime/polkadot/src/runtime/staking.rs b/substrate/wasm-runtime/polkadot/src/runtime/staking.rs index b87d26c360..b0e8a9ddaf 100644 --- a/substrate/wasm-runtime/polkadot/src/runtime/staking.rs +++ b/substrate/wasm-runtime/polkadot/src/runtime/staking.rs @@ -29,88 +29,6 @@ pub type Balance = u64; /// The amount of bonding period left in an account. Measured in eras. pub type Bondage = u64; -struct IntentionStorageVec {} -impl StorageVec for IntentionStorageVec { - type Item = AccountID; - const PREFIX: &'static[u8] = b"sta:wil:"; -} - -// 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. -// - ~0: active: currently representing a validator. -// - n | n > current_era(): deactivating: recently representing a validator and not yet -// ready for transfer. - -// TRANSACTION API - -/// Transfer some unlocked staking balance to another staker. -pub fn transfer(transactor: &AccountID, dest: &AccountID, value: Balance) { - let from_key = transactor.to_keyed_vec(b"sta:bal:"); - let from_balance = Balance::lookup_default(&from_key); - assert!(from_balance >= value); - let to_key = dest.to_keyed_vec(b"sta:bal:"); - let to_balance: Balance = Storable::lookup_default(&to_key); - assert!(bondage(transactor) <= bondage(dest)); - assert!(to_balance + value > to_balance); // no overflow - (from_balance - value).store(&from_key); - (to_balance + value).store(&to_key); -} - -/// Declare the desire to stake for the transactor. -/// -/// Effects will be felt at the beginning of the next era. -pub fn stake(transactor: &AccountID) { - let mut intentions = IntentionStorageVec::items(); - // can't be in the list twice. - assert!(intentions.iter().find(|t| *t == transactor).is_none(), "Cannot stake if already staked."); - intentions.push(transactor.clone()); - IntentionStorageVec::set_items(&intentions); - u64::max_value().store(&transactor.to_keyed_vec(b"sta:bon:")); -} - -/// Retract the desire to stake for the transactor. -/// -/// Effects will be felt at the beginning of the next era. -pub fn unstake(transactor: &AccountID) { - let mut intentions = IntentionStorageVec::items(); - if let Some(position) = intentions.iter().position(|t| t == transactor) { - intentions.swap_remove(position); - } else { - panic!("Cannot unstake if not already staked."); - } - IntentionStorageVec::set_items(&intentions); - (current_era() + bonding_duration()).store(&transactor.to_keyed_vec(b"sta:bon:")); -} - -// PRIVILEDGED API - -/// Set the number of sessions in an era. -pub fn set_sessions_per_era(new: BlockNumber) { - new.store(b"sta:nse"); -} - -/// The length of the bonding duration in eras. -pub fn set_bonding_duration(new: BlockNumber) { - new.store(b"sta:loc"); -} - -/// The length of a staking era in sessions. -pub fn set_validator_count(new: usize) { - (new as u32).store(b"sta:vac"); -} - -// INTERNAL API - -/// 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 { - new_era(); - } -} - -// INSPECTION API - /// The length of the bonding duration in eras. pub fn bonding_duration() -> BlockNumber { Storable::lookup_default(b"sta:loc") @@ -151,7 +69,91 @@ pub fn bondage(who: &AccountID) -> Bondage { Storable::lookup_default(&who.to_keyed_vec(b"sta:bon:")) } -// PRIVATE +// 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. +// - ~0: active: currently representing a validator. +// - n | n > current_era(): deactivating: recently representing a validator and not yet +// ready for transfer. + +pub mod public { + use super::*; + + /// Transfer some unlocked staking balance to another staker. + pub fn transfer(transactor: &AccountID, dest: &AccountID, value: Balance) { + let from_key = transactor.to_keyed_vec(b"sta:bal:"); + let from_balance = Balance::lookup_default(&from_key); + assert!(from_balance >= value); + let to_key = dest.to_keyed_vec(b"sta:bal:"); + let to_balance: Balance = Storable::lookup_default(&to_key); + assert!(bondage(transactor) <= bondage(dest)); + assert!(to_balance + value > to_balance); // no overflow + (from_balance - value).store(&from_key); + (to_balance + value).store(&to_key); + } + + /// Declare the desire to stake for the transactor. + /// + /// Effects will be felt at the beginning of the next era. + pub fn stake(transactor: &AccountID) { + let mut intentions = IntentionStorageVec::items(); + // can't be in the list twice. + assert!(intentions.iter().find(|t| *t == transactor).is_none(), "Cannot stake if already staked."); + intentions.push(transactor.clone()); + IntentionStorageVec::set_items(&intentions); + u64::max_value().store(&transactor.to_keyed_vec(b"sta:bon:")); + } + + /// Retract the desire to stake for the transactor. + /// + /// Effects will be felt at the beginning of the next era. + pub fn unstake(transactor: &AccountID) { + let mut intentions = IntentionStorageVec::items(); + if let Some(position) = intentions.iter().position(|t| t == transactor) { + intentions.swap_remove(position); + } else { + panic!("Cannot unstake if not already staked."); + } + IntentionStorageVec::set_items(&intentions); + (current_era() + bonding_duration()).store(&transactor.to_keyed_vec(b"sta:bon:")); + } +} + +pub mod privileged { + use super::*; + + /// Set the number of sessions in an era. + pub fn set_sessions_per_era(new: BlockNumber) { + new.store(b"sta:nse"); + } + + /// The length of the bonding duration in eras. + pub fn set_bonding_duration(new: BlockNumber) { + new.store(b"sta:loc"); + } + + /// The length of a staking era in sessions. + pub fn set_validator_count(new: usize) { + (new as u32).store(b"sta:vac"); + } +} + +pub mod internal { + use super::*; + + /// 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 { + new_era(); + } + } +} + +struct IntentionStorageVec {} +impl StorageVec for IntentionStorageVec { + type Item = AccountID; + const PREFIX: &'static[u8] = b"sta:wil:"; +} /// The era has changed - enact new staking set. /// @@ -159,7 +161,7 @@ pub fn bondage(who: &AccountID) -> Bondage { /// get a chance to set their session keys. fn new_era() { // Inform governance module that it's the end of an era - governance::end_of_an_era(); + governance::internal::end_of_an_era(); // Increment current era. (current_era() + 1).store(b"sta:era"); @@ -172,7 +174,7 @@ fn new_era() { } // evaluate desired staking amounts and nominations and optimise to find the best - // combination of validators, then use session::set_validators(). + // 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() @@ -180,7 +182,7 @@ fn new_era() { .map(|v| (balance(&v), v)) .collect::>(); intentions.sort_unstable_by(|&(b1, _), &(b2, _)| b2.cmp(&b1)); - session::set_validators( + session::internal::set_validators( &intentions.into_iter() .map(|(_, v)| v) .take(validator_count()) @@ -190,13 +192,16 @@ fn new_era() { #[cfg(test)] mod tests { + use super::*; + use super::internal::*; + use super::public::*; + use super::privileged::*; + use runtime_std::{with_externalities, twox_128}; - use keyedvec::KeyedVec; - use joiner::Joiner; - use testing::{one, two, TestExternalities}; + use codec::{KeyedVec, Joiner}; + use support::{one, two, TestExternalities, with_env}; use primitives::AccountID; use runtime::{staking, session}; - use environment::with_env; #[test] fn staking_should_work() { @@ -220,54 +225,54 @@ mod tests { ], }; with_externalities(&mut t, || { - assert_eq!(staking::era_length(), 2u64); - assert_eq!(staking::validator_count(), 2usize); - assert_eq!(staking::bonding_duration(), 3u64); + assert_eq!(era_length(), 2u64); + assert_eq!(validator_count(), 2usize); + assert_eq!(bonding_duration(), 3u64); assert_eq!(session::validators(), vec![[10u8; 32], [20u8; 32]]); // Block 1: Add three validators. No obvious change. with_env(|e| e.block_number = 1); - staking::stake(&one); - staking::stake(&two); - staking::stake(&four); - staking::check_new_era(); + stake(&one); + stake(&two); + stake(&four); + check_new_era(); assert_eq!(session::validators(), vec![[10u8; 32], [20u8; 32]]); // Block 2: New validator set now. with_env(|e| e.block_number = 2); - staking::check_new_era(); + check_new_era(); assert_eq!(session::validators(), vec![four.clone(), two.clone()]); // Block 3: Unstake highest, introduce another staker. No change yet. with_env(|e| e.block_number = 3); - staking::stake(&three); - staking::unstake(&four); - staking::check_new_era(); + stake(&three); + unstake(&four); + check_new_era(); // Block 4: New era - validators change. with_env(|e| e.block_number = 4); - staking::check_new_era(); + check_new_era(); assert_eq!(session::validators(), vec![three.clone(), two.clone()]); // Block 5: Transfer stake from highest to lowest. No change yet. with_env(|e| e.block_number = 5); - staking::transfer(&four, &one, 40); - staking::check_new_era(); + transfer(&four, &one, 40); + check_new_era(); // Block 6: Lowest now validator. with_env(|e| e.block_number = 6); - staking::check_new_era(); + check_new_era(); assert_eq!(session::validators(), vec![one.clone(), three.clone()]); // Block 7: Unstake three. No change yet. with_env(|e| e.block_number = 7); - staking::unstake(&three); - staking::check_new_era(); + unstake(&three); + check_new_era(); assert_eq!(session::validators(), vec![one.clone(), three.clone()]); // Block 8: Back to one and two. with_env(|e| e.block_number = 8); - staking::check_new_era(); + check_new_era(); assert_eq!(session::validators(), vec![one.clone(), two.clone()]); }); } @@ -279,60 +284,60 @@ mod tests { twox_128(b"sta:spe").to_vec() => vec![].join(&2u64) ], }; with_externalities(&mut t, || { - assert_eq!(staking::era_length(), 2u64); - assert_eq!(staking::sessions_per_era(), 2u64); - assert_eq!(staking::last_era_length_change(), 0u64); - assert_eq!(staking::current_era(), 0u64); + assert_eq!(era_length(), 2u64); + assert_eq!(sessions_per_era(), 2u64); + assert_eq!(last_era_length_change(), 0u64); + assert_eq!(current_era(), 0u64); // Block 1: No change. with_env(|e| e.block_number = 1); - staking::check_new_era(); - assert_eq!(staking::sessions_per_era(), 2u64); - assert_eq!(staking::last_era_length_change(), 0u64); - assert_eq!(staking::current_era(), 0u64); + check_new_era(); + assert_eq!(sessions_per_era(), 2u64); + assert_eq!(last_era_length_change(), 0u64); + assert_eq!(current_era(), 0u64); // Block 2: Simple era change. with_env(|e| e.block_number = 2); - staking::check_new_era(); - assert_eq!(staking::sessions_per_era(), 2u64); - assert_eq!(staking::last_era_length_change(), 0u64); - assert_eq!(staking::current_era(), 1u64); + check_new_era(); + assert_eq!(sessions_per_era(), 2u64); + assert_eq!(last_era_length_change(), 0u64); + assert_eq!(current_era(), 1u64); // Block 3: Schedule an era length change; no visible changes. with_env(|e| e.block_number = 3); - staking::set_sessions_per_era(3); - staking::check_new_era(); - assert_eq!(staking::sessions_per_era(), 2u64); - assert_eq!(staking::last_era_length_change(), 0u64); - assert_eq!(staking::current_era(), 1u64); + 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); // Block 4: Era change kicks in. with_env(|e| e.block_number = 4); - staking::check_new_era(); - assert_eq!(staking::sessions_per_era(), 3u64); - assert_eq!(staking::last_era_length_change(), 4u64); - assert_eq!(staking::current_era(), 2u64); + check_new_era(); + assert_eq!(sessions_per_era(), 3u64); + assert_eq!(last_era_length_change(), 4u64); + assert_eq!(current_era(), 2u64); // Block 5: No change. with_env(|e| e.block_number = 5); - staking::check_new_era(); - assert_eq!(staking::sessions_per_era(), 3u64); - assert_eq!(staking::last_era_length_change(), 4u64); - assert_eq!(staking::current_era(), 2u64); + check_new_era(); + assert_eq!(sessions_per_era(), 3u64); + assert_eq!(last_era_length_change(), 4u64); + assert_eq!(current_era(), 2u64); // Block 6: No change. with_env(|e| e.block_number = 6); - staking::check_new_era(); - assert_eq!(staking::sessions_per_era(), 3u64); - assert_eq!(staking::last_era_length_change(), 4u64); - assert_eq!(staking::current_era(), 2u64); + check_new_era(); + assert_eq!(sessions_per_era(), 3u64); + assert_eq!(last_era_length_change(), 4u64); + assert_eq!(current_era(), 2u64); // Block 7: Era increment. with_env(|e| e.block_number = 7); - staking::check_new_era(); - assert_eq!(staking::sessions_per_era(), 3u64); - assert_eq!(staking::last_era_length_change(), 4u64); - assert_eq!(staking::current_era(), 3u64); + check_new_era(); + assert_eq!(sessions_per_era(), 3u64); + assert_eq!(last_era_length_change(), 4u64); + assert_eq!(current_era(), 3u64); }); } @@ -346,8 +351,8 @@ mod tests { ], }; with_externalities(&mut t, || { - assert_eq!(staking::balance(&one), 42); - assert_eq!(staking::balance(&two), 0); + assert_eq!(balance(&one), 42); + assert_eq!(balance(&two), 0); }); } @@ -361,9 +366,9 @@ mod tests { ], }; with_externalities(&mut t, || { - staking::transfer(&one, &two, 69); - assert_eq!(staking::balance(&one), 42); - assert_eq!(staking::balance(&two), 69); + transfer(&one, &two, 69); + assert_eq!(balance(&one), 42); + assert_eq!(balance(&two), 69); }); } @@ -378,8 +383,8 @@ mod tests { ], }; with_externalities(&mut t, || { - staking::stake(&one); - staking::transfer(&one, &two, 69); + stake(&one); + transfer(&one, &two, 69); }); } } diff --git a/substrate/wasm-runtime/polkadot/src/runtime/system.rs b/substrate/wasm-runtime/polkadot/src/runtime/system.rs index d6236beb78..2327ffc3c4 100644 --- a/substrate/wasm-runtime/polkadot/src/runtime/system.rs +++ b/substrate/wasm-runtime/polkadot/src/runtime/system.rs @@ -34,75 +34,83 @@ pub fn block_hash(number: BlockNumber) -> Hash { Storable::lookup_default(&number.to_keyed_vec(b"sys:old:")) } -/// Deposits a log and ensures it matches the blocks log data. -pub fn deposit_log(log: &[u8]) { - with_env(|e| { - assert_eq!(log, &e.digest.logs[e.next_log_index][..]); - e.next_log_index += 1; - }); +pub mod privileged { + use super::*; + + /// Set the new code. + pub fn set_code(new: &[u8]) { + new.store(b":code"); + } } -/// Actually execute all transitioning for `block`. -pub fn execute_block(mut block: Block) { - // populate environment from header. - with_env(|e| { - e.block_number = block.header.number; - mem::swap(&mut e.digest, &mut block.header.digest); - e.next_log_index = 0; - }); +pub mod internal { + use super::*; - let ref header = block.header; + /// Deposits a log and ensures it matches the blocks log data. + pub fn deposit_log(log: &[u8]) { + with_env(|e| { + assert_eq!(log, &e.digest.logs[e.next_log_index][..]); + e.next_log_index += 1; + }); + } - // check parent_hash is correct. - assert!( - header.number > 0 && block_hash(header.number - 1) == header.parent_hash, - "Parent hash should be valid." - ); + /// Actually execute all transitioning for `block`. + pub fn execute_block(mut block: Block) { + // populate environment from header. + with_env(|e| { + e.block_number = block.header.number; + mem::swap(&mut e.digest, &mut block.header.digest); + e.next_log_index = 0; + }); - // TODO: check transaction trie root represents the transactions. - // this requires non-trivial changes to the externals API or compiling trie rooting into wasm - // so will wait until a little later. + let ref header = block.header; - // store the header hash in storage. - let header_hash_key = header.number.to_keyed_vec(b"sys:old:"); - header.blake2_256().store(&header_hash_key); + // check parent_hash is correct. + assert!( + header.number > 0 && block_hash(header.number - 1) == header.parent_hash, + "Parent hash should be valid." + ); - // execute transactions - block.transactions.iter().for_each(execute_transaction); + // TODO: check transaction trie root represents the transactions. + // this requires non-trivial changes to the externals API or compiling trie rooting into wasm + // so will wait until a little later. - staking::check_new_era(); - session::check_rotate_session(); + // store the header hash in storage. + let header_hash_key = header.number.to_keyed_vec(b"sys:old:"); + header.blake2_256().store(&header_hash_key); - // any final checks - final_checks(&block); + // execute transactions + block.transactions.iter().for_each(execute_transaction); - // TODO: check storage root. - // this requires non-trivial changes to the externals API or compiling trie rooting into wasm - // so will wait until a little later. -} + staking::internal::check_new_era(); + session::internal::check_rotate_session(); -/// Execute a given transaction. -pub fn execute_transaction(utx: &UncheckedTransaction) { - // Verify the signature is good. - assert!(utx.ed25519_verify(), "All transactions should be properly signed"); + // any final checks + final_checks(&block); - let ref tx = utx.transaction; + // TODO: check storage root. + // this requires non-trivial changes to the externals API or compiling trie rooting into wasm + // so will wait until a little later. + } - // check nonce - let nonce_key = tx.signed.to_keyed_vec(b"sys:non:"); - let expected_nonce: TxOrder = Storable::lookup_default(&nonce_key); - assert!(tx.nonce == expected_nonce, "All transactions should have the correct nonce"); + /// Execute a given transaction. + pub fn execute_transaction(utx: &UncheckedTransaction) { + // Verify the signature is good. + assert!(utx.ed25519_verify(), "All transactions should be properly signed"); - // increment nonce in storage - (expected_nonce + 1).store(&nonce_key); + let ref tx = utx.transaction; - // decode parameters and dispatch - tx.function.dispatch(&tx.signed, &tx.input_data); -} + // check nonce + let nonce_key = tx.signed.to_keyed_vec(b"sys:non:"); + let expected_nonce: TxOrder = Storable::lookup_default(&nonce_key); + assert!(tx.nonce == expected_nonce, "All transactions should have the correct nonce"); -/// Set the new code. -pub fn set_code(new: &[u8]) { - new.store(b":code"); + // increment nonce in storage + (expected_nonce + 1).store(&nonce_key); + + // decode parameters and dispatch + tx.function.dispatch(&tx.signed, &tx.input_data); + } } fn final_checks(_block: &Block) { @@ -113,15 +121,14 @@ fn final_checks(_block: &Block) { #[cfg(test)] mod tests { - use joiner::Joiner; - use function::Function; - use keyedvec::KeyedVec; - use slicable::Slicable; + use super::*; + use super::internal::*; + use runtime_std::{with_externalities, twox_128}; - use primitives::{UncheckedTransaction, Transaction}; - use statichex::StaticHexInto; - use runtime::{system, staking}; - use testing::{TestExternalities, HexDisplay, one, two}; + use codec::{Joiner, KeyedVec, Slicable}; + use support::{StaticHexInto, TestExternalities, HexDisplay, one, two}; + use primitives::{UncheckedTransaction, Transaction, Function}; + use runtime::staking; #[test] fn staking_balance_transfer_dispatch_works() { @@ -147,7 +154,7 @@ mod tests { println!("tx is {}", HexDisplay::from(&tx.transaction.to_vec())); with_externalities(&mut t, || { - system::execute_transaction(&tx); + execute_transaction(&tx); assert_eq!(staking::balance(&one), 42); assert_eq!(staking::balance(&two), 69); }); diff --git a/substrate/wasm-runtime/polkadot/src/runtime/timestamp.rs b/substrate/wasm-runtime/polkadot/src/runtime/timestamp.rs index 09fb36a606..0f471f4a98 100644 --- a/substrate/wasm-runtime/polkadot/src/runtime/timestamp.rs +++ b/substrate/wasm-runtime/polkadot/src/runtime/timestamp.rs @@ -26,18 +26,24 @@ pub fn get() -> Timestamp { Storable::lookup_default(b"tim:val") } -/// Set the current time. -pub fn set(now: Timestamp) { - now.store(b"tim:val") +pub mod public { + use super::*; + + /// Set the current time. + pub fn set(now: Timestamp) { + now.store(b"tim:val") + } } #[cfg(test)] mod tests { - use joiner::Joiner; - use keyedvec::KeyedVec; + use super::*; + use super::public::*; + use runtime_std::{with_externalities, twox_128}; use runtime::timestamp; - use testing::TestExternalities; + use codec::{Joiner, KeyedVec}; + use support::TestExternalities; #[test] fn timestamp_works() { @@ -46,9 +52,9 @@ mod tests { ], }; with_externalities(&mut t, || { - assert_eq!(timestamp::get(), 42); - timestamp::set(69); - assert_eq!(timestamp::get(), 69); + assert_eq!(get(), 42); + set(69); + assert_eq!(get(), 69); }); } } diff --git a/substrate/wasm-runtime/polkadot/src/support/storable.rs b/substrate/wasm-runtime/polkadot/src/support/storable.rs index 97f6384c10..dc1a6fde2d 100644 --- a/substrate/wasm-runtime/polkadot/src/support/storable.rs +++ b/substrate/wasm-runtime/polkadot/src/support/storable.rs @@ -110,7 +110,7 @@ mod tests { use super::*; use std::collections::HashMap; use runtime_std::with_externalities; - use testing::{TestExternalities, HexDisplay}; + use support::{TestExternalities, HexDisplay}; use runtime_std::{storage, twox_128}; #[test] @@ -180,7 +180,7 @@ mod tests { #[test] fn proposals_can_be_stored() { - use proposal::{Proposal, InternalFunction}; + use primitives::{Proposal, InternalFunction}; let mut t = TestExternalities { storage: HashMap::new(), }; with_externalities(&mut t, || { let x = Proposal { function: InternalFunction::StakingSetSessionsPerEra, input_data: b"Hello world".to_vec() };