Vesting schedules. (#1726)

* Vesting.

* Vesting stuff.

* Add new wasm blobs

* Bump runtime version

* Update lock

* Fix tests

* Bump version
This commit is contained in:
Gav Wood
2019-02-10 11:15:16 +01:00
committed by GitHub
parent 51a98f5c94
commit e5ac7f0957
17 changed files with 105 additions and 32 deletions
+1
View File
@@ -3066,6 +3066,7 @@ version = "0.1.0"
dependencies = [
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec-derive 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-io 0.1.0",
+1
View File
@@ -1146,6 +1146,7 @@ version = "0.1.0"
dependencies = [
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec-derive 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-primitives 0.1.0",
@@ -96,6 +96,7 @@ fn testnet_genesis(initial_authorities: Vec<Ed25519AuthorityId>, endowed_account
transfer_fee: 0,
creation_fee: 0,
balances: endowed_accounts.iter().map(|&k|(k, (1 << 60))).collect(),
vesting: vec![],
}),
sudo: Some(SudoConfig {
key: root_key,
+2
View File
@@ -69,6 +69,7 @@ fn staging_testnet_config_genesis() -> GenesisConfig {
existential_deposit: 1 * DOLLARS,
transfer_fee: 1 * CENTS,
creation_fee: 1 * CENTS,
vesting: vec![],
}),
indices: Some(IndicesConfig {
ids: endowed_accounts.clone(),
@@ -197,6 +198,7 @@ pub fn testnet_genesis(
transfer_fee: 0,
creation_fee: 0,
balances: endowed_accounts.iter().map(|&k| (k.into(), (1 << 60))).collect(),
vesting: vec![],
}),
session: Some(SessionConfig {
validators: initial_authorities.iter().cloned().map(Into::into).collect(),
+1
View File
@@ -269,6 +269,7 @@ mod tests {
existential_deposit: 0,
transfer_fee: 0,
creation_fee: 0,
vesting: vec![],
}),
session: Some(SessionConfig {
session_length: 2,
+2 -2
View File
@@ -65,8 +65,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("node"),
impl_name: create_runtime_str!("substrate-node"),
authoring_version: 10,
spec_version: 25,
impl_version: 25,
spec_version: 26,
impl_version: 26,
apis: RUNTIME_API_VERSIONS,
};
+1
View File
@@ -1160,6 +1160,7 @@ version = "0.1.0"
dependencies = [
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec-derive 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.81 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-primitives 0.1.0",
+2
View File
@@ -9,6 +9,7 @@ hex-literal = "0.1.0"
serde = { version = "1.0", default-features = false }
safe-mix = { version = "1.0", default-features = false}
parity-codec = { version = "3.0", default-features = false }
parity-codec-derive = { version = "3.0", default-features = false }
substrate-keyring = { path = "../../core/keyring", optional = true }
rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false }
primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false }
@@ -26,6 +27,7 @@ std = [
"safe-mix/std",
"substrate-keyring",
"parity-codec/std",
"parity-codec-derive/std",
"rstd/std",
"srml-support/std",
"primitives/std",
+87 -30
View File
@@ -27,7 +27,8 @@
use rstd::prelude::*;
use rstd::{cmp, result};
use parity_codec::Codec;
use srml_support::{StorageValue, StorageMap, Parameter, decl_module, decl_event, decl_storage};
use parity_codec_derive::{Encode, Decode};
use srml_support::{StorageValue, StorageMap, Parameter, decl_event, decl_storage, decl_module};
use srml_support::traits::{UpdateBalanceOutcome, Currency, EnsureAccountLiquid, OnFreeBalanceZero};
use srml_support::dispatch::Result;
use primitives::traits::{Zero, SimpleArithmetic, MakePayment,
@@ -57,34 +58,6 @@ pub trait Trait: system::Trait {
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
}
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
fn deposit_event<T>() = default;
/// Transfer some liquid free balance to another staker.
pub fn transfer(
origin,
dest: <T::Lookup as StaticLookup>::Source,
#[compact] value: T::Balance
) {
let transactor = ensure_signed(origin)?;
let dest = T::Lookup::lookup(dest)?;
Self::make_transfer(&transactor, &dest, value)?;
}
/// Set the balances of a given account.
fn set_balance(
who: <T::Lookup as StaticLookup>::Source,
#[compact] free: T::Balance,
#[compact] reserved: T::Balance
) {
let who = T::Lookup::lookup(who)?;
Self::set_free_balance(&who, free);
Self::set_reserved_balance(&who, reserved);
}
}
}
decl_event!(
pub enum Event<T> where
<T as system::Trait>::AccountId,
@@ -99,6 +72,27 @@ decl_event!(
}
);
/// Struct to encode the vesting schedule of an individual account.
#[derive(Encode, Decode, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct VestingSchedule<Balance> {
/// Locked amount at genesis.
pub offset: Balance,
/// Amount that gets unlocked every block from genesis.
pub per_block: Balance,
}
impl<Balance: SimpleArithmetic + Copy + As<u64>> VestingSchedule<Balance> {
/// Amount locked at block `n`.
pub fn locked_at<BlockNumber: As<u64>>(&self, n: BlockNumber) -> Balance {
if let Some(x) = Balance::sa(n.as_()).checked_mul(&self.per_block) {
self.offset.max(x) - x
} else {
Zero::zero()
}
}
}
decl_storage! {
trait Store for Module<T: Trait> as Balances {
/// The total amount of stake on the system.
@@ -112,6 +106,28 @@ decl_storage! {
/// The fee required to create an account. At least as big as ReclaimRebate.
pub CreationFee get(creation_fee) config(): T::Balance;
/// Information regarding the vesting of a given account.
pub Vesting get(vesting) build(|config: &GenesisConfig<T>| {
config.vesting.iter().filter_map(|&(ref who, begin, length)| {
let begin: u64 = begin.as_();
let length: u64 = length.as_();
let begin: T::Balance = As::sa(begin);
let length: T::Balance = As::sa(length);
config.balances.iter()
.find(|&&(ref w, _)| w == who)
.map(|&(_, balance)| {
// <= begin it should be >= balance
// >= begin+length it should be <= 0
let per_block = balance / length;
let offset = begin * per_block + balance;
(who.clone(), VestingSchedule { offset, per_block })
})
}).collect::<Vec<_>>()
}): map T::AccountId => Option<VestingSchedule<T::Balance>>;
/// The 'free' balance of a given account.
///
/// This is the only balance that matters in terms of most operations on tokens. It is
@@ -149,11 +165,50 @@ decl_storage! {
}
add_extra_genesis {
config(balances): Vec<(T::AccountId, T::Balance)>;
config(vesting): Vec<(T::AccountId, T::BlockNumber, T::BlockNumber)>; // begin, length
}
}
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
fn deposit_event<T>() = default;
/// Transfer some liquid free balance to another staker.
pub fn transfer(
origin,
dest: <T::Lookup as StaticLookup>::Source,
#[compact] value: T::Balance
) {
let transactor = ensure_signed(origin)?;
let dest = T::Lookup::lookup(dest)?;
Self::make_transfer(&transactor, &dest, value)?;
}
/// Set the balances of a given account.
fn set_balance(
who: <T::Lookup as StaticLookup>::Source,
#[compact] free: T::Balance,
#[compact] reserved: T::Balance
) {
let who = T::Lookup::lookup(who)?;
Self::set_free_balance(&who, free);
Self::set_reserved_balance(&who, reserved);
}
}
}
// For funding methods, see Currency trait
impl<T: Trait> Module<T> {
/// Get the amount that is currently being vested and cannot be transfered out of this account.
pub fn vesting_balance(who: &T::AccountId) -> T::Balance {
if let Some(v) = Self::vesting(who) {
Self::free_balance(who).min(v.locked_at(<system::Module<T>>::block_number()))
} else {
Zero::zero()
}
}
/// Set the free balance of an account to some new value.
///
/// Will enforce ExistentialDeposit law, anulling the account as needed.
@@ -260,9 +315,11 @@ impl<T: Trait> Module<T> {
None => return Err("got overflow after adding a fee to value"),
};
let vesting_balance = Self::vesting_balance(transactor);
let new_from_balance = match from_balance.checked_sub(&liability) {
Some(b) => b,
None => return Err("balance too low to send value"),
Some(b) if b < vesting_balance => return Err("vesting balance too high to send value"),
Some(b) => b,
};
if would_create && value < Self::existential_deposit() {
return Err("value too low to create account");
+1
View File
@@ -105,6 +105,7 @@ impl ExtBuilder {
existential_deposit: self.existential_deposit,
transfer_fee: self.transfer_fee,
creation_fee: self.creation_fee,
vesting: vec![],
}.build_storage().unwrap().0);
t.into()
}
+1
View File
@@ -170,6 +170,7 @@ impl ExtBuilder {
existential_deposit: self.existential_deposit,
transfer_fee: self.transfer_fee,
creation_fee: self.creation_fee,
vesting: vec![],
}
.build_storage()
.unwrap()
+1
View File
@@ -123,6 +123,7 @@ mod tests {
existential_deposit: 0,
transfer_fee: 0,
creation_fee: 0,
vesting: vec![],
}.build_storage().unwrap().0);
t.extend(democracy::GenesisConfig::<Test>{
launch_period: 1,
+1
View File
@@ -501,6 +501,7 @@ mod tests {
existential_deposit: 0,
transfer_fee: 0,
creation_fee: 0,
vesting: vec![],
}.build_storage().unwrap().0);
t.extend(GenesisConfig::<Test>{
launch_period: 1,
+1
View File
@@ -355,6 +355,7 @@ mod tests {
existential_deposit: 0,
transfer_fee: 0,
creation_fee: 0,
vesting: vec![],
}.build_storage().unwrap().0);
let xt = primitives::testing::TestXt(Some(1), 0, Call::transfer(2, 69));
let mut t = runtime_io::TestExternalities::<Blake2Hasher>::new(t);
+1
View File
@@ -108,6 +108,7 @@ pub fn new_test_ext(
existential_deposit: ext_deposit,
transfer_fee: 0,
creation_fee: 0,
vesting: vec![],
}.build_storage().unwrap().0);
t.extend(GenesisConfig::<Test>{
sessions_per_era,
+1
View File
@@ -308,6 +308,7 @@ mod tests {
transfer_fee: 0,
creation_fee: 0,
existential_deposit: 0,
vesting: vec![],
}.build_storage().unwrap().0);
t.extend(GenesisConfig::<Test>{
proposal_bond: Permill::from_percent(5),