Introduce module structure over comments.

This commit is contained in:
Gav
2018-01-28 14:36:40 +01:00
parent bc6eb7d70a
commit 59469995b2
10 changed files with 460 additions and 426 deletions
+2 -2
View File
@@ -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<u8> {
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<u8> {
let utx = UncheckedTransaction::from_slice(input).unwrap();
runtime::system::execute_transaction(&utx);
runtime::system::internal::execute_transaction(&utx);
Vec::new()
}
@@ -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);
}
}
}
@@ -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<u8> = 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() {
@@ -31,14 +31,18 @@ pub fn authorities() -> Vec<SessionKey> {
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);
}
}
@@ -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);
});
}
@@ -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<AccountID> {
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]]);
});
}
@@ -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::<Vec<_>>();
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);
});
}
}
@@ -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);
});
@@ -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);
});
}
}
@@ -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() };