From fd523c8025118ec5e89b7b0bb9f5ea689ff3b8c8 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 24 Jun 2021 19:35:08 -0700 Subject: [PATCH 001/144] Support multiple, mergable vesting schedules --- frame/vesting/src/lib.rs | 1492 ++++++++++++++++++++++++++++++++------ 1 file changed, 1264 insertions(+), 228 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 8c520b715801e..2ebeb0ce18ea0 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -47,11 +47,10 @@ mod benchmarking; pub mod weights; -use sp_std::prelude::*; -use sp_std::fmt::Debug; +use sp_std::{prelude::*, fmt::Debug, convert::TryInto}; use codec::{Encode, Decode}; use sp_runtime::{RuntimeDebug, traits::{ - StaticLookup, Zero, AtLeast32BitUnsigned, MaybeSerializeDeserialize, Convert + StaticLookup, Zero, AtLeast32BitUnsigned, MaybeSerializeDeserialize, Convert, Saturating, CheckedDiv }}; use frame_support::{ensure, pallet_prelude::*}; use frame_support::traits::{ @@ -61,41 +60,120 @@ use frame_support::traits::{ use frame_system::{ensure_signed, ensure_root, pallet_prelude::*}; pub use weights::WeightInfo; pub use pallet::*; +pub use vesting_info::*; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; type MaxLocksOf = <::Currency as LockableCurrency<::AccountId>>::MaxLocks; const VESTING_ID: LockIdentifier = *b"vesting "; -/// Struct to encode the vesting schedule of an individual account. -#[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug)] -pub struct VestingInfo { - /// Locked amount at genesis. - pub locked: Balance, - /// Amount that gets unlocked every block after `starting_block`. - pub per_block: Balance, - /// Starting block for unlocking(vesting). - pub starting_block: BlockNumber, -} +// Module to enforce private fields on `VestingInfo` +mod vesting_info { + use super::*; + /// Struct to encode the vesting schedule of an individual account. + #[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug)] + pub struct VestingInfo { + /// Locked amount at genesis. + locked: Balance, + /// Amount that gets unlocked every block after `starting_block`. + per_block: Balance, + /// Starting block for unlocking(vesting). + starting_block: BlockNumber, + } -impl< - Balance: AtLeast32BitUnsigned + Copy, - BlockNumber: AtLeast32BitUnsigned + Copy, -> VestingInfo { - /// Amount locked at block `n`. - pub fn locked_at< - BlockNumberToBalance: Convert - >(&self, n: BlockNumber) -> Balance { - // Number of blocks that count toward vesting - // Saturating to 0 when n < starting_block - let vested_block_count = n.saturating_sub(self.starting_block); - let vested_block_count = BlockNumberToBalance::convert(vested_block_count); - // Return amount that is still locked in vesting - let maybe_balance = vested_block_count.checked_mul(&self.per_block); - if let Some(balance) = maybe_balance { - self.locked.saturating_sub(balance) - } else { - Zero::zero() + impl + VestingInfo + { + /// Instantiate a new `VestingInfo` and validate parameters + pub fn try_new( + locked: Balance, + per_block: Balance, + starting_block: BlockNumber, + ) -> Result, Error> { + Self::validate_params(locked, per_block, starting_block)?; + let per_block = if per_block > locked { locked } else { per_block }; + Ok(VestingInfo { locked, per_block, starting_block }) + } + + /// Validate parameters for `VestingInfo`. + pub fn validate_params( + locked: Balance, + per_block: Balance, + _starting_block: BlockNumber, + ) -> Result<(), Error> { + ensure!(!locked.is_zero() && !per_block.is_zero(), Error::::InvalidScheduleParams); + let min_transfer: u32 = T::MinVestedTransfer::get().try_into().unwrap_or(u32::MAX); + let min_transfer = Balance::from(min_transfer); + // TODO - Do we want to enforce this here? This would keep from merging where sum of + // schedules is below MinVestedTransfer + ensure!(locked >= min_transfer, Error::::AmountLow); + Ok(()) + } + + /// Instantiate a new `VestingInfo` without param validation. Useful for + /// mocking bad inputs in testing. + pub fn unsafe_new( + locked: Balance, + per_block: Balance, + starting_block: BlockNumber, + ) -> VestingInfo { + VestingInfo { locked, per_block, starting_block } + } + + /// Locked amount at genesis. + pub fn locked(&self) -> Balance { + self.locked + } + + /// Amount that gets unlocked every block after `starting_block`. + pub fn per_block(&self) -> Balance { + self.per_block + } + + /// Starting block for unlocking(vesting). + pub fn starting_block(&self) -> BlockNumber { + self.starting_block + } + + /// Amount locked at block `n`. + pub fn locked_at>( + &self, + n: BlockNumber, + ) -> Balance { + // Number of blocks that count toward vesting + // Saturating to 0 when n < starting_block + let vested_block_count = n.saturating_sub(self.starting_block); + let vested_block_count = BlockNumberToBalance::convert(vested_block_count); + // Return amount that is still locked in vesting + let maybe_balance = vested_block_count.checked_mul(&self.per_block); + if let Some(balance) = maybe_balance { + self.locked.saturating_sub(balance) + } else { + Zero::zero() + } + } + + /// Block number at which the schedule ends + pub fn ending_block>(&self) -> Balance { + let starting_block = BlockNumberToBalance::convert(self.starting_block); + let duration = if self.per_block > self.locked { + // If `per_block` is bigger than `locked`, the schedule will end + // the block after starting + 1u32.into() + } else if self.per_block.is_zero() { + // Check for div by 0 errors, which should only be from legacy + // vesting schedules since new ones are validated for this. + self.locked + } else { + let has_remainder = !(self.locked % self.per_block).is_zero(); + let maybe_duration = self.locked / self.per_block; + if has_remainder { + maybe_duration + 1u32.into() + } else { + maybe_duration + } + }; + starting_block.saturating_add(duration) } } } @@ -121,6 +199,10 @@ pub mod pallet { /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; + + /// Maximum number of vesting schedules an account may have at a given moment. + #[pallet::constant] + type MaxVestingSchedules: Get; } /// Information regarding the vesting of a given account. @@ -130,7 +212,7 @@ pub mod pallet { _, Blake2_128Concat, T::AccountId, - VestingInfo, T::BlockNumber>, + BoundedVec, T::BlockNumber>, T::MaxVestingSchedules> >; #[pallet::pallet] @@ -153,7 +235,7 @@ pub mod pallet { #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { - fn build(&self) { + fn build(&self) { use sp_runtime::traits::Saturating; // Generate initial vesting configuration @@ -168,21 +250,22 @@ pub mod pallet { let locked = balance.saturating_sub(liquid); let length_as_balance = T::BlockNumberToBalance::convert(length); let per_block = locked / length_as_balance.max(sp_runtime::traits::One::one()); + let vesting_info = VestingInfo::try_new::(locked, per_block, begin) + .expect("Invalid VestingInfo params at genesis"); - Vesting::::insert(who, VestingInfo { - locked: locked, - per_block: per_block, - starting_block: begin - }); + Vesting::::try_append(who, vesting_info) + .expect("Too many vesting schedules at genesis."); let reasons = WithdrawReasons::TRANSFER | WithdrawReasons::RESERVE; T::Currency::set_lock(VESTING_ID, who, locked, reasons); } } - } + } #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] - #[pallet::metadata(T::AccountId = "AccountId", BalanceOf = "Balance")] + #[pallet::metadata( + T::AccountId = "AccountId", BalanceOf = "Balance", T::BlockNumber = "BlockNumber" + )] pub enum Event { /// The amount vested has been updated. This could indicate more funds are available. The /// balance given is the amount which is left unvested (and thus locked). @@ -190,6 +273,9 @@ pub mod pallet { VestingUpdated(T::AccountId, BalanceOf), /// An \[account\] has become fully vested. No further vesting can happen. VestingCompleted(T::AccountId), + /// 2 vesting schedules where successfully merged together. + ///\[locked, per_block, starting_block\] + VestingMergeSuccess(BalanceOf, BalanceOf, T::BlockNumber), } /// Error for the vesting pallet. @@ -197,10 +283,16 @@ pub mod pallet { pub enum Error { /// The account given is not vesting. NotVesting, - /// An existing vesting schedule already exists for this account that cannot be clobbered. - ExistingVestingSchedule, + /// The account already has `MaxVestingSchedules` number of schedules and thus + /// cannot add another one. Consider merging existing schedules in order to add another. + AtMaxVestingSchedules, /// Amount being transferred is too low to create a vesting schedule. AmountLow, + /// At least one of the indexes is out of bounds of the vesting schedules. + ScheduleIndexOutOfBounds, + /// Failed to create a new schedule because the parameters where invalid. i.e. `per_block` or + /// `locked` was a 0. + InvalidScheduleParams, } #[pallet::call] @@ -223,7 +315,14 @@ pub mod pallet { )] pub fn vest(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - Self::update_lock(who) + let vesting = Self::vesting(&who).ok_or(Error::::NotVesting)?; + let maybe_vesting = Self::update_lock_and_schedules(who.clone(), vesting, vec![]); + if let Some(vesting) = maybe_vesting { + Vesting::::insert(&who, vesting); + } else { + Vesting::::remove(&who); + } + Ok(()) } /// Unlock any vested funds of a `target` account. @@ -246,7 +345,15 @@ pub mod pallet { )] pub fn vest_other(origin: OriginFor, target: ::Source) -> DispatchResult { ensure_signed(origin)?; - Self::update_lock(T::Lookup::lookup(target)?) + let who = T::Lookup::lookup(target)?; + let vesting = Self::vesting(&who).ok_or(Error::::NotVesting)?; + let maybe_vesting = Self::update_lock_and_schedules(who.clone(), vesting, vec![]); + if let Some(vesting) = maybe_vesting { + Vesting::::insert(&who, vesting); + } else { + Vesting::::remove(&who); + } + Ok(()) } /// Create a vested transfer. @@ -254,7 +361,6 @@ pub mod pallet { /// The dispatch origin for this call must be _Signed_. /// /// - `target`: The account that should be transferred the vested funds. - /// - `amount`: The amount of funds to transfer and will be vested. /// - `schedule`: The vesting schedule attached to the transfer. /// /// Emits `VestingCreated`. @@ -272,17 +378,8 @@ pub mod pallet { schedule: VestingInfo, T::BlockNumber>, ) -> DispatchResult { let transactor = ensure_signed(origin)?; - ensure!(schedule.locked >= T::MinVestedTransfer::get(), Error::::AmountLow); - - let who = T::Lookup::lookup(target)?; - ensure!(!Vesting::::contains_key(&who), Error::::ExistingVestingSchedule); - - T::Currency::transfer(&transactor, &who, schedule.locked, ExistenceRequirement::AllowDeath)?; - - Self::add_vesting_schedule(&who, schedule.locked, schedule.per_block, schedule.starting_block) - .expect("user does not have an existing vesting schedule; q.e.d."); - - Ok(()) + let transactor = ::unlookup(transactor); + Self::do_vested_transfer(transactor, target, schedule) } /// Force a vested transfer. @@ -291,7 +388,6 @@ pub mod pallet { /// /// - `source`: The account whose funds should be transferred. /// - `target`: The account that should be transferred the vested funds. - /// - `amount`: The amount of funds to transfer and will be vested. /// - `schedule`: The vesting schedule attached to the transfer. /// /// Emits `VestingCreated`. @@ -310,16 +406,67 @@ pub mod pallet { schedule: VestingInfo, T::BlockNumber>, ) -> DispatchResult { ensure_root(origin)?; - ensure!(schedule.locked >= T::MinVestedTransfer::get(), Error::::AmountLow); - - let target = T::Lookup::lookup(target)?; - let source = T::Lookup::lookup(source)?; - ensure!(!Vesting::::contains_key(&target), Error::::ExistingVestingSchedule); + Self::do_vested_transfer(source, target, schedule) + } - T::Currency::transfer(&source, &target, schedule.locked, ExistenceRequirement::AllowDeath)?; + /// Merge two vesting schedules together, creating a new vesting schedule that vests over + /// the maximum of the original two schedules duration. + /// + /// NOTE: this will vest all schedules through the current block prior to merging. However, + /// the schedule indexes are based off of the ordering prior to schedules being vested/filtered. + /// + /// NOTE: If `schedule1_index == schedule2_index` this is a no-op. + /// + /// The dispatch origin for this call must be _Signed_. + /// + /// - `schedule1_index`: TODO + /// - `schedule2_index`: TODO + /// + /// # + /// - `O(1)`. + /// - DbWeight: TODO Reads, TODO Writes + /// - Reads: TODO + /// - Writes: TODO + /// # + #[pallet::weight(123)] // TODO + pub fn merge_schedules( + origin: OriginFor, + schedule1_index: u32, + schedule2_index: u32, + ) -> DispatchResult { + if schedule1_index == schedule2_index { + return Ok(()); + }; + let who = ensure_signed(origin)?; + let schedule1_index = schedule1_index as usize; + let schedule2_index = schedule2_index as usize; + let vesting = Self::vesting(&who).ok_or(Error::::NotVesting)?; + let len = vesting.len(); + ensure!(schedule1_index < len && schedule2_index < len, Error::::ScheduleIndexOutOfBounds); + + // The schedule index is based off of the schedule ordering prior to filtering out any + // schedules that may be ending at this block. + let schedule1 = vesting[schedule1_index]; + let schedule2 = vesting[schedule2_index]; + let filter = vec![schedule1_index, schedule2_index]; + // The length of vesting decreases by 2 here since we filter out 2 schedules. So we know + // below we have the space to insert the merged schedule. + let maybe_vesting = Self::update_lock_and_schedules(who.clone(), vesting, filter); - Self::add_vesting_schedule(&target, schedule.locked, schedule.per_block, schedule.starting_block) - .expect("user does not have an existing vesting schedule; q.e.d."); + let now = >::block_number(); + if let Some(s) = Self::merge_vesting_info(now, schedule1, schedule2) { + let mut vesting = maybe_vesting.unwrap_or_default(); + // It shouldn't be possible for this to fail because we removed 2 schedules above. + ensure!(vesting.try_push(s).is_ok(), Error::::AtMaxVestingSchedules); + Self::deposit_event( + Event::::VestingMergeSuccess(s.locked(), s.per_block(), s.starting_block()) + ); + Vesting::::insert(&who, vesting); + } else if maybe_vesting.is_some() { + Vesting::::insert(&who, maybe_vesting.unwrap()); + } else { + Vesting::::remove(&who); + } Ok(()) } @@ -327,23 +474,132 @@ pub mod pallet { } impl Pallet { + // Create a new `VestingInfo`, based off of two other `VestingInfo`s. + // Note: We assume both schedules have been vested up through the current block. + fn merge_vesting_info( + now: T::BlockNumber, + schedule1: VestingInfo, T::BlockNumber>, + schedule2: VestingInfo, T::BlockNumber>, + ) -> Option, T::BlockNumber>> { + let schedule1_ending_block = schedule1.ending_block::(); + let schedule2_ending_block = schedule2.ending_block::(); + let now_as_balance = T::BlockNumberToBalance::convert(now); + if schedule1_ending_block <= now_as_balance && schedule2_ending_block <= now_as_balance { + // If both schedule has ended, we don't merge + return None; + } else if schedule1_ending_block <= now_as_balance { + // If one schedule has ended, we treat the one that has not ended as the new "merged schedule" + return Some(schedule2) + } else if schedule2_ending_block <= now_as_balance { + return Some(schedule1) + } + let locked = schedule1.locked_at::(now) + .saturating_add(schedule2.locked_at::(now)); + // This shouldn't happen because we know at least one ending block is greater than now. + if locked.is_zero() { return None; } + + let ending_block = schedule1_ending_block.max(schedule2_ending_block); + let starting_block = now + .max(schedule1.starting_block()) + .max(schedule2.starting_block()); + let duration = + ending_block.saturating_sub(T::BlockNumberToBalance::convert(starting_block)); + let per_block = if duration.is_zero() { + // The logic of `ending_block` guarantees that each schedule ends at least a block + // after it starts and since we take the max starting and ending_block we should never get here + locked + } else if duration > locked { + // This would mean we have a per_block of less than 1, which should not be not possible + // because when we merge the new schedule is at most the same duration as the longest, but + // never greater + 1u32.into() + } else { + locked.checked_div(&duration)? + }; + + // At this point inputs have been validated, so this should always be `Some` + VestingInfo::try_new::(locked, per_block, starting_block).ok() + } + + // Execute a vested transfer from `source` to `target` with the given `schedule`. + fn do_vested_transfer( + source: ::Source, + target: ::Source, + schedule: VestingInfo, T::BlockNumber>, + ) -> DispatchResult { + VestingInfo::validate_params::( + schedule.locked(), + schedule.per_block(), + schedule.starting_block(), + )?; + + let target = T::Lookup::lookup(target)?; + let source = T::Lookup::lookup(source)?; + if let Some(len) = Vesting::::decode_len(&target) { + ensure!( + len < T::MaxVestingSchedules::get() as usize, + Error::::AtMaxVestingSchedules + ); + } + + T::Currency::transfer( + &source, + &target, + schedule.locked(), + ExistenceRequirement::AllowDeath, + )?; + + // We can't let this fail because the currency transfer has already happened + Self::add_vesting_schedule( + &target, + schedule.locked(), + schedule.per_block(), + schedule.starting_block(), + ) + .expect("schedule inputs and vec bounds validated. q.e."); + + Ok(()) + } + /// (Re)set or remove the pallet's currency lock on `who`'s account in accordance with their - /// current unvested amount. - fn update_lock(who: T::AccountId) -> DispatchResult { - let vesting = Self::vesting(&who).ok_or(Error::::NotVesting)?; + /// current unvested amount and prune any vesting schedules that have completed. + /// + /// NOTE: This will update the users lock, but will not read/write the `Vesting` storage item. + fn update_lock_and_schedules( + who: T::AccountId, + vesting: BoundedVec, T::BlockNumber>, T::MaxVestingSchedules>, + filter: Vec, + ) -> Option, T::BlockNumber>, T::MaxVestingSchedules>> { let now = >::block_number(); - let locked_now = vesting.locked_at::(now); - if locked_now.is_zero() { + let mut total_locked_now: BalanceOf = Zero::zero(); + let still_vesting = vesting + .into_iter() + .enumerate() + .filter_map(| (i, schedule) | { + let locked_now = schedule.locked_at::(now); + total_locked_now = total_locked_now.saturating_add(locked_now); + if locked_now.is_zero() || filter.contains(&i) { + None + } else { + Some(schedule) + } + }) + .collect::>() + .try_into() + .expect("`BoundedVec` is created from another `BoundedVec` with same bound; q.e.d."); + + if total_locked_now.is_zero() { T::Currency::remove_lock(VESTING_ID, &who); Vesting::::remove(&who); Self::deposit_event(Event::::VestingCompleted(who)); + None } else { let reasons = WithdrawReasons::TRANSFER | WithdrawReasons::RESERVE; - T::Currency::set_lock(VESTING_ID, &who, locked_now, reasons); - Self::deposit_event(Event::::VestingUpdated(who, locked_now)); + T::Currency::set_lock(VESTING_ID, &who, total_locked_now, reasons); + Self::deposit_event(Event::::VestingUpdated(who, total_locked_now)); + Some(still_vesting) } - Ok(()) } } @@ -353,12 +609,17 @@ impl VestingSchedule for Pallet where type Moment = T::BlockNumber; type Currency = T::Currency; + // TODO should we expose merge vesting schedules here? Its a bit dangerous because schedules + // need to be vested before being merged + /// Get the amount that is currently being vested and cannot be transferred out of this account. fn vesting_balance(who: &T::AccountId) -> Option> { if let Some(v) = Self::vesting(who) { let now = >::block_number(); - let locked_now = v.locked_at::(now); - Some(T::Currency::free_balance(who).min(locked_now)) + let total_locked_now = v.iter().fold(Zero::zero(), |total, schedule| { + schedule.locked_at::(now).saturating_add(total) + }); + Some(T::Currency::free_balance(who).min(total_locked_now)) } else { None } @@ -366,8 +627,8 @@ impl VestingSchedule for Pallet where /// Adds a vesting schedule to a given account. /// - /// If there already exists a vesting schedule for the given account, an `Err` is returned - /// and nothing is updated. + /// If there already `MaxVestingSchedules`, an `Err` is returned and nothing + /// is updated. /// /// On success, a linearly reducing amount of funds will be locked. In order to realise any /// reduction of the lock over time as it diminishes, the account owner must use `vest` or @@ -381,27 +642,38 @@ impl VestingSchedule for Pallet where starting_block: T::BlockNumber ) -> DispatchResult { if locked.is_zero() { return Ok(()) } - if Vesting::::contains_key(who) { - Err(Error::::ExistingVestingSchedule)? - } - let vesting_schedule = VestingInfo { - locked, - per_block, - starting_block + let vesting_schedule = VestingInfo::try_new::( + locked, per_block, starting_block + )?; + let mut vesting = if let Some(v) = Self::vesting(who) { + v + } else { + BoundedVec::default() }; - Vesting::::insert(who, vesting_schedule); - // it can't fail, but even if somehow it did, we don't really care. - let res = Self::update_lock(who.clone()); - debug_assert!(res.is_ok()); + ensure!(vesting.try_push(vesting_schedule).is_ok(), Error::::AtMaxVestingSchedules); + if let Some(v) = Self::update_lock_and_schedules(who.clone(), vesting, vec![]) { + Vesting::::insert(&who, v); + } else { + Vesting::::remove(&who); + } Ok(()) } /// Remove a vesting schedule for a given account. - fn remove_vesting_schedule(who: &T::AccountId) { - Vesting::::remove(who); - // it can't fail, but even if somehow it did, we don't really care. - let res = Self::update_lock(who.clone()); - debug_assert!(res.is_ok()); + fn remove_vesting_schedule(who: &T::AccountId, schedule_index: Option) -> DispatchResult { + let filter = if let Some(schedule_index) = schedule_index { + ensure!(schedule_index < T::MaxVestingSchedules::get(), Error::::ScheduleIndexOutOfBounds); + vec![schedule_index as usize] + } else { + vec![] + }; + let vesting= Self::vesting(who).ok_or(Error::::NotVesting)?; + if let Some(v) = Self::update_lock_and_schedules(who.clone(), vesting, filter) { + Vesting::::insert(&who, v); + } else { + Vesting::::remove(&who); + }; + Ok(()) } } @@ -478,8 +750,9 @@ mod tests { type WeightInfo = (); } parameter_types! { - pub const MinVestedTransfer: u64 = 256 * 2; + pub const MinVestedTransfer: u64 = 10; pub static ExistentialDeposit: u64 = 0; + pub const MaxVestingSchedules: u32 = 3; } impl Config for Test { type Event = Event; @@ -487,15 +760,19 @@ mod tests { type BlockNumberToBalance = Identity; type MinVestedTransfer = MinVestedTransfer; type WeightInfo = (); + type MaxVestingSchedules = MaxVestingSchedules; } pub struct ExtBuilder { existential_deposit: u64, + vesting_genesis_config: Option>, } + impl Default for ExtBuilder { fn default() -> Self { Self { existential_deposit: 1, + vesting_genesis_config: None, } } } @@ -504,6 +781,10 @@ mod tests { self.existential_deposit = existential_deposit; self } + pub fn vesting_genesis_config(mut self, config: Vec<(u64, u64, u64, u64)>) -> Self { + self.vesting_genesis_config = Some(config); + self + } pub fn build(self) -> sp_io::TestExternalities { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); @@ -516,49 +797,56 @@ mod tests { (12, 10 * self.existential_deposit) ], }.assimilate_storage(&mut t).unwrap(); - pallet_vesting::GenesisConfig:: { - vesting: vec![ + + let vesting = if let Some(vesting_config) = self.vesting_genesis_config { + vesting_config + } else { + vec![ (1, 0, 10, 5 * self.existential_deposit), (2, 10, 20, 0), (12, 10, 20, 5 * self.existential_deposit) - ], + ] + }; + + pallet_vesting::GenesisConfig:: { + vesting }.assimilate_storage(&mut t).unwrap(); let mut ext = sp_io::TestExternalities::new(t); ext.execute_with(|| System::set_block_number(1)); ext } - } + } #[test] fn check_vesting_status() { - ExtBuilder::default() - .existential_deposit(256) - .build() - .execute_with(|| { - let user1_free_balance = Balances::free_balance(&1); - let user2_free_balance = Balances::free_balance(&2); - let user12_free_balance = Balances::free_balance(&12); - assert_eq!(user1_free_balance, 256 * 10); // Account 1 has free balance - assert_eq!(user2_free_balance, 256 * 20); // Account 2 has free balance - assert_eq!(user12_free_balance, 256 * 10); // Account 12 has free balance - let user1_vesting_schedule = VestingInfo { - locked: 256 * 5, - per_block: 128, // Vesting over 10 blocks - starting_block: 0, - }; - let user2_vesting_schedule = VestingInfo { - locked: 256 * 20, - per_block: 256, // Vesting over 20 blocks - starting_block: 10, - }; - let user12_vesting_schedule = VestingInfo { - locked: 256 * 5, - per_block: 64, // Vesting over 20 blocks - starting_block: 10, - }; - assert_eq!(Vesting::vesting(&1), Some(user1_vesting_schedule)); // Account 1 has a vesting schedule - assert_eq!(Vesting::vesting(&2), Some(user2_vesting_schedule)); // Account 2 has a vesting schedule - assert_eq!(Vesting::vesting(&12), Some(user12_vesting_schedule)); // Account 12 has a vesting schedule + ExtBuilder::default().existential_deposit(256).build().execute_with(|| { + let user1_free_balance = Balances::free_balance(&1); + let user2_free_balance = Balances::free_balance(&2); + let user12_free_balance = Balances::free_balance(&12); + assert_eq!(user1_free_balance, 256 * 10); // Account 1 has free balance + assert_eq!(user2_free_balance, 256 * 20); // Account 2 has free balance + assert_eq!(user12_free_balance, 256 * 10); // Account 12 has free balance + let user1_vesting_schedule = VestingInfo::try_new::( + 256 * 5, + 128, // Vesting over 10 blocks + 0, + ) + .unwrap(); + let user2_vesting_schedule = VestingInfo::try_new::( + 256 * 20, + 256, // Vesting over 20 blocks + 10, + ) + .unwrap(); + let user12_vesting_schedule = VestingInfo::try_new::( + 256 * 5, + 64, // Vesting over 20 blocks + 10, + ) + .unwrap(); + assert_eq!(Vesting::vesting(&1).unwrap(), vec![user1_vesting_schedule]); // Account 1 has a vesting schedule + assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); // Account 2 has a vesting schedule + assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); // Account 12 has a vesting schedule // Account 1 has only 128 units vested from their illiquid 256 * 5 units at block 1 assert_eq!(Vesting::vesting_balance(&1), Some(128 * 9)); @@ -583,7 +871,98 @@ mod tests { assert_eq!(Vesting::vesting_balance(&1), Some(0)); // Account 1 is still fully vested, and not negative assert_eq!(Vesting::vesting_balance(&2), Some(0)); // Account 2 has fully vested by block 30 assert_eq!(Vesting::vesting_balance(&12), Some(0)); // Account 2 has fully vested by block 30 + }); + } + + #[test] + fn check_vesting_status_for_multi_schedule_account() { + let existential_deposit = 256; + ExtBuilder::default() + .existential_deposit(existential_deposit) + .build() + .execute_with(|| { + assert_eq!(System::block_number(), 1); + let sched0 = VestingInfo::try_new::( + existential_deposit * 20, + existential_deposit, // Vesting over 20 blocks + 10, + ) + .unwrap(); + + // Use 2 already has a vesting schedule. + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + + // User 2's free balance is from sched0 + let free_balance = Balances::free_balance(&2); + assert_eq!(free_balance, existential_deposit * (20)); + assert_eq!(Vesting::vesting_balance(&2), Some(free_balance)); + + let sched1 = VestingInfo::try_new::( + existential_deposit * 10, + existential_deposit, // Vesting over 10 blocks + 0, + ) + .unwrap(); + assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched1)); + // Free balance is equal to the two existing schedules total amount + let free_balance = Balances::free_balance(&2); + assert_eq!(free_balance, existential_deposit * (10 + 20)); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); + // sched1 has free funds at block #1, but nothing else + assert_eq!(Vesting::vesting_balance(&2), Some(free_balance - sched1.per_block())); + + System::set_block_number(4); + assert_eq!(System::block_number(), 4); + // sched1 has freeing funds at block #4, but nothing else + assert_eq!( + Vesting::vesting_balance(&2), + Some(free_balance - sched1.per_block() * 4), + ); + + let sched2 = VestingInfo::try_new::( + existential_deposit * 30, + existential_deposit, // Vesting over 30 blocks + 5, + ) + .unwrap(); + // Add a 3rd schedule for user t2 + assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched2)); + + System::set_block_number(9); + assert_eq!(System::block_number(), 9); + // Free balance is equal to the two existing schedules total amount + let free_balance = Balances::free_balance(&2); + assert_eq!(free_balance, existential_deposit * (10 + 20 + 30)); + // sched1 and sched2 are freeing funds at block #9 + assert_eq!( + Vesting::vesting_balance(&2), + Some(free_balance - sched1.per_block() * 9 - sched2.per_block() * 4) + ); + + System::set_block_number(20); + assert_eq!(System::block_number(), 20); + assert_eq!( + Vesting::vesting_balance(&2), + Some( + free_balance - + sched1.locked() - sched2.per_block() * 15 - + sched0.per_block() * 10 + ) + ); + + System::set_block_number(30); + assert_eq!(System::block_number(), 30); + assert_eq!( + Vesting::vesting_balance(&2), + Some( + free_balance - sched1.locked() - sched2.per_block() * 25 - sched0.locked() + ) + ); + // Fully vested now that sched2 has finished + System::set_block_number(35); + assert_eq!(System::block_number(), 35); + assert_eq!(Vesting::vesting_balance(&2), Some(0)); }); } @@ -674,12 +1053,12 @@ mod tests { assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); // Account 12 has delayed vesting - let user12_vesting_schedule = VestingInfo { - locked: 256 * 5, - per_block: 64, // Vesting over 20 blocks - starting_block: 10, - }; - assert_eq!(Vesting::vesting(&12), Some(user12_vesting_schedule)); + let user12_vesting_schedule = VestingInfo::try_new::( + 256 * 5, + 64, // Vesting over 20 blocks + 10, + ).unwrap(); + assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); // Account 12 can still send liquid funds assert_ok!(Balances::transfer(Some(12).into(), 3, 256 * 5)); @@ -699,14 +1078,14 @@ mod tests { // Account 4 should not have any vesting yet. assert_eq!(Vesting::vesting(&4), None); // Make the schedule for the new transfer. - let new_vesting_schedule = VestingInfo { - locked: 256 * 5, - per_block: 64, // Vesting over 20 blocks - starting_block: 10, - }; + let new_vesting_schedule = VestingInfo::try_new::( + 256 * 5, + 64, // Vesting over 20 blocks + 10, + ).unwrap(); assert_ok!(Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule)); // Now account 4 should have vesting. - assert_eq!(Vesting::vesting(&4), Some(new_vesting_schedule)); + assert_eq!(Vesting::vesting(&4).unwrap(), vec![new_vesting_schedule]); // Ensure the transfer happened correctly. let user3_free_balance_updated = Balances::free_balance(&3); assert_eq!(user3_free_balance_updated, 256 * 25); @@ -739,129 +1118,786 @@ mod tests { let user4_free_balance = Balances::free_balance(&4); assert_eq!(user2_free_balance, 256 * 20); assert_eq!(user4_free_balance, 256 * 40); + // Account 2 should already have a vesting schedule. - let user2_vesting_schedule = VestingInfo { - locked: 256 * 20, - per_block: 256, // Vesting over 20 blocks - starting_block: 10, - }; - assert_eq!(Vesting::vesting(&2), Some(user2_vesting_schedule)); - - // The vesting schedule we will try to create, fails due to pre-existence of schedule. - let new_vesting_schedule = VestingInfo { - locked: 256 * 5, - per_block: 64, // Vesting over 20 blocks - starting_block: 10, - }; - assert_noop!( - Vesting::vested_transfer(Some(4).into(), 2, new_vesting_schedule), - Error::::ExistingVestingSchedule, - ); + let user2_vesting_schedule = VestingInfo::try_new::( + 256 * 20, + 256, // Vesting over 20 blocks + 10, + ).unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); // Fails due to too low transfer amount. - let new_vesting_schedule_too_low = VestingInfo { - locked: 256 * 1, - per_block: 64, - starting_block: 10, - }; + let new_vesting_schedule_too_low = VestingInfo::unsafe_new( + ::MinVestedTransfer::get() - 1, + 64, + 10, + ); assert_noop!( Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule_too_low), Error::::AmountLow, ); - // Verify no currency transfer happened. - assert_eq!(user2_free_balance, 256 * 20); - assert_eq!(user4_free_balance, 256 * 40); + // `per_block` is 0. + let schedule_per_block_0 = VestingInfo::unsafe_new( + 256, + 0, + 10, + ); + assert_noop!( + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_per_block_0), + Error::::InvalidScheduleParams, + ); + + // `locked` is 0. + let schedule_locked_0 = VestingInfo::unsafe_new( + 0, + 1, + 10, + ); + assert_noop!( + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), + Error::::InvalidScheduleParams, + ); + + // Free balance has not changed. + assert_eq!(user2_free_balance, Balances::free_balance(&2)); + assert_eq!(user4_free_balance, Balances::free_balance(&4)); + + // Add max amount schedules to user 4, (additionally asserting that vested_transfer + // works with multiple schedules). + for _ in 0..::MaxVestingSchedules::get() - 1 { + assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, user2_vesting_schedule)); + } + // Try to insert a 4th vesting schedule when `MaxVestingSchedules` === 3. + assert_noop!( + Vesting::vested_transfer(Some(4).into(), 2, user2_vesting_schedule), + Error::::AtMaxVestingSchedules, + ); }); } #[test] fn force_vested_transfer_works() { + ExtBuilder::default().existential_deposit(256).build().execute_with(|| { + let user3_free_balance = Balances::free_balance(&3); + let user4_free_balance = Balances::free_balance(&4); + assert_eq!(user3_free_balance, 256 * 30); + assert_eq!(user4_free_balance, 256 * 40); + // Account 4 should not have any vesting yet. + assert_eq!(Vesting::vesting(&4), None); + // Make the schedule for the new transfer. + let new_vesting_schedule = VestingInfo::try_new::( + 256 * 5, + 64, // Vesting over 20 blocks + 10, + ) + .unwrap(); + + assert_noop!( + Vesting::force_vested_transfer(Some(4).into(), 3, 4, new_vesting_schedule), + BadOrigin + ); + assert_ok!(Vesting::force_vested_transfer( + RawOrigin::Root.into(), + 3, + 4, + new_vesting_schedule + )); + // Now account 4 should have vesting. + assert_eq!(Vesting::vesting(&4).unwrap()[0], new_vesting_schedule); + assert_eq!(Vesting::vesting(&4).unwrap().len(), 1); + // Ensure the transfer happened correctly. + let user3_free_balance_updated = Balances::free_balance(&3); + assert_eq!(user3_free_balance_updated, 256 * 25); + let user4_free_balance_updated = Balances::free_balance(&4); + assert_eq!(user4_free_balance_updated, 256 * 45); + // Account 4 has 5 * 256 locked. + assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5)); + + System::set_block_number(20); + assert_eq!(System::block_number(), 20); + + // Account 4 has 5 * 64 units vested by block 20. + assert_eq!(Vesting::vesting_balance(&4), Some(10 * 64)); + + System::set_block_number(30); + assert_eq!(System::block_number(), 30); + + // Account 4 has fully vested. + assert_eq!(Vesting::vesting_balance(&4), Some(0)); + }); + } + + #[test] + fn force_vested_transfer_correctly_fails() { + ExtBuilder::default().existential_deposit(256).build().execute_with(|| { + let user2_free_balance = Balances::free_balance(&2); + let user4_free_balance = Balances::free_balance(&4); + assert_eq!(user2_free_balance, 256 * 20); + assert_eq!(user4_free_balance, 256 * 40); + // Account 2 should already have a vesting schedule. + let user2_vesting_schedule = VestingInfo::try_new::( + 256 * 20, + 256, // Vesting over 20 blocks + 10, + ) + .unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); + + let new_vesting_schedule = VestingInfo::try_new::( + 256 * 5, + 64, // Vesting over 20 blocks + 10, + ) + .unwrap(); + + // Too low transfer amount. + let new_vesting_schedule_too_low = + VestingInfo::unsafe_new(::MinVestedTransfer::get() - 1, 64, 10); + assert_noop!( + Vesting::force_vested_transfer( + RawOrigin::Root.into(), + 3, + 4, + new_vesting_schedule_too_low + ), + Error::::AmountLow, + ); + + // `per_block` is 0 + let schedule_per_block_0 = VestingInfo::unsafe_new(256, 0, 10); + assert_noop!( + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_per_block_0), + Error::::InvalidScheduleParams, + ); + + // `locked` is 0 + let schedule_locked_0 = VestingInfo::unsafe_new(0, 1, 10); + assert_noop!( + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), + Error::::InvalidScheduleParams, + ); + + // Verify no currency transfer happened. + assert_eq!(user2_free_balance, Balances::free_balance(&2)); + assert_eq!(user4_free_balance, Balances::free_balance(&4)); + + // Add max amount schedules to user 4, (additionally asserting that force_vested_transfer + // works with multiple schedules). + for _ in 0 .. ::MaxVestingSchedules::get() - 1 { + assert_ok!(Vesting::force_vested_transfer( + RawOrigin::Root.into(), + 4, + 2, + new_vesting_schedule + )); + } + // Try to insert a 4th vesting schedule when `MaxVestingSchedules` === 3. + assert_noop!( + Vesting::vested_transfer(Some(4).into(), 2, user2_vesting_schedule), + Error::::AtMaxVestingSchedules, + ); + }); + } + + #[test] + fn max_vesting_schedules_bounds_vesting_schedules() { + let existential_deposit = 256; + // Test `vested_transfer` ExtBuilder::default() - .existential_deposit(256) + .existential_deposit(existential_deposit) .build() .execute_with(|| { - let user3_free_balance = Balances::free_balance(&3); - let user4_free_balance = Balances::free_balance(&4); - assert_eq!(user3_free_balance, 256 * 30); - assert_eq!(user4_free_balance, 256 * 40); - // Account 4 should not have any vesting yet. - assert_eq!(Vesting::vesting(&4), None); - // Make the schedule for the new transfer. - let new_vesting_schedule = VestingInfo { - locked: 256 * 5, - per_block: 64, // Vesting over 20 blocks - starting_block: 10, - }; - assert_noop!(Vesting::force_vested_transfer(Some(4).into(), 3, 4, new_vesting_schedule), BadOrigin); - assert_ok!(Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, new_vesting_schedule)); - // Now account 4 should have vesting. - assert_eq!(Vesting::vesting(&4), Some(new_vesting_schedule)); - // Ensure the transfer happened correctly. - let user3_free_balance_updated = Balances::free_balance(&3); - assert_eq!(user3_free_balance_updated, 256 * 25); - let user4_free_balance_updated = Balances::free_balance(&4); - assert_eq!(user4_free_balance_updated, 256 * 45); - // Account 4 has 5 * 256 locked. - assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5)); + let new_vesting_schedule = + VestingInfo::try_new::(existential_deposit * 5, existential_deposit, 10) + .unwrap(); + + assert_eq!(Vesting::vesting(&3), None); + for _ in 0 .. ::MaxVestingSchedules::get() { + assert_ok!(Vesting::vested_transfer(Some(4).into(), 3, new_vesting_schedule)); + } + assert_noop!( + Vesting::vested_transfer(Some(4).into(), 3, new_vesting_schedule), + Error::::AtMaxVestingSchedules, + ); + }); - System::set_block_number(20); - assert_eq!(System::block_number(), 20); + // Test `force_vested_transfer` + ExtBuilder::default() + .existential_deposit(existential_deposit) + .build() + .execute_with(|| { + let new_vesting_schedule = + VestingInfo::try_new::(existential_deposit * 5, existential_deposit, 10) + .unwrap(); + + assert_eq!(Vesting::vesting(&3), None); + for _ in 0 .. ::MaxVestingSchedules::get() { + assert_ok!( + Vesting::force_vested_transfer( + RawOrigin::Root.into(), + 4, + 3, + new_vesting_schedule + ) + ); + } + assert_noop!( + Vesting::force_vested_transfer( + RawOrigin::Root.into(), + 4, + 3, + new_vesting_schedule + ), + Error::::AtMaxVestingSchedules, + ); + }); + } - // Account 4 has 5 * 64 units vested by block 20. - assert_eq!(Vesting::vesting_balance(&4), Some(10 * 64)); + #[test] + fn merge_schedules_that_have_not_started() { + // Merging schedules that have not started works + let existential_deposit = 256; + ExtBuilder::default() + .existential_deposit(existential_deposit) + .build() + .execute_with(|| { + // Account 2 should already have a vesting schedule. + let sched0 = + VestingInfo::try_new::(existential_deposit * 20, existential_deposit, 10) + .unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + assert_eq!(Balances::usable_balance(&2), 0); + + // Add a schedule that is identical to the one that already exists + Vesting::vested_transfer(Some(3).into(), 2, sched0).unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched0]); + assert_eq!(Balances::usable_balance(&2), 0); + Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + + // Since we merged identical schedules, the new schedule finishes at the same + // time as the original, just with double the amount + let sched1 = VestingInfo::try_new::( + sched0.locked() * 2, + sched0.per_block() * 2, + 10, // starts at the block the schedules are merged + ) + .unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched1]); + + assert_eq!(Balances::usable_balance(&2), 0); + }); + } - System::set_block_number(30); - assert_eq!(System::block_number(), 30); + #[test] + fn merge_ongoing_schedules() { + // Merging two schedules that have started will vest both before merging + let existential_deposit = 256; + ExtBuilder::default() + .existential_deposit(existential_deposit) + .build() + .execute_with(|| { + // Account 2 should already have a vesting schedule. + let sched0 = VestingInfo::try_new::( + existential_deposit * 20, + existential_deposit, // Vest over 20 blocks + 10, + ) + .unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + + let sched1 = VestingInfo::try_new::( + 300 * 10, + 300, // Vest over 10 blocks + sched0.starting_block() + 5, + ) + .unwrap(); + Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap().len(), 2); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); + + // Got to half way through the second schedule where both schedules are actively vesting + let cur_block = (sched1.ending_block::() - sched1.starting_block()) / 2 + + sched1.starting_block(); + assert_eq!(cur_block, 20); + + System::set_block_number(cur_block); + + // user2 has no usable balances prior to the merge because they have not vested yet + assert_eq!(Balances::usable_balance(&2), 0); + Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + + // Merging schedules vests all pre-existing schedules prior to merging, which is reflected + // in user2's updated usable balance + let sched0_vested_now = sched0.per_block() * (cur_block - sched0.starting_block()); + let sched1_vested_now = sched1.per_block() * (cur_block - sched1.starting_block()); + assert_eq!(Balances::usable_balance(&2), sched0_vested_now + sched1_vested_now); + + // The locked amount is the sum of schedules locked minus the amount that each schedule + // has vested up until the current block. + let sched2_locked = sched1 + .locked_at::(cur_block) + .saturating_add(sched0.locked_at::(cur_block)); + // End block of the new schedule is the greater of either merged schedule + let sched2_end = + sched1.ending_block::().max(sched0.ending_block::()); + let sched2_remaining_blocks = sched2_end - cur_block; + let sched2_per_block = sched2_locked / sched2_remaining_blocks; + let sched2 = + VestingInfo::try_new::(sched2_locked, sched2_per_block, cur_block) + .unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2]); + }); + } - // Account 4 has fully vested. - assert_eq!(Vesting::vesting_balance(&4), Some(0)); + #[test] + fn merging_shifts_other_schedules_index() { + // Schedules being merged are filtered out, schedules to the right of any merged + // schedule shift left and the new schedule is last + let existential_deposit = 256; + ExtBuilder::default() + .existential_deposit(existential_deposit) + .build() + .execute_with(|| { + let sched0 = VestingInfo::try_new::( + existential_deposit * 10, + existential_deposit, // Vesting over 10 blocks + 10, + ) + .unwrap(); + let sched1 = VestingInfo::try_new::( + existential_deposit * 11, + existential_deposit, // Vesting over 11 blocks + 11, + ) + .unwrap(); + let sched2 = VestingInfo::try_new::( + existential_deposit * 12, + existential_deposit, // Vesting over 12 blocks + 12, + ) + .unwrap(); + + // Account 3 start out with no schedules + assert_eq!(Vesting::vesting(&3), None); + // and some usable balance. + let usable_balance = Balances::usable_balance(&3); + assert_eq!(usable_balance, 30 * existential_deposit); + + let cur_block = 1; + assert_eq!(System::block_number(), cur_block); + + // Transfer the above 3 schedules to account 3 + Vesting::vested_transfer(Some(4).into(), 3, sched0).unwrap(); + Vesting::vested_transfer(Some(4).into(), 3, sched1).unwrap(); + Vesting::vested_transfer(Some(4).into(), 3, sched2).unwrap(); + + // With no schedules vested or merged they are in the order they are created + assert_eq!(Vesting::vesting(&3).unwrap(), vec![sched0, sched1, sched2]); + // and the usable balance has not changed + assert_eq!(usable_balance, Balances::usable_balance(&3)); + + Vesting::merge_schedules(Some(3).into(), 0, 2).unwrap(); + + // Create the merged schedule of sched0 & sched2. + let sched3_start = + sched1.starting_block().max(sched2.starting_block()).max(cur_block); + let sched3_locked = sched2 + .locked_at::(cur_block) + .saturating_add(sched0.locked_at::(cur_block)); + // End block of the new schedule is the greater of either schedule. + let sched3_end = + sched2.ending_block::().max(sched0.ending_block::()); + let sched3_remaining_blocks = sched3_end - sched3_start; + let sched3_per_block = sched3_locked / sched3_remaining_blocks; + let sched3 = + VestingInfo::try_new::(sched3_locked, sched3_per_block, sched3_start) + .unwrap(); + + let sched0_vested_now = sched0.per_block() * + (cur_block.max(sched0.starting_block()) - sched0.starting_block()); + let sched1_vested_now = sched1.per_block() * + (cur_block.max(sched1.starting_block()) - sched1.starting_block()); + let sched2_vested_now = sched2.per_block() * + (cur_block.max(sched2.starting_block()) - sched2.starting_block()); + // The not touched schedule moves left and the new merged schedule is appended. + assert_eq!(Vesting::vesting(&3).unwrap(), vec![sched1, sched3]); + // and the usable balance increases by how much the original schedules vested through + // the current block + assert_eq!( + Balances::usable_balance(&3), + usable_balance + sched1_vested_now + sched0_vested_now + sched2_vested_now + ); }); } #[test] - fn force_vested_transfer_correctly_fails() { + fn merge_ongoing_schedule_and_yet_to_be_started_schedule() { + // Merging an ongoing schedule and one that has not started yet works + let existential_deposit = 256; ExtBuilder::default() - .existential_deposit(256) + .existential_deposit(existential_deposit) .build() .execute_with(|| { - let user2_free_balance = Balances::free_balance(&2); - let user4_free_balance = Balances::free_balance(&4); - assert_eq!(user2_free_balance, 256 * 20); - assert_eq!(user4_free_balance, 256 * 40); // Account 2 should already have a vesting schedule. - let user2_vesting_schedule = VestingInfo { - locked: 256 * 20, - per_block: 256, // Vesting over 20 blocks - starting_block: 10, - }; - assert_eq!(Vesting::vesting(&2), Some(user2_vesting_schedule)); - - // The vesting schedule we will try to create, fails due to pre-existence of schedule. - let new_vesting_schedule = VestingInfo { - locked: 256 * 5, - per_block: 64, // Vesting over 20 blocks - starting_block: 10, - }; - assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 4, 2, new_vesting_schedule), - Error::::ExistingVestingSchedule, + let sched0 = VestingInfo::try_new::( + existential_deposit * 20, + existential_deposit, // Vesting over 20 blocks + 10, + ) + .unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap()[0], sched0); + assert_eq!(Vesting::vesting(&2).unwrap().len(), 1); + + // Fast forward to half way through the life of sched_1 + let mut cur_block = sched0.starting_block() + sched0.ending_block::() / 2; + System::set_block_number(cur_block); + assert_eq!(Balances::usable_balance(&2), 0); + + // Prior to vesting there is no usable balance + let mut usable_balance = 0; + assert_eq!(Balances::usable_balance(&2), usable_balance); + // We are also testing the behavior of when vest has been called on one of the + // schedules prior to merging. + Vesting::vest(Some(2).into()).unwrap(); + + // After vesting the usable balance increases by the amount the vested amount + let sched0_vested_now = + (cur_block - sched0.starting_block()) * sched0.per_block(); + usable_balance += sched0_vested_now; + assert_eq!(Balances::usable_balance(&2), usable_balance); + + // Go forward a block + cur_block += 1; + + System::set_block_number(cur_block); + + // And add a schedule that starts after this block, but before sched0 finishes. + let sched1 = VestingInfo::try_new::( + existential_deposit * 10, + 1, // Vesting over 256 * 10 (2560) blocks + cur_block + 1, + ) + .unwrap(); + Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); + + // Merge the schedules before sched1 starts. + Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + // The usable balance only changes by the amount sched0 vested since we last called `vest` + // and is not affected by sched1 which has not started yet. + usable_balance += sched0.per_block(); + assert_eq!(Balances::usable_balance(&2), usable_balance); + + // The resulting schedule will have the later starting block of the two. + let sched2_start = sched1.starting_block(); + let sched2_locked = sched0 + .locked_at::(cur_block) + .saturating_add(sched1.locked_at::(cur_block)); + // End block of the new schedule is the greater of either schedule. + let sched2_end = + sched0.ending_block::().max(sched1.ending_block::()); + let sched2_remaining_blocks = sched2_end - sched2_start; + let sched2_per_block = sched2_locked / sched2_remaining_blocks; + let sched2 = + VestingInfo::try_new::(sched2_locked, sched2_per_block, sched2_start) + .unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2]); + + }); + } + + #[test] + fn merge_schedules_with_zero_values_does_not_create_new_schedules() { + ExtBuilder::default().existential_deposit(256).build().execute_with(|| {}); + } + + #[test] + fn merge_vesting_info_handles_per_block_0() { + // Faulty schedules with an infinite duration (per_block == 0) can be merged to create + // a schedule that vest 1 per_block (helpful for faulty legacy schedules) + let existential_deposit = 256; + ExtBuilder::default() + .existential_deposit(existential_deposit) + .build() + .execute_with(|| { + // Merge a two schedules both with a duration greater than there + // locked amount + let sched0 = VestingInfo::unsafe_new(existential_deposit, 0, 1); + let sched1 = VestingInfo::unsafe_new(existential_deposit * 2, 0, 10); + + let cur_block = 5; + let merged_locked = sched0.locked_at::(cur_block) + + sched1.locked_at::(cur_block); + let merged_sched = VestingInfo::try_new::( + merged_locked, + 1, // Merged schedule will have a per_block of 1 + 10, + ) + .unwrap(); + assert_eq!( + merged_sched, + Vesting::merge_vesting_info(cur_block, sched0, sched1).unwrap() ); + }); + } - // Fails due to too low transfer amount. - let new_vesting_schedule_too_low = VestingInfo { - locked: 256 * 1, - per_block: 64, - starting_block: 10, - }; + #[test] + fn merge_finishing_and_ongoing_schedule() { + // If a schedule finishes by the block we treat the ongoing schedule as the merged one + let existential_deposit = 256; + ExtBuilder::default() + .existential_deposit(existential_deposit) + .build() + .execute_with(|| { + // Account 2 should already have a vesting schedule. + let sched0 = + VestingInfo::try_new::(existential_deposit * 20, existential_deposit, 10) + .unwrap(); + assert_eq!(sched0.ending_block::(), 30); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + + let sched1 = + VestingInfo::try_new::(existential_deposit * 40, existential_deposit, 10) + .unwrap(); + assert_eq!(sched1.ending_block::(), 50); + Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); + + // Transfer a 3rd schedule, so we can demonstrate how schedule indices change + // (We are not merging this schedule) + let sched2 = + VestingInfo::try_new::(existential_deposit * 30, existential_deposit, 10) + .unwrap(); + Vesting::vested_transfer(Some(3).into(), 2, sched2).unwrap(); + + // Current schedule order, prior to any merging + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1, sched2]); + + // Fast forward to sched0's end block + let current_block = sched0.ending_block::(); + System::set_block_number(current_block); + + assert_eq!(System::block_number(), 30); + // Prior to merge_schedules and with no vest/vest_other called the user has no usable + // balance. + assert_eq!(Balances::usable_balance(&2), 0); + Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + + // sched2 is now the first, since sched0 & sched1 get filtered out while "merging". + // sched1 gets treated like the new merged schedule by getting pushed onto back + // of the vesting schedules vec. Note: sched0 finished at the current block. + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2, sched1]); + + let sched0_vested_now = sched0.per_block() * + (sched0.ending_block::() - sched0.starting_block()); + let sched1_vested_now = + sched1.per_block() * (current_block - sched1.starting_block()); + let sched2_vested_now = + sched2.per_block() * (current_block - sched2.starting_block()); + // The users usable balance after merging includes all pre-existing + // schedules vested through the current block, including schedules not merged + assert_eq!( + Balances::usable_balance(&2), + sched0_vested_now + sched1_vested_now + sched2_vested_now + ); + }); + } + + #[test] + fn merge_finishing_schedules_does_not_create_a_new_one() { + // If both schedules finish by the current block we don't create new one + let existential_deposit = 256; + ExtBuilder::default() + .existential_deposit(existential_deposit) + .build() + .execute_with(|| { + // Account 2 should already have a vesting schedule. + let sched0 = + VestingInfo::try_new::(existential_deposit * 20, existential_deposit, 10) + .unwrap(); + assert_eq!(sched0.ending_block::(), 30); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + + let sched1 = + VestingInfo::try_new::(existential_deposit * 30, existential_deposit, 10) + .unwrap(); + assert_eq!(sched1.ending_block::(), 40); + Vesting::vested_transfer(Some(3).into(), 2, sched1).unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); + + let all_scheds_end = + sched0.ending_block::().max(sched1.ending_block::()); + assert_eq!(all_scheds_end, 40); + System::set_block_number(all_scheds_end); + // Prior to merge_schedules and with no vest/vest_other called the user has no usable + // balance. + assert_eq!(Balances::usable_balance(&2), 0); + Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + // The user no longer has any more vesting schedules because they both ended at the + // block they where merged + assert_eq!(Vesting::vesting(&2), None); + + let sched0_vested_now = sched0.per_block() * + (sched0.ending_block::() - sched0.starting_block()); + let sched1_vested_now = sched1.per_block() * + (sched1.ending_block::() - sched1.starting_block()); + assert_eq!(Balances::usable_balance(&2), sched0_vested_now + sched1_vested_now); + }); + } + + #[test] + fn merge_schedules_throws_proper_errors() { + let existential_deposit = 256; + ExtBuilder::default() + .existential_deposit(existential_deposit) + .build() + .execute_with(|| { + // Account 2 should already have a vesting schedule. + let sched0 = + VestingInfo::try_new::(existential_deposit * 20, existential_deposit, 10) + .unwrap(); + + // Not enough vesting schedules + assert_eq!(Vesting::vesting(&2).unwrap().len(), 1); assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, new_vesting_schedule_too_low), - Error::::AmountLow, + Vesting::merge_schedules(Some(2).into(), 0, 1), + Error::::ScheduleIndexOutOfBounds ); - // Verify no currency transfer happened. - assert_eq!(user2_free_balance, 256 * 20); - assert_eq!(user4_free_balance, 256 * 40); + // Enough schedules to merge but an index is non-existent + Vesting::vested_transfer(Some(3).into(), 2, sched0).unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched0]); + assert_noop!( + Vesting::merge_schedules(Some(2).into(), 0, 2), + Error::::ScheduleIndexOutOfBounds + ); + + // Index >= max allowed schedules + let max_schedules = ::MaxVestingSchedules::get(); + Vesting::vested_transfer(Some(4).into(), 2, sched0).unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap().len(), max_schedules as usize); + assert_noop!( + Vesting::merge_schedules(Some(2).into(), 0, max_schedules), + Error::::ScheduleIndexOutOfBounds + ); + + // Essentially a noop with no errors if indexes are the same. + let cur_scheds = Vesting::vesting(&2); + let usable_balance = Balances::usable_balance(&2); + assert_ok!(Vesting::merge_schedules(Some(2).into(), 0, 0)); + // The relevant storage is invariant. + assert_eq!(Vesting::vesting(&2), cur_scheds); + assert_eq!(usable_balance, Balances::usable_balance(&2)); + }); + } + + #[test] + fn generates_multiple_schedules_from_genesis_config() { + let existential_deposit = 256; + let vesting_config = vec![ + // 5 * existential_deposit locked + (1, 0, 10, 5 * existential_deposit), + // 1 * existential_deposit locked + (2, 10, 20, 19 * existential_deposit), + // 2 * existential_deposit locked + (2, 10, 20, 18 * existential_deposit), + // 1 * existential_deposit locked + (12, 10, 20, 9 * existential_deposit), + // 2 * existential_deposit locked + (12, 10, 20, 8 * existential_deposit), + // 3 * existential_deposit locked + (12, 10, 20, 7 * existential_deposit), + ]; + ExtBuilder::default() + .existential_deposit(existential_deposit) + .vesting_genesis_config(vesting_config) + .build() + .execute_with(|| { + let user1_sched1 = + VestingInfo::try_new::(5 * existential_deposit, 128, 0u64).unwrap(); + assert_eq!(Vesting::vesting(&1).unwrap(), vec![user1_sched1]); + + let user2_sched1 = + VestingInfo::try_new::(1 * existential_deposit, 12, 10u64).unwrap(); + let user2_sched2 = + VestingInfo::try_new::(2 * existential_deposit, 25, 10u64).unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_sched1, user2_sched2]); + + let user12_sched1 = + VestingInfo::try_new::(1 * existential_deposit, 12, 10u64).unwrap(); + let user12_sched2 = + VestingInfo::try_new::(2 * existential_deposit, 25, 10u64).unwrap(); + let user12_sched3 = + VestingInfo::try_new::(3 * existential_deposit, 38, 10u64).unwrap(); + assert_eq!( + Vesting::vesting(&12).unwrap(), + vec![user12_sched1, user12_sched2, user12_sched3] + ); }); } + + #[test] + fn vesting_info_validation_works() { + let min_transfer = ::MinVestedTransfer::get(); + // `locked` cannot be less than min_transfer (non 0 case) + match VestingInfo::try_new::(min_transfer - 1, 1u64, 10u64) { + Err(Error::::AmountLow) => (), + _ => panic!(), + } + // `locked` cannot be 0 + match VestingInfo::try_new::(0, 1u64, 10u64) { + Err(Error::::InvalidScheduleParams) => (), + _ => panic!(), + } + // `per_block` cannot be 0 + match VestingInfo::try_new::(min_transfer + 1, 0u64, 10u64) { + Err(Error::::InvalidScheduleParams) => (), + _ => panic!(), + } + // Valid inputs + let ok_sched = VestingInfo::try_new::(min_transfer, 1u64, 10u64); + assert!(ok_sched.is_ok()); + assert_eq!(ok_sched.unwrap(), VestingInfo::unsafe_new(min_transfer, 1u64, 10u64)); + // `per_block` is never bigger than `locked` + assert_eq!( + VestingInfo::try_new::(256u64, 256 * 2u64, 10u64).unwrap(), + VestingInfo::try_new::(256u64, 256u64, 10u64).unwrap() + ); + } + + #[test] + fn vesting_info_ending_block_works() { + // Treats `per_block` 0 as a `per_block` of 1 + let per_block_0 = VestingInfo::unsafe_new(256u32, 0u32, 10u32); + let per_block_1 = VestingInfo::try_new::(256u32, 1u32, 10u32).unwrap(); + assert_eq!( + per_block_0.ending_block::(), + per_block_0.locked() + per_block_0.starting_block() + ); + assert_eq!(per_block_0.ending_block::(), per_block_1.ending_block::()); + + // `per_block >= locked` always results in a schedule ending the block after it starts + let per_block_gt_locked = VestingInfo::unsafe_new(256u32, 256 * 2u32, 10u32); + let per_block_eq_locked = VestingInfo::try_new::(256u32, 256u32, 10u32).unwrap(); + assert_eq!( + per_block_gt_locked.ending_block::(), + 1 + per_block_gt_locked.starting_block() + ); + assert_eq!( + per_block_gt_locked.ending_block::(), + per_block_eq_locked.ending_block::() + ); + + // Correctly calcs end if `locked % per_block != 0` + let imperfect_per_block = VestingInfo::try_new::(256u32, 250u32, 10u32).unwrap(); + assert_eq!( + imperfect_per_block.ending_block::(), + imperfect_per_block.starting_block() + 2u32, + ); + assert_eq!( + imperfect_per_block + .locked_at::(imperfect_per_block.ending_block::()), + 0 + ); + } } From 73eb1390c13945084955c3a2ee597ac68314ba1e Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 24 Jun 2021 20:14:20 -0700 Subject: [PATCH 002/144] Update node runtime --- bin/node/runtime/src/lib.rs | 2 ++ frame/support/src/traits/tokens/currency/lockable.rs | 5 ++++- frame/vesting/README.md | 5 ++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 2e11ab54e4316..6eb636a7f21e8 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1009,6 +1009,7 @@ impl pallet_society::Config for Runtime { parameter_types! { pub const MinVestedTransfer: Balance = 100 * DOLLARS; + pub const MaxVestingSchedules: u32 = 3; } impl pallet_vesting::Config for Runtime { @@ -1017,6 +1018,7 @@ impl pallet_vesting::Config for Runtime { type BlockNumberToBalance = ConvertInto; type MinVestedTransfer = MinVestedTransfer; type WeightInfo = pallet_vesting::weights::SubstrateWeight; + type MaxVestingSchedules = MaxVestingSchedules; } impl pallet_mmr::Config for Runtime { diff --git a/frame/support/src/traits/tokens/currency/lockable.rs b/frame/support/src/traits/tokens/currency/lockable.rs index ed3d1cf46362b..9b331752357fd 100644 --- a/frame/support/src/traits/tokens/currency/lockable.rs +++ b/frame/support/src/traits/tokens/currency/lockable.rs @@ -99,6 +99,9 @@ pub trait VestingSchedule { /// Remove a vesting schedule for a given account. /// + /// Parameter `schedule_index` is only applicable for implementations that + /// support multiple schedules. + /// /// NOTE: This doesn't alter the free balance of the account. - fn remove_vesting_schedule(who: &AccountId); + fn remove_vesting_schedule(who: &AccountId, schedule_index: Option) -> DispatchResult; } diff --git a/frame/vesting/README.md b/frame/vesting/README.md index 811b0dc44152d..3a05db523c3e7 100644 --- a/frame/vesting/README.md +++ b/frame/vesting/README.md @@ -15,6 +15,8 @@ equivalent to the amount remaining to be vested. This is done through a dispatch either `vest` (in typical case where the sender is calling on their own behalf) or `vest_other` in case the sender is calling on another account's behalf. +A user can have multiple vesting schedules, which are capped by an upper bound. With an upper bound +>= 2, users can merge 2 schedules. ## Interface This module implements the `VestingSchedule` trait. @@ -24,8 +26,9 @@ This module implements the `VestingSchedule` trait. - `vest` - Update the lock, reducing it in line with the amount "vested" so far. - `vest_other` - Update the lock of another account, reducing it in line with the amount "vested" so far. +- `merge_schedules` - Merge two vesting schedules together, creating a new vesting schedule that + over the "worst case" duration. [`Call`]: ./enum.Call.html [`Config`]: ./trait.Config.html - License: Apache-2.0 From 100bca1094f1e4e92f6519bc42b69bb8547b6384 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 24 Jun 2021 20:43:17 -0700 Subject: [PATCH 003/144] Remove some TODO design questions and put them as commennts --- frame/vesting/src/benchmarking.rs | 2 ++ frame/vesting/src/lib.rs | 5 ----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index 8d16a53fba2c1..988379f172a5e 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -222,6 +222,8 @@ benchmarks! { "Lock not created", ); } + + } impl_benchmark_test_suite!( diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 2ebeb0ce18ea0..46ac30533d433 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -104,8 +104,6 @@ mod vesting_info { ensure!(!locked.is_zero() && !per_block.is_zero(), Error::::InvalidScheduleParams); let min_transfer: u32 = T::MinVestedTransfer::get().try_into().unwrap_or(u32::MAX); let min_transfer = Balance::from(min_transfer); - // TODO - Do we want to enforce this here? This would keep from merging where sum of - // schedules is below MinVestedTransfer ensure!(locked >= min_transfer, Error::::AmountLow); Ok(()) } @@ -609,9 +607,6 @@ impl VestingSchedule for Pallet where type Moment = T::BlockNumber; type Currency = T::Currency; - // TODO should we expose merge vesting schedules here? Its a bit dangerous because schedules - // need to be vested before being merged - /// Get the amount that is currently being vested and cannot be transferred out of this account. fn vesting_balance(who: &T::AccountId) -> Option> { if let Some(v) = Self::vesting(who) { From 29898c45a4397e1b6e5b9fb1ace8e589dcc60259 Mon Sep 17 00:00:00 2001 From: Zeke Mostov <32168567+emostov@users.noreply.github.com> Date: Fri, 25 Jun 2021 12:03:31 -0700 Subject: [PATCH 004/144] Update frame/vesting/src/benchmarking.rs --- frame/vesting/src/benchmarking.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index 988379f172a5e..8d16a53fba2c1 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -222,8 +222,6 @@ benchmarks! { "Lock not created", ); } - - } impl_benchmark_test_suite!( From 5a41c95ef3c6ad921f8bb0653ffd9b093817d724 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Sun, 27 Jun 2021 12:01:06 -0700 Subject: [PATCH 005/144] Syntax and comment clean up --- frame/vesting/src/lib.rs | 97 +++++++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 40 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 46ac30533d433..b0a1101b13fcf 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -408,12 +408,17 @@ pub mod pallet { } /// Merge two vesting schedules together, creating a new vesting schedule that vests over - /// the maximum of the original two schedules duration. - /// - /// NOTE: this will vest all schedules through the current block prior to merging. However, - /// the schedule indexes are based off of the ordering prior to schedules being vested/filtered. + /// that finishes at the highest start and end blocks. If both schedules have already started + /// the current block will be used as the schedule start. /// /// NOTE: If `schedule1_index == schedule2_index` this is a no-op. + /// NOTE: This will vest all schedules through the current block prior to merging. + /// NOTE: If both schedules have ended by the current block, no new schedule will be created. + /// + /// Merged schedule attributes: + /// starting_block: `MAX(schedule1.starting_block, scheduled2.starting_block, current_block)` + /// ending_block: `MAX(schedule1.ending_block, schedule2.ending_block)` + /// locked: `schedule1.locked_at(current_block) + schedule2.locked_at(current_block)` /// /// The dispatch origin for this call must be _Signed_. /// @@ -440,7 +445,10 @@ pub mod pallet { let schedule2_index = schedule2_index as usize; let vesting = Self::vesting(&who).ok_or(Error::::NotVesting)?; let len = vesting.len(); - ensure!(schedule1_index < len && schedule2_index < len, Error::::ScheduleIndexOutOfBounds); + ensure!( + schedule1_index < len && schedule2_index < len, + Error::::ScheduleIndexOutOfBounds + ); // The schedule index is based off of the schedule ordering prior to filtering out any // schedules that may be ending at this block. @@ -456,9 +464,11 @@ pub mod pallet { let mut vesting = maybe_vesting.unwrap_or_default(); // It shouldn't be possible for this to fail because we removed 2 schedules above. ensure!(vesting.try_push(s).is_ok(), Error::::AtMaxVestingSchedules); - Self::deposit_event( - Event::::VestingMergeSuccess(s.locked(), s.per_block(), s.starting_block()) - ); + Self::deposit_event(Event::::VestingMergeSuccess( + s.locked(), + s.per_block(), + s.starting_block(), + )); Vesting::::insert(&who, vesting); } else if maybe_vesting.is_some() { Vesting::::insert(&who, maybe_vesting.unwrap()); @@ -473,7 +483,7 @@ pub mod pallet { impl Pallet { // Create a new `VestingInfo`, based off of two other `VestingInfo`s. - // Note: We assume both schedules have been vested up through the current block. + // NOTE: We assume both schedules have been vested up through the current block. fn merge_vesting_info( now: T::BlockNumber, schedule1: VestingInfo, T::BlockNumber>, @@ -482,40 +492,45 @@ impl Pallet { let schedule1_ending_block = schedule1.ending_block::(); let schedule2_ending_block = schedule2.ending_block::(); let now_as_balance = T::BlockNumberToBalance::convert(now); + if schedule1_ending_block <= now_as_balance && schedule2_ending_block <= now_as_balance { - // If both schedule has ended, we don't merge + // If both schedules hav ended, we don't merge and exit early. return None; } else if schedule1_ending_block <= now_as_balance { - // If one schedule has ended, we treat the one that has not ended as the new "merged schedule" - return Some(schedule2) + // If one schedule has ended, we treat the one that has not ended as the new + // merged schedule. + return Some(schedule2); } else if schedule2_ending_block <= now_as_balance { - return Some(schedule1) + return Some(schedule1); } - let locked = schedule1.locked_at::(now) + + let locked = schedule1 + .locked_at::(now) .saturating_add(schedule2.locked_at::(now)); // This shouldn't happen because we know at least one ending block is greater than now. - if locked.is_zero() { return None; } + if locked.is_zero() { + return None; + } let ending_block = schedule1_ending_block.max(schedule2_ending_block); - let starting_block = now - .max(schedule1.starting_block()) - .max(schedule2.starting_block()); + let starting_block = now.max(schedule1.starting_block()).max(schedule2.starting_block()); let duration = ending_block.saturating_sub(T::BlockNumberToBalance::convert(starting_block)); let per_block = if duration.is_zero() { // The logic of `ending_block` guarantees that each schedule ends at least a block - // after it starts and since we take the max starting and ending_block we should never get here + // after it starts and since we take the max starting and ending_block we should never + // get here locked } else if duration > locked { // This would mean we have a per_block of less than 1, which should not be not possible - // because when we merge the new schedule is at most the same duration as the longest, but - // never greater + // because when we create the new schedule is at most the same duration as the longest, + // but never greater. 1u32.into() } else { locked.checked_div(&duration)? }; - // At this point inputs have been validated, so this should always be `Some` + // At this point inputs have been validated, so this should always be `Some`. VestingInfo::try_new::(locked, per_block, starting_block).ok() } @@ -554,7 +569,7 @@ impl Pallet { schedule.per_block(), schedule.starting_block(), ) - .expect("schedule inputs and vec bounds validated. q.e."); + .expect("schedule inputs and vec bounds have been validated. q.e.d."); Ok(()) } @@ -571,10 +586,10 @@ impl Pallet { let now = >::block_number(); let mut total_locked_now: BalanceOf = Zero::zero(); - let still_vesting = vesting + let still_vesting = vesting .into_iter() .enumerate() - .filter_map(| (i, schedule) | { + .filter_map(|(i, schedule)| { let locked_now = schedule.locked_at::(now); total_locked_now = total_locked_now.saturating_add(locked_now); if locked_now.is_zero() || filter.contains(&i) { @@ -601,11 +616,12 @@ impl Pallet { } } -impl VestingSchedule for Pallet where - BalanceOf: MaybeSerializeDeserialize + Debug +impl VestingSchedule for Pallet +where + BalanceOf: MaybeSerializeDeserialize + Debug, { - type Moment = T::BlockNumber; type Currency = T::Currency; + type Moment = T::BlockNumber; /// Get the amount that is currently being vested and cannot be transferred out of this account. fn vesting_balance(who: &T::AccountId) -> Option> { @@ -634,18 +650,16 @@ impl VestingSchedule for Pallet where who: &T::AccountId, locked: BalanceOf, per_block: BalanceOf, - starting_block: T::BlockNumber + starting_block: T::BlockNumber, ) -> DispatchResult { - if locked.is_zero() { return Ok(()) } - let vesting_schedule = VestingInfo::try_new::( - locked, per_block, starting_block - )?; - let mut vesting = if let Some(v) = Self::vesting(who) { - v - } else { - BoundedVec::default() - }; + if locked.is_zero() { + return Ok(()); + } + + let vesting_schedule = VestingInfo::try_new::(locked, per_block, starting_block)?; + let mut vesting = if let Some(v) = Self::vesting(who) { v } else { BoundedVec::default() }; ensure!(vesting.try_push(vesting_schedule).is_ok(), Error::::AtMaxVestingSchedules); + if let Some(v) = Self::update_lock_and_schedules(who.clone(), vesting, vec![]) { Vesting::::insert(&who, v); } else { @@ -657,12 +671,15 @@ impl VestingSchedule for Pallet where /// Remove a vesting schedule for a given account. fn remove_vesting_schedule(who: &T::AccountId, schedule_index: Option) -> DispatchResult { let filter = if let Some(schedule_index) = schedule_index { - ensure!(schedule_index < T::MaxVestingSchedules::get(), Error::::ScheduleIndexOutOfBounds); + ensure!( + schedule_index < T::MaxVestingSchedules::get(), + Error::::ScheduleIndexOutOfBounds + ); vec![schedule_index as usize] } else { vec![] }; - let vesting= Self::vesting(who).ok_or(Error::::NotVesting)?; + let vesting = Self::vesting(who).ok_or(Error::::NotVesting)?; if let Some(v) = Self::update_lock_and_schedules(who.clone(), vesting, filter) { Vesting::::insert(&who, v); } else { From 65eb0da377eb1f6abc0d5f1fda4170fe4c81eca3 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Sun, 27 Jun 2021 12:36:12 -0700 Subject: [PATCH 006/144] Create filter enum for removing schedules --- frame/vesting/src/lib.rs | 46 ++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index b0a1101b13fcf..63804550a495b 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -176,6 +176,27 @@ mod vesting_info { } } +/// The indexes of vesting schedules to remove from an accounts vesting schedule collection. +enum Filter { + /// Do not filter out any schedules. + Zero, + /// Filter out 1 schedule. + One(usize), + /// Filter out 2 schedules. + Two((usize, usize)), +} + +impl Filter { + /// Wether or not the filter says the schedule index should be removed. + fn should_remove(&self, index: &usize) -> bool { + match self { + Self::Zero => false, + Self::One(index1) => index1 == index, + Self::Two((index1, index2)) => index1 == index || index2 == index, + } + } +} + #[frame_support::pallet] pub mod pallet { use super::*; @@ -313,8 +334,8 @@ pub mod pallet { )] pub fn vest(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - let vesting = Self::vesting(&who).ok_or(Error::::NotVesting)?; - let maybe_vesting = Self::update_lock_and_schedules(who.clone(), vesting, vec![]); + let vesting = Self::vesting(&who).ok_or(Error::::NotVesting)?; // TODO make do_vest + let maybe_vesting = Self::update_lock_and_schedules(who.clone(), vesting, Filter::Zero); if let Some(vesting) = maybe_vesting { Vesting::::insert(&who, vesting); } else { @@ -341,11 +362,14 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::vest_other_locked(MaxLocksOf::::get()) .max(T::WeightInfo::vest_other_unlocked(MaxLocksOf::::get())) )] - pub fn vest_other(origin: OriginFor, target: ::Source) -> DispatchResult { + pub fn vest_other( + origin: OriginFor, + target: ::Source, + ) -> DispatchResult { ensure_signed(origin)?; let who = T::Lookup::lookup(target)?; let vesting = Self::vesting(&who).ok_or(Error::::NotVesting)?; - let maybe_vesting = Self::update_lock_and_schedules(who.clone(), vesting, vec![]); + let maybe_vesting = Self::update_lock_and_schedules(who.clone(), vesting, Filter::Zero); if let Some(vesting) = maybe_vesting { Vesting::::insert(&who, vesting); } else { @@ -454,7 +478,7 @@ pub mod pallet { // schedules that may be ending at this block. let schedule1 = vesting[schedule1_index]; let schedule2 = vesting[schedule2_index]; - let filter = vec![schedule1_index, schedule2_index]; + let filter = Filter::Two((schedule1_index, schedule2_index)); // The length of vesting decreases by 2 here since we filter out 2 schedules. So we know // below we have the space to insert the merged schedule. let maybe_vesting = Self::update_lock_and_schedules(who.clone(), vesting, filter); @@ -581,7 +605,7 @@ impl Pallet { fn update_lock_and_schedules( who: T::AccountId, vesting: BoundedVec, T::BlockNumber>, T::MaxVestingSchedules>, - filter: Vec, + filter: Filter, ) -> Option, T::BlockNumber>, T::MaxVestingSchedules>> { let now = >::block_number(); @@ -589,10 +613,10 @@ impl Pallet { let still_vesting = vesting .into_iter() .enumerate() - .filter_map(|(i, schedule)| { + .filter_map(|(index, schedule)| { let locked_now = schedule.locked_at::(now); total_locked_now = total_locked_now.saturating_add(locked_now); - if locked_now.is_zero() || filter.contains(&i) { + if locked_now.is_zero() || filter.should_remove(&index) { None } else { Some(schedule) @@ -660,7 +684,7 @@ where let mut vesting = if let Some(v) = Self::vesting(who) { v } else { BoundedVec::default() }; ensure!(vesting.try_push(vesting_schedule).is_ok(), Error::::AtMaxVestingSchedules); - if let Some(v) = Self::update_lock_and_schedules(who.clone(), vesting, vec![]) { + if let Some(v) = Self::update_lock_and_schedules(who.clone(), vesting, Filter::Zero) { Vesting::::insert(&who, v); } else { Vesting::::remove(&who); @@ -675,9 +699,9 @@ where schedule_index < T::MaxVestingSchedules::get(), Error::::ScheduleIndexOutOfBounds ); - vec![schedule_index as usize] + Filter::One(schedule_index as usize) } else { - vec![] + Filter::Zero }; let vesting = Self::vesting(who).ok_or(Error::::NotVesting)?; if let Some(v) = Self::update_lock_and_schedules(who.clone(), vesting, filter) { From 4c95ba235e7ec6b378b01b18306d3f48d799fd59 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Sun, 27 Jun 2021 13:01:44 -0700 Subject: [PATCH 007/144] Dry vesting calls with do_vest --- frame/vesting/src/lib.rs | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 63804550a495b..3ba12bfa69007 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -334,14 +334,7 @@ pub mod pallet { )] pub fn vest(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - let vesting = Self::vesting(&who).ok_or(Error::::NotVesting)?; // TODO make do_vest - let maybe_vesting = Self::update_lock_and_schedules(who.clone(), vesting, Filter::Zero); - if let Some(vesting) = maybe_vesting { - Vesting::::insert(&who, vesting); - } else { - Vesting::::remove(&who); - } - Ok(()) + Self::do_vest(who) } /// Unlock any vested funds of a `target` account. @@ -368,14 +361,7 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; let who = T::Lookup::lookup(target)?; - let vesting = Self::vesting(&who).ok_or(Error::::NotVesting)?; - let maybe_vesting = Self::update_lock_and_schedules(who.clone(), vesting, Filter::Zero); - if let Some(vesting) = maybe_vesting { - Vesting::::insert(&who, vesting); - } else { - Vesting::::remove(&who); - } - Ok(()) + Self::do_vest(who) } /// Create a vested transfer. @@ -638,6 +624,19 @@ impl Pallet { Some(still_vesting) } } + + /// Unlock any vested funds of `who`. + fn do_vest(who: T::AccountId) -> DispatchResult { + let vesting = Self::vesting(&who).ok_or(Error::::NotVesting)?; + let maybe_vesting = Self::update_lock_and_schedules(who.clone(), vesting, Filter::Zero); + if let Some(vesting) = maybe_vesting { + Vesting::::insert(&who, vesting); + } else { + Vesting::::remove(&who); + } + + Ok(()) + } } impl VestingSchedule for Pallet @@ -662,7 +661,7 @@ where /// Adds a vesting schedule to a given account. /// - /// If there already `MaxVestingSchedules`, an `Err` is returned and nothing + /// If there already `MaxVestingSchedules`, an Error is returned and nothing /// is updated. /// /// On success, a linearly reducing amount of funds will be locked. In order to realise any From fe24ab1cde0e235d623e3c0d394cd3bd57825ec1 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Sun, 27 Jun 2021 18:23:14 -0700 Subject: [PATCH 008/144] Improve old benchmarks to account for max schedules --- frame/vesting/src/benchmarking.rs | 196 ++++++++++++++++++++++-------- frame/vesting/src/lib.rs | 6 + 2 files changed, 151 insertions(+), 51 deletions(-) diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index 8d16a53fba2c1..1f332d5806253 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -24,52 +24,69 @@ use super::*; use frame_system::{RawOrigin, Pallet as System}; use frame_benchmarking::{benchmarks, account, whitelisted_caller, impl_benchmark_test_suite}; use sp_runtime::traits::Bounded; +use frame_support::assert_ok; use crate::Pallet as Vesting; const SEED: u32 = 0; +const ED: u32 = 256; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; fn add_locks(who: &T::AccountId, n: u8) { for id in 0..n { let lock_id = [id; 8]; - let locked = 100u32; + // let locked = 100u32; + let locked = ED; + let reasons = WithdrawReasons::TRANSFER | WithdrawReasons::RESERVE; T::Currency::set_lock(lock_id, who, locked.into(), reasons); } } -fn add_vesting_schedule(who: &T::AccountId) -> Result<(), &'static str> { - let locked = 100u32; - let per_block = 10u32; +fn add_vesting_schedules( + target: ::Source, + n: u32, +) -> Result, &'static str> { + let locked = ED * 20u32; + let per_block = ED; // Vest over 20 blocks let starting_block = 1u32; - System::::set_block_number(0u32.into()); + let source: T::AccountId = account("source", 0, SEED); + let source_lookup: ::Source = T::Lookup::unlookup(source.clone()); + T::Currency::make_free_balance_be(&source, BalanceOf::::max_value()); + + System::::set_block_number(T::BlockNumber::zero()); - // Add schedule to avoid `NotVesting` error. - Vesting::::add_vesting_schedule( - &who, - locked.into(), - per_block.into(), - starting_block.into(), - )?; - Ok(()) + let mut total_locked = 0; + for _ in 0 .. n { + total_locked += locked; + + let schedule = + VestingInfo::try_new::(locked.into(), per_block.into(), starting_block.into()) + .map_err(|_| "Failed to create vesting schedule")?; + assert_ok!(Vesting::::do_vested_transfer(source_lookup.clone(), target.clone(), schedule)); + } + + Ok(total_locked.into()) } benchmarks! { vest_locked { let l in 0 .. MaxLocksOf::::get(); - let caller = whitelisted_caller(); - T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + let caller: T::AccountId = whitelisted_caller(); + let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); + T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value() / 2u32.into()); add_locks::(&caller, l as u8); - add_vesting_schedule::(&caller)?; + let expected_balance = add_vesting_schedules::(caller_lookup, T::MaxVestingSchedules::get())?; + // At block zero, everything is vested. System::::set_block_number(T::BlockNumber::zero()); + assert_eq!( Vesting::::vesting_balance(&caller), - Some(100u32.into()), + Some(expected_balance.into()), "Vesting schedule not added", ); }: vest(RawOrigin::Signed(caller.clone())) @@ -77,7 +94,7 @@ benchmarks! { // Nothing happened since everything is still vested. assert_eq!( Vesting::::vesting_balance(&caller), - Some(100u32.into()), + Some(expected_balance.into()), "Vesting schedule was removed", ); } @@ -85,12 +102,13 @@ benchmarks! { vest_unlocked { let l in 0 .. MaxLocksOf::::get(); - let caller = whitelisted_caller(); - T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + let caller: T::AccountId = whitelisted_caller(); + let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); + T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value() / 2u32.into()); add_locks::(&caller, l as u8); - add_vesting_schedule::(&caller)?; - // At block 20, everything is unvested. - System::::set_block_number(20u32.into()); + add_vesting_schedules::(caller_lookup, T::MaxVestingSchedules::get())?; + // At block 21, everything is unvested. + System::::set_block_number(21u32.into()); assert_eq!( Vesting::::vesting_balance(&caller), Some(BalanceOf::::zero()), @@ -111,14 +129,14 @@ benchmarks! { let other: T::AccountId = account("other", 0, SEED); let other_lookup: ::Source = T::Lookup::unlookup(other.clone()); - T::Currency::make_free_balance_be(&other, BalanceOf::::max_value()); + T::Currency::make_free_balance_be(&other, BalanceOf::::max_value() / 2u32.into()); add_locks::(&other, l as u8); - add_vesting_schedule::(&other)?; + let expected_balance = add_vesting_schedules::(other_lookup.clone(), T::MaxVestingSchedules::get())?; // At block zero, everything is vested. System::::set_block_number(T::BlockNumber::zero()); assert_eq!( Vesting::::vesting_balance(&other), - Some(100u32.into()), + Some(expected_balance), "Vesting schedule not added", ); @@ -128,7 +146,7 @@ benchmarks! { // Nothing happened since everything is still vested. assert_eq!( Vesting::::vesting_balance(&other), - Some(100u32.into()), + Some(expected_balance.into()), "Vesting schedule was removed", ); } @@ -138,11 +156,11 @@ benchmarks! { let other: T::AccountId = account("other", 0, SEED); let other_lookup: ::Source = T::Lookup::unlookup(other.clone()); - T::Currency::make_free_balance_be(&other, BalanceOf::::max_value()); + T::Currency::make_free_balance_be(&other, BalanceOf::::max_value() / 2u32.into()); add_locks::(&other, l as u8); - add_vesting_schedule::(&other)?; - // At block 20, everything is unvested. - System::::set_block_number(20u32.into()); + add_vesting_schedules::(other_lookup.clone(), T::MaxVestingSchedules::get())?; + // At block 21, everything is unvested. + System::::set_block_number(21u32.into()); assert_eq!( Vesting::::vesting_balance(&other), Some(BalanceOf::::zero()), @@ -160,7 +178,7 @@ benchmarks! { ); } - vested_transfer { + last_vested_transfer { let l in 0 .. MaxLocksOf::::get(); let caller: T::AccountId = whitelisted_caller(); @@ -169,29 +187,67 @@ benchmarks! { let target_lookup: ::Source = T::Lookup::unlookup(target.clone()); // Give target existing locks add_locks::(&target, l as u8); + // Add one less than max vesting schedules + let one_less_than_max_vesting = T::MaxVestingSchedules::get() - 1; + let mut expected_balance = add_vesting_schedules::(target_lookup.clone(), one_less_than_max_vesting)?; + + let duration = 10u32; + let transfer_amount = (ED * duration).into(); + expected_balance += transfer_amount; + + let vesting_schedule = VestingInfo::try_new::( + transfer_amount, + duration.into(), + 1u32.into(), + ).map_err(|_| "Failed to create vesting schedule")?; + }: vested_transfer(RawOrigin::Signed(caller), target_lookup, vesting_schedule) + verify { + assert_eq!( + expected_balance, + T::Currency::free_balance(&target), + "Transfer didn't happen", + ); + assert_eq!( + Vesting::::vesting_balance(&target), + Some(expected_balance), + "Lock not correctly updated", + ); + } - let transfer_amount = T::MinVestedTransfer::get(); + first_vested_transfer { + let l in 0 .. MaxLocksOf::::get(); - let vesting_schedule = VestingInfo { - locked: transfer_amount, - per_block: 10u32.into(), - starting_block: 1u32.into(), - }; - }: _(RawOrigin::Signed(caller), target_lookup, vesting_schedule) + let caller: T::AccountId = whitelisted_caller(); + T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + let target: T::AccountId = account("target", 0, SEED); + let target_lookup: ::Source = T::Lookup::unlookup(target.clone()); + // Give target existing locks + add_locks::(&target, l as u8); + + let duration = 10u32; + let transfer_amount = (ED * duration).into(); + + let vesting_schedule = VestingInfo::try_new::( + transfer_amount, + duration.into(), + 1u32.into(), + ).map_err(|_| "Failed to create vesting schedule")?; + + }: vested_transfer(RawOrigin::Signed(caller), target_lookup, vesting_schedule) verify { assert_eq!( - T::MinVestedTransfer::get(), + transfer_amount, T::Currency::free_balance(&target), "Transfer didn't happen", ); assert_eq!( Vesting::::vesting_balance(&target), - Some(T::MinVestedTransfer::get()), + Some(transfer_amount), "Lock not created", ); } - force_vested_transfer { + first_force_vested_transfer { let l in 0 .. MaxLocksOf::::get(); let source: T::AccountId = account("source", 0, SEED); @@ -202,26 +258,64 @@ benchmarks! { // Give target existing locks add_locks::(&target, l as u8); - let transfer_amount = T::MinVestedTransfer::get(); + let duration = 10u32; + let transfer_amount = (ED * duration).into(); - let vesting_schedule = VestingInfo { - locked: transfer_amount, - per_block: 10u32.into(), - starting_block: 1u32.into(), - }; - }: _(RawOrigin::Root, source_lookup, target_lookup, vesting_schedule) + let vesting_schedule = VestingInfo::try_new::( + transfer_amount, + duration.into(), + 1u32.into(), + ).map_err(|_| "Failed to create vesting schedule")?; + }: force_vested_transfer(RawOrigin::Root, source_lookup, target_lookup, vesting_schedule) verify { assert_eq!( - T::MinVestedTransfer::get(), + transfer_amount, T::Currency::free_balance(&target), "Transfer didn't happen", ); assert_eq!( Vesting::::vesting_balance(&target), - Some(T::MinVestedTransfer::get()), + Some(transfer_amount), "Lock not created", ); } + + last_force_vested_transfer { + let l in 0 .. MaxLocksOf::::get(); + + let source: T::AccountId = account("source", 0, SEED); + let source_lookup: ::Source = T::Lookup::unlookup(source.clone()); + T::Currency::make_free_balance_be(&source, BalanceOf::::max_value()); + let target: T::AccountId = account("target", 0, SEED); + let target_lookup: ::Source = T::Lookup::unlookup(target.clone()); + // Give target existing locks + add_locks::(&target, l as u8); + // Add one less than max vesting schedules + let one_less_than_max_vesting = T::MaxVestingSchedules::get() - 1; + let mut expected_balance = add_vesting_schedules::(target_lookup.clone(), one_less_than_max_vesting)?; + + let duration = 10u32; + let transfer_amount = (ED * duration).into(); + expected_balance += transfer_amount; + + let vesting_schedule = VestingInfo::try_new::( + transfer_amount, + duration.into(), + 1u32.into(), + ).map_err(|_| "Failed to create vesting schedule")?; + }: force_vested_transfer(RawOrigin::Root, source_lookup, target_lookup, vesting_schedule) + verify { + assert_eq!( + expected_balance, + T::Currency::free_balance(&target), + "Transfer didn't happen", + ); + assert_eq!( + Vesting::::vesting_balance(&target), + Some(expected_balance.into()), + "Lock not correctly updated", + ); + } } impl_benchmark_test_suite!( diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 3ba12bfa69007..5bdce91f24b92 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -1935,4 +1935,10 @@ mod tests { 0 ); } + + // #[test] + // fn test_benchmarks() { + // use benchmarking::*; + // test_benchmark_last_force_vested_transfer() + // } } From ce92a019ed2534dae20343b4774de4a18658317c Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Sun, 27 Jun 2021 18:57:23 -0700 Subject: [PATCH 009/144] Update WeightInfo trait and make dummy fns --- frame/vesting/src/lib.rs | 10 ++++-- frame/vesting/src/weights.rs | 68 ++++++++++++++++++++++++++---------- 2 files changed, 57 insertions(+), 21 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 5bdce91f24b92..0eee82b0f1887 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -379,7 +379,10 @@ pub mod pallet { /// - Reads: Vesting Storage, Balances Locks, Target Account, [Sender Account] /// - Writes: Vesting Storage, Balances Locks, Target Account, [Sender Account] /// # - #[pallet::weight(T::WeightInfo::vested_transfer(MaxLocksOf::::get()))] + #[pallet::weight( + T::WeightInfo::last_vested_transfer(MaxLocksOf::::get()) + .max(T::WeightInfo::first_vested_transfer(MaxLocksOf::::get())) + )] pub fn vested_transfer( origin: OriginFor, target: ::Source, @@ -406,7 +409,10 @@ pub mod pallet { /// - Reads: Vesting Storage, Balances Locks, Target Account, Source Account /// - Writes: Vesting Storage, Balances Locks, Target Account, Source Account /// # - #[pallet::weight(T::WeightInfo::force_vested_transfer(MaxLocksOf::::get()))] + #[pallet::weight( + T::WeightInfo::first_force_vested_transfer(MaxLocksOf::::get()) + .max(T::WeightInfo::last_force_vested_transfer(MaxLocksOf::::get())) + )] pub fn force_vested_transfer( origin: OriginFor, source: ::Source, diff --git a/frame/vesting/src/weights.rs b/frame/vesting/src/weights.rs index 053453d757f38..1004c5d13a354 100644 --- a/frame/vesting/src/weights.rs +++ b/frame/vesting/src/weights.rs @@ -48,8 +48,10 @@ pub trait WeightInfo { fn vest_unlocked(l: u32, ) -> Weight; fn vest_other_locked(l: u32, ) -> Weight; fn vest_other_unlocked(l: u32, ) -> Weight; - fn vested_transfer(l: u32, ) -> Weight; - fn force_vested_transfer(l: u32, ) -> Weight; + fn first_vested_transfer(l: u32, ) -> Weight; + fn last_vested_transfer(l: u32, ) -> Weight; + fn first_force_vested_transfer(l: u32, ) -> Weight; + fn last_force_vested_transfer(l: u32, ) -> Weight; } /// Weights for pallet_vesting using the Substrate node and recommended hardware. @@ -83,14 +85,28 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn vested_transfer(l: u32, ) -> Weight { - (96_661_000 as Weight) - // Standard Error: 10_000 - .saturating_add((211_000 as Weight).saturating_mul(l as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) + fn first_vested_transfer(l: u32, ) -> Weight { + (98_812_000 as Weight) + // Standard Error: 13_000 + .saturating_add((139_000 as Weight).saturating_mul(l as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) } - fn force_vested_transfer(l: u32, ) -> Weight { + fn last_vested_transfer(l: u32, ) -> Weight { + (98_812_000 as Weight) + // Standard Error: 13_000 + .saturating_add((139_000 as Weight).saturating_mul(l as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) + } + fn first_force_vested_transfer(l: u32, ) -> Weight { + (98_812_000 as Weight) + // Standard Error: 13_000 + .saturating_add((139_000 as Weight).saturating_mul(l as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) + } + fn last_force_vested_transfer(l: u32, ) -> Weight { (98_812_000 as Weight) // Standard Error: 13_000 .saturating_add((139_000 as Weight).saturating_mul(l as Weight)) @@ -129,18 +145,32 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn vested_transfer(l: u32, ) -> Weight { - (96_661_000 as Weight) - // Standard Error: 10_000 - .saturating_add((211_000 as Weight).saturating_mul(l as Weight)) + fn first_vested_transfer(l: u32, ) -> Weight { + (45_324_000 as Weight) + // Standard Error: 12_000 + .saturating_add((214_000 as Weight).saturating_mul(l as Weight)) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + } + fn last_vested_transfer(l: u32, ) -> Weight { + (45_324_000 as Weight) + // Standard Error: 12_000 + .saturating_add((214_000 as Weight).saturating_mul(l as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn force_vested_transfer(l: u32, ) -> Weight { - (98_812_000 as Weight) - // Standard Error: 13_000 - .saturating_add((139_000 as Weight).saturating_mul(l as Weight)) - .saturating_add(RocksDbWeight::get().reads(4 as Weight)) - .saturating_add(RocksDbWeight::get().writes(4 as Weight)) + fn first_force_vested_transfer(l: u32, ) -> Weight { + (45_324_000 as Weight) + // Standard Error: 12_000 + .saturating_add((214_000 as Weight).saturating_mul(l as Weight)) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + } + fn last_force_vested_transfer(l: u32, ) -> Weight { + (45_324_000 as Weight) + // Standard Error: 12_000 + .saturating_add((214_000 as Weight).saturating_mul(l as Weight)) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } } From 0f8fef81a3c94a7bfa10d5ec00c80bb73e05f644 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 28 Jun 2021 13:26:45 -0700 Subject: [PATCH 010/144] Add merge_schedule weights --- frame/vesting/src/benchmarking.rs | 127 +++++++++++++++++++++++++++--- frame/vesting/src/lib.rs | 57 +++++++------- frame/vesting/src/weights.rs | 57 ++++++++++++-- 3 files changed, 196 insertions(+), 45 deletions(-) diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index 1f332d5806253..ce486d1e3767b 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -34,9 +34,8 @@ const ED: u32 = 256; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; fn add_locks(who: &T::AccountId, n: u8) { - for id in 0..n { + for id in 0 .. n { let lock_id = [id; 8]; - // let locked = 100u32; let locked = ED; let reasons = WithdrawReasons::TRANSFER | WithdrawReasons::RESERVE; @@ -63,8 +62,7 @@ fn add_vesting_schedules( total_locked += locked; let schedule = - VestingInfo::try_new::(locked.into(), per_block.into(), starting_block.into()) - .map_err(|_| "Failed to create vesting schedule")?; + VestingInfo::try_new::(locked.into(), per_block.into(), starting_block.into())?; assert_ok!(Vesting::::do_vested_transfer(source_lookup.clone(), target.clone(), schedule)); } @@ -82,8 +80,7 @@ benchmarks! { let expected_balance = add_vesting_schedules::(caller_lookup, T::MaxVestingSchedules::get())?; // At block zero, everything is vested. - System::::set_block_number(T::BlockNumber::zero()); - + assert_eq!(System::::block_number(), T::BlockNumber::zero()); assert_eq!( Vesting::::vesting_balance(&caller), Some(expected_balance.into()), @@ -107,6 +104,7 @@ benchmarks! { T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value() / 2u32.into()); add_locks::(&caller, l as u8); add_vesting_schedules::(caller_lookup, T::MaxVestingSchedules::get())?; + // At block 21, everything is unvested. System::::set_block_number(21u32.into()); assert_eq!( @@ -132,8 +130,9 @@ benchmarks! { T::Currency::make_free_balance_be(&other, BalanceOf::::max_value() / 2u32.into()); add_locks::(&other, l as u8); let expected_balance = add_vesting_schedules::(other_lookup.clone(), T::MaxVestingSchedules::get())?; + // At block zero, everything is vested. - System::::set_block_number(T::BlockNumber::zero()); + assert_eq!(System::::block_number(), T::BlockNumber::zero()); assert_eq!( Vesting::::vesting_balance(&other), Some(expected_balance), @@ -157,6 +156,7 @@ benchmarks! { let other: T::AccountId = account("other", 0, SEED); let other_lookup: ::Source = T::Lookup::unlookup(other.clone()); T::Currency::make_free_balance_be(&other, BalanceOf::::max_value() / 2u32.into()); + add_locks::(&other, l as u8); add_vesting_schedules::(other_lookup.clone(), T::MaxVestingSchedules::get())?; // At block 21, everything is unvested. @@ -183,6 +183,7 @@ benchmarks! { let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + let target: T::AccountId = account("target", 0, SEED); let target_lookup: ::Source = T::Lookup::unlookup(target.clone()); // Give target existing locks @@ -199,7 +200,7 @@ benchmarks! { transfer_amount, duration.into(), 1u32.into(), - ).map_err(|_| "Failed to create vesting schedule")?; + )?; }: vested_transfer(RawOrigin::Signed(caller), target_lookup, vesting_schedule) verify { assert_eq!( @@ -219,6 +220,7 @@ benchmarks! { let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + let target: T::AccountId = account("target", 0, SEED); let target_lookup: ::Source = T::Lookup::unlookup(target.clone()); // Give target existing locks @@ -231,7 +233,7 @@ benchmarks! { transfer_amount, duration.into(), 1u32.into(), - ).map_err(|_| "Failed to create vesting schedule")?; + )?; }: vested_transfer(RawOrigin::Signed(caller), target_lookup, vesting_schedule) verify { @@ -253,6 +255,7 @@ benchmarks! { let source: T::AccountId = account("source", 0, SEED); let source_lookup: ::Source = T::Lookup::unlookup(source.clone()); T::Currency::make_free_balance_be(&source, BalanceOf::::max_value()); + let target: T::AccountId = account("target", 0, SEED); let target_lookup: ::Source = T::Lookup::unlookup(target.clone()); // Give target existing locks @@ -265,7 +268,7 @@ benchmarks! { transfer_amount, duration.into(), 1u32.into(), - ).map_err(|_| "Failed to create vesting schedule")?; + )?; }: force_vested_transfer(RawOrigin::Root, source_lookup, target_lookup, vesting_schedule) verify { assert_eq!( @@ -286,6 +289,7 @@ benchmarks! { let source: T::AccountId = account("source", 0, SEED); let source_lookup: ::Source = T::Lookup::unlookup(source.clone()); T::Currency::make_free_balance_be(&source, BalanceOf::::max_value()); + let target: T::AccountId = account("target", 0, SEED); let target_lookup: ::Source = T::Lookup::unlookup(target.clone()); // Give target existing locks @@ -302,7 +306,7 @@ benchmarks! { transfer_amount, duration.into(), 1u32.into(), - ).map_err(|_| "Failed to create vesting schedule")?; + )?; }: force_vested_transfer(RawOrigin::Root, source_lookup, target_lookup, vesting_schedule) verify { assert_eq!( @@ -313,7 +317,106 @@ benchmarks! { assert_eq!( Vesting::::vesting_balance(&target), Some(expected_balance.into()), - "Lock not correctly updated", + "Lock not correctly updated", + ); + } + + not_unlocking_merge_schedules { + let l in 0 .. MaxLocksOf::::get(); + + let caller: T::AccountId = account("caller", 0, SEED); + let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); + // Give target existing locks + add_locks::(&caller, l as u8); + // Add max vesting schedules + let expected_balance = add_vesting_schedules::(caller_lookup.clone(), T::MaxVestingSchedules::get())?; + + // Schedules are not vesting at block 0 + assert_eq!(System::::block_number(), T::BlockNumber::zero()); + assert_eq!( + Vesting::::vesting_balance(&caller), + Some(expected_balance), + "Vesting balance should equal sum locked of all schedules", + ); + assert_eq!( + Vesting::::vesting(&caller).unwrap().len(), + (T::MaxVestingSchedules::get()) as usize, + "There should be exactly max vesting schedules" + ); + }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, T::MaxVestingSchedules::get() - 1) + verify { + let expected_schedule = VestingInfo::try_new::( + (ED * 40u32).into(), + (ED * 2u32).into(), + 1u32.into(), + )?; + let expected_index = (T::MaxVestingSchedules::get() - 2) as usize; + assert_eq!( + Vesting::::vesting(&caller).unwrap()[expected_index], + expected_schedule + ); + assert_eq!( + Vesting::::vesting_balance(&caller), + Some(expected_balance), + "Vesting balance should equal total locked of all schedules", + ); + assert_eq!( + Vesting::::vesting(&caller).unwrap().len(), + (T::MaxVestingSchedules::get() - 1) as usize, + "Schedule count should reduce by 1" + ); + } + + unlocking_merge_schedules { + let l in 0 .. MaxLocksOf::::get(); + + let caller: T::AccountId = account("caller", 0, SEED); + let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); + // Give target existing locks + add_locks::(&caller, l as u8); + // Add max vesting schedules + let mut expected_balance = add_vesting_schedules::(caller_lookup.clone(), T::MaxVestingSchedules::get())?; + + // Go to half way through all the schedules duration. (They all start at 1, and have a duration of 20). + System::::set_block_number(11u32.into()); + // We expect half the original locked balance + expected_balance = expected_balance / 2u32.into(); + assert_eq!( + Vesting::::vesting_balance(&caller), + Some(expected_balance), + "Vesting balance should reflect that we are half way through all schedules duration", + ); + assert_eq!( + Vesting::::vesting(&caller).unwrap().len(), + (T::MaxVestingSchedules::get()) as usize, + "There should be exactly max vesting schedules" + ); + }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, T::MaxVestingSchedules::get() - 1) + verify { + let expected_schedule = VestingInfo::try_new::( + (ED * 20u32).into(), + (ED * 2u32).into(), + 11u32.into(), + )?; + let expected_index = (T::MaxVestingSchedules::get() - 2) as usize; + assert_eq!( + Vesting::::vesting(&caller).unwrap()[expected_index], + expected_schedule, + "New schedule is properly created and placed" + ); + assert_eq!( + Vesting::::vesting(&caller).unwrap()[expected_index], + expected_schedule + ); + assert_eq!( + Vesting::::vesting_balance(&caller), + Some(expected_balance), + "Vesting balance should equal half total locked of all schedules", + ); + assert_eq!( + Vesting::::vesting(&caller).unwrap().len(), + (T::MaxVestingSchedules::get() - 1) as usize, + "Schedule count should reduce by 1" ); } } diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 0eee82b0f1887..d1fe2883e81c0 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -428,13 +428,13 @@ pub mod pallet { /// the current block will be used as the schedule start. /// /// NOTE: If `schedule1_index == schedule2_index` this is a no-op. - /// NOTE: This will vest all schedules through the current block prior to merging. + /// NOTE: This will unlock all schedules through the current block prior to merging. /// NOTE: If both schedules have ended by the current block, no new schedule will be created. /// /// Merged schedule attributes: - /// starting_block: `MAX(schedule1.starting_block, scheduled2.starting_block, current_block)` - /// ending_block: `MAX(schedule1.ending_block, schedule2.ending_block)` - /// locked: `schedule1.locked_at(current_block) + schedule2.locked_at(current_block)` + /// starting_block: `MAX(schedule1.starting_block, scheduled2.starting_block, current_block)`. + /// ending_block: `MAX(schedule1.ending_block, schedule2.ending_block)`. + /// locked: `schedule1.locked_at(current_block) + schedule2.locked_at(current_block)`. /// /// The dispatch origin for this call must be _Signed_. /// @@ -447,7 +447,10 @@ pub mod pallet { /// - Reads: TODO /// - Writes: TODO /// # - #[pallet::weight(123)] // TODO + #[pallet::weight( + T::WeightInfo::not_unlocking_merge_schedules(MaxLocksOf::::get()) + .max(T::WeightInfo::unlocking_merge_schedules(MaxLocksOf::::get())) + )] pub fn merge_schedules( origin: OriginFor, schedule1_index: u32, @@ -471,10 +474,13 @@ pub mod pallet { let schedule1 = vesting[schedule1_index]; let schedule2 = vesting[schedule2_index]; let filter = Filter::Two((schedule1_index, schedule2_index)); + // The length of vesting decreases by 2 here since we filter out 2 schedules. So we know - // below we have the space to insert the merged schedule. + // below we have the space to insert the new, merged schedule. let maybe_vesting = Self::update_lock_and_schedules(who.clone(), vesting, filter); + // We can't fail from here on because we have potentially removed two schedules. + let now = >::block_number(); if let Some(s) = Self::merge_vesting_info(now, schedule1, schedule2) { let mut vesting = maybe_vesting.unwrap_or_default(); @@ -1455,7 +1461,6 @@ mod tests { ) .unwrap(); Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); - assert_eq!(Vesting::vesting(&2).unwrap().len(), 2); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); // Got to half way through the second schedule where both schedules are actively vesting @@ -1648,11 +1653,6 @@ mod tests { }); } - #[test] - fn merge_schedules_with_zero_values_does_not_create_new_schedules() { - ExtBuilder::default().existential_deposit(256).build().execute_with(|| {}); - } - #[test] fn merge_vesting_info_handles_per_block_0() { // Faulty schedules with an infinite duration (per_block == 0) can be merged to create @@ -1672,7 +1672,8 @@ mod tests { sched1.locked_at::(cur_block); let merged_sched = VestingInfo::try_new::( merged_locked, - 1, // Merged schedule will have a per_block of 1 + // Merged schedule will have a per_block of 1, which corrects existing schedules. + 1, 10, ) .unwrap(); @@ -1805,22 +1806,22 @@ mod tests { Error::::ScheduleIndexOutOfBounds ); - // Enough schedules to merge but an index is non-existent - Vesting::vested_transfer(Some(3).into(), 2, sched0).unwrap(); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched0]); - assert_noop!( - Vesting::merge_schedules(Some(2).into(), 0, 2), - Error::::ScheduleIndexOutOfBounds - ); + // Enough schedules to merge but an index is non-existent + Vesting::vested_transfer(Some(3).into(), 2, sched0).unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched0]); + assert_noop!( + Vesting::merge_schedules(Some(2).into(), 0, 2), + Error::::ScheduleIndexOutOfBounds + ); - // Index >= max allowed schedules - let max_schedules = ::MaxVestingSchedules::get(); - Vesting::vested_transfer(Some(4).into(), 2, sched0).unwrap(); - assert_eq!(Vesting::vesting(&2).unwrap().len(), max_schedules as usize); - assert_noop!( - Vesting::merge_schedules(Some(2).into(), 0, max_schedules), - Error::::ScheduleIndexOutOfBounds - ); + // Index >= max allowed schedules + let max_schedules = ::MaxVestingSchedules::get(); + Vesting::vested_transfer(Some(4).into(), 2, sched0).unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap().len(), max_schedules as usize); + assert_noop!( + Vesting::merge_schedules(Some(2).into(), 0, max_schedules), + Error::::ScheduleIndexOutOfBounds + ); // Essentially a noop with no errors if indexes are the same. let cur_scheds = Vesting::vesting(&2); diff --git a/frame/vesting/src/weights.rs b/frame/vesting/src/weights.rs index 1004c5d13a354..31a14c3a45317 100644 --- a/frame/vesting/src/weights.rs +++ b/frame/vesting/src/weights.rs @@ -35,7 +35,6 @@ // --output=./frame/vesting/src/weights.rs // --template=./.maintain/frame-weight-template.hbs - #![allow(unused_parens)] #![allow(unused_imports)] @@ -50,8 +49,10 @@ pub trait WeightInfo { fn vest_other_unlocked(l: u32, ) -> Weight; fn first_vested_transfer(l: u32, ) -> Weight; fn last_vested_transfer(l: u32, ) -> Weight; - fn first_force_vested_transfer(l: u32, ) -> Weight; - fn last_force_vested_transfer(l: u32, ) -> Weight; + fn first_force_vested_transfer(l: u32) -> Weight; + fn last_force_vested_transfer(l: u32) -> Weight; + fn unlocking_merge_schedules(l: u32) -> Weight; + fn not_unlocking_merge_schedules(l: u32) -> Weight; } /// Weights for pallet_vesting using the Substrate node and recommended hardware. @@ -64,6 +65,7 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } + fn vest_unlocked(l: u32, ) -> Weight { (45_650_000 as Weight) // Standard Error: 12_000 @@ -71,6 +73,7 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + fn vest_other_locked(l: u32, ) -> Weight { (42_273_000 as Weight) // Standard Error: 15_000 @@ -78,6 +81,7 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + fn vest_other_unlocked(l: u32, ) -> Weight { (45_324_000 as Weight) // Standard Error: 12_000 @@ -85,6 +89,7 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } + fn first_vested_transfer(l: u32, ) -> Weight { (98_812_000 as Weight) // Standard Error: 13_000 @@ -92,6 +97,7 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } + fn last_vested_transfer(l: u32, ) -> Weight { (98_812_000 as Weight) // Standard Error: 13_000 @@ -99,6 +105,7 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } + fn first_force_vested_transfer(l: u32, ) -> Weight { (98_812_000 as Weight) // Standard Error: 13_000 @@ -106,7 +113,24 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } - fn last_force_vested_transfer(l: u32, ) -> Weight { + + fn last_force_vested_transfer(l: u32) -> Weight { + (98_812_000 as Weight) + // Standard Error: 13_000 + .saturating_add((139_000 as Weight).saturating_mul(l as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) + } + + fn unlocking_merge_schedules(l: u32) -> Weight { + (98_812_000 as Weight) + // Standard Error: 13_000 + .saturating_add((139_000 as Weight).saturating_mul(l as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) + } + + fn not_unlocking_merge_schedules(l: u32) -> Weight { (98_812_000 as Weight) // Standard Error: 13_000 .saturating_add((139_000 as Weight).saturating_mul(l as Weight)) @@ -124,6 +148,7 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } + fn vest_unlocked(l: u32, ) -> Weight { (45_650_000 as Weight) // Standard Error: 12_000 @@ -131,6 +156,7 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + fn vest_other_locked(l: u32, ) -> Weight { (42_273_000 as Weight) // Standard Error: 15_000 @@ -138,6 +164,7 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + fn vest_other_unlocked(l: u32, ) -> Weight { (45_324_000 as Weight) // Standard Error: 12_000 @@ -145,6 +172,7 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } + fn first_vested_transfer(l: u32, ) -> Weight { (45_324_000 as Weight) // Standard Error: 12_000 @@ -152,6 +180,7 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } + fn last_vested_transfer(l: u32, ) -> Weight { (45_324_000 as Weight) // Standard Error: 12_000 @@ -159,6 +188,7 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } + fn first_force_vested_transfer(l: u32, ) -> Weight { (45_324_000 as Weight) // Standard Error: 12_000 @@ -166,7 +196,24 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn last_force_vested_transfer(l: u32, ) -> Weight { + + fn last_force_vested_transfer(l: u32) -> Weight { + (45_324_000 as Weight) + // Standard Error: 12_000 + .saturating_add((214_000 as Weight).saturating_mul(l as Weight)) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + } + + fn unlocking_merge_schedules(l: u32) -> Weight { + (45_324_000 as Weight) + // Standard Error: 12_000 + .saturating_add((214_000 as Weight).saturating_mul(l as Weight)) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + } + + fn not_unlocking_merge_schedules(l: u32) -> Weight { (45_324_000 as Weight) // Standard Error: 12_000 .saturating_add((214_000 as Weight).saturating_mul(l as Weight)) From 08d547531be68d053e553082dc8c81db8617e14a Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 28 Jun 2021 15:05:22 -0700 Subject: [PATCH 011/144] Explicitly test multiple vesting scheudles --- frame/vesting/src/lib.rs | 147 +++++++++++++++++++++++++-------------- 1 file changed, 94 insertions(+), 53 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index d1fe2883e81c0..7bca4783ad71e 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -726,16 +726,16 @@ where #[cfg(test)] mod tests { - use super::*; - use crate as pallet_vesting; - - use frame_support::{assert_ok, assert_noop, parameter_types}; + use frame_support::{assert_noop, assert_ok, assert_storage_noop, parameter_types}; + use frame_system::RawOrigin; use sp_core::H256; use sp_runtime::{ testing::Header, - traits::{BlakeTwo256, IdentityLookup, Identity, BadOrigin}, + traits::{BadOrigin, BlakeTwo256, Identity, IdentityLookup}, }; - use frame_system::RawOrigin; + + use super::*; + use crate as pallet_vesting; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -944,6 +944,7 @@ mod tests { assert_eq!(free_balance, existential_deposit * (20)); assert_eq!(Vesting::vesting_balance(&2), Some(free_balance)); + // Add a 2nd schedule that is already unlocking by block #1 let sched1 = VestingInfo::try_new::( existential_deposit * 10, existential_deposit, // Vesting over 10 blocks @@ -951,43 +952,35 @@ mod tests { ) .unwrap(); assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched1)); - // Free balance is equal to the two existing schedules total amount + // Free balance is equal to the two existing schedules total amount. let free_balance = Balances::free_balance(&2); assert_eq!(free_balance, existential_deposit * (10 + 20)); + // The most recently added schedule exists. assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); - // sched1 has free funds at block #1, but nothing else + // sched1 has free funds at block #1, but nothing else. assert_eq!(Vesting::vesting_balance(&2), Some(free_balance - sched1.per_block())); - System::set_block_number(4); - assert_eq!(System::block_number(), 4); - // sched1 has freeing funds at block #4, but nothing else - assert_eq!( - Vesting::vesting_balance(&2), - Some(free_balance - sched1.per_block() * 4), - ); - + // Add a 3rd schedule let sched2 = VestingInfo::try_new::( existential_deposit * 30, existential_deposit, // Vesting over 30 blocks 5, ) .unwrap(); - // Add a 3rd schedule for user t2 assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched2)); System::set_block_number(9); - assert_eq!(System::block_number(), 9); - // Free balance is equal to the two existing schedules total amount + // Free balance is equal to the 3 existing schedules total amount. let free_balance = Balances::free_balance(&2); assert_eq!(free_balance, existential_deposit * (10 + 20 + 30)); - // sched1 and sched2 are freeing funds at block #9 + // sched1 and sched2 are freeing funds at block #9. assert_eq!( Vesting::vesting_balance(&2), Some(free_balance - sched1.per_block() * 9 - sched2.per_block() * 4) ); System::set_block_number(20); - assert_eq!(System::block_number(), 20); + // At block #20 sched1 is fully unlocked while sched2 and sched0 are partially unlocked. assert_eq!( Vesting::vesting_balance(&2), Some( @@ -998,7 +991,7 @@ mod tests { ); System::set_block_number(30); - assert_eq!(System::block_number(), 30); + // At block #30 sched0 and sched1 are fully unlocked while sched2 is partially unlocked. assert_eq!( Vesting::vesting_balance(&2), Some( @@ -1006,7 +999,7 @@ mod tests { ) ); - // Fully vested now that sched2 has finished + // At block #35 sched2 fully unlocks and thus all schedules funds are unlocked. System::set_block_number(35); assert_eq!(System::block_number(), 35); assert_eq!(Vesting::vesting_balance(&2), Some(0)); @@ -1185,7 +1178,7 @@ mod tests { Error::::AmountLow, ); - // `per_block` is 0. + // `per_block` is 0, which would result in a schedule with infinite duration. let schedule_per_block_0 = VestingInfo::unsafe_new( 256, 0, @@ -1211,16 +1204,47 @@ mod tests { assert_eq!(user2_free_balance, Balances::free_balance(&2)); assert_eq!(user4_free_balance, Balances::free_balance(&4)); - // Add max amount schedules to user 4, (additionally asserting that vested_transfer - // works with multiple schedules). - for _ in 0..::MaxVestingSchedules::get() - 1 { - assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, user2_vesting_schedule)); + + }); + } + + #[test] + fn vested_transfer_allows_max_schedules() { + let existential_deposit = 256; + ExtBuilder::default() + .existential_deposit(existential_deposit) + .build() + .execute_with(|| { + let mut user_4_free_balance = Balances::free_balance(&4); + let max_schedules = ::MaxVestingSchedules::get(); + let sched = VestingInfo::try_new::( + existential_deposit, + 1, // Vest over 256 blocks. + 10, + ) + .unwrap(); + // Add max amount schedules to user 4. + for _ in 0 .. max_schedules { + assert_ok!(Vesting::vested_transfer(Some(3).into(), 4, sched)); } - // Try to insert a 4th vesting schedule when `MaxVestingSchedules` === 3. + // The schedules count towards vesting balance + let transferred_amount = existential_deposit * max_schedules as u64; + assert_eq!(Vesting::vesting_balance(&4), Some(transferred_amount)); + // and free balance. + user_4_free_balance += transferred_amount; + assert_eq!(Balances::free_balance(&4), user_4_free_balance); + + // Cannot insert a 4th vesting schedule when `MaxVestingSchedules` === 3 assert_noop!( - Vesting::vested_transfer(Some(4).into(), 2, user2_vesting_schedule), + Vesting::vested_transfer(Some(3).into(), 4, sched), Error::::AtMaxVestingSchedules, ); + // so the free balance does not change. + assert_eq!(Balances::free_balance(&4), user_4_free_balance); + + // Account 4 has fully vested when all the schedules end + System::set_block_number(existential_deposit + 10); + assert_eq!(Vesting::vesting_balance(&4), Some(0)); }); } @@ -1292,13 +1316,6 @@ mod tests { .unwrap(); assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); - let new_vesting_schedule = VestingInfo::try_new::( - 256 * 5, - 64, // Vesting over 20 blocks - 10, - ) - .unwrap(); - // Too low transfer amount. let new_vesting_schedule_too_low = VestingInfo::unsafe_new(::MinVestedTransfer::get() - 1, 64, 10); @@ -1329,25 +1346,49 @@ mod tests { // Verify no currency transfer happened. assert_eq!(user2_free_balance, Balances::free_balance(&2)); assert_eq!(user4_free_balance, Balances::free_balance(&4)); - - // Add max amount schedules to user 4, (additionally asserting that force_vested_transfer - // works with multiple schedules). - for _ in 0 .. ::MaxVestingSchedules::get() - 1 { - assert_ok!(Vesting::force_vested_transfer( - RawOrigin::Root.into(), - 4, - 2, - new_vesting_schedule - )); - } - // Try to insert a 4th vesting schedule when `MaxVestingSchedules` === 3. - assert_noop!( - Vesting::vested_transfer(Some(4).into(), 2, user2_vesting_schedule), - Error::::AtMaxVestingSchedules, - ); }); } + #[test] + fn force_vested_transfer_allows_max_schedules() { + let existential_deposit = 256; + ExtBuilder::default() + .existential_deposit(existential_deposit) + .build() + .execute_with(|| { + let mut user_4_free_balance = Balances::free_balance(&4); + let max_schedules = ::MaxVestingSchedules::get(); + let sched = VestingInfo::try_new::( + existential_deposit, + 1, // Vest over 256 blocks. + 10, + ) + .unwrap(); + // Add max amount schedules to user 4. + for _ in 0 .. max_schedules { + assert_ok!(Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, sched)); + } + // The schedules count towards vesting balance. + let transferred_amount = existential_deposit * max_schedules as u64; + assert_eq!(Vesting::vesting_balance(&4), Some(transferred_amount)); + // and free balance. + user_4_free_balance += transferred_amount; + assert_eq!(Balances::free_balance(&4), user_4_free_balance); + + // Cannot insert a 4th vesting schedule when `MaxVestingSchedules` === 3. + assert_noop!( + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, sched), + Error::::AtMaxVestingSchedules, + ); + // so the free balance does not change. + assert_eq!(Balances::free_balance(&4), user_4_free_balance); + + // Account 4 has fully vested when all the schedules end. + System::set_block_number(existential_deposit + 10); + assert_eq!(Vesting::vesting_balance(&4), Some(0)); + }); + } + #[test] fn max_vesting_schedules_bounds_vesting_schedules() { let existential_deposit = 256; From ce18c071161bfbc6af4b8e12e7fcb8480b47fb29 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 28 Jun 2021 17:07:24 -0700 Subject: [PATCH 012/144] Make new vesting tests more more clear --- frame/vesting/src/lib.rs | 1141 +++++++++++++++++--------------------- 1 file changed, 522 insertions(+), 619 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 7bca4783ad71e..029f09a1f9778 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -828,10 +828,12 @@ mod tests { self.existential_deposit = existential_deposit; self } + pub fn vesting_genesis_config(mut self, config: Vec<(u64, u64, u64, u64)>) -> Self { self.vesting_genesis_config = Some(config); self } + pub fn build(self) -> sp_io::TestExternalities { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); @@ -862,7 +864,10 @@ mod tests { ext.execute_with(|| System::set_block_number(1)); ext } - } + } + + /// A default existential deposit. + const ED: u64 = 256; #[test] fn check_vesting_status() { @@ -923,65 +928,61 @@ mod tests { #[test] fn check_vesting_status_for_multi_schedule_account() { - let existential_deposit = 256; - ExtBuilder::default() - .existential_deposit(existential_deposit) - .build() - .execute_with(|| { - assert_eq!(System::block_number(), 1); - let sched0 = VestingInfo::try_new::( - existential_deposit * 20, - existential_deposit, // Vesting over 20 blocks - 10, - ) - .unwrap(); + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + assert_eq!(System::block_number(), 1); + let sched0 = VestingInfo::try_new::( + ED * 20, + ED, // Vesting over 20 blocks + 10, + ) + .unwrap(); - // Use 2 already has a vesting schedule. - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + // Use 2 already has a vesting schedule. + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); - // User 2's free balance is from sched0 - let free_balance = Balances::free_balance(&2); - assert_eq!(free_balance, existential_deposit * (20)); - assert_eq!(Vesting::vesting_balance(&2), Some(free_balance)); + // User 2's free balance is from sched0 + let free_balance = Balances::free_balance(&2); + assert_eq!(free_balance, ED * (20)); + assert_eq!(Vesting::vesting_balance(&2), Some(free_balance)); - // Add a 2nd schedule that is already unlocking by block #1 - let sched1 = VestingInfo::try_new::( - existential_deposit * 10, - existential_deposit, // Vesting over 10 blocks - 0, - ) - .unwrap(); - assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched1)); - // Free balance is equal to the two existing schedules total amount. - let free_balance = Balances::free_balance(&2); - assert_eq!(free_balance, existential_deposit * (10 + 20)); - // The most recently added schedule exists. - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); - // sched1 has free funds at block #1, but nothing else. - assert_eq!(Vesting::vesting_balance(&2), Some(free_balance - sched1.per_block())); - - // Add a 3rd schedule - let sched2 = VestingInfo::try_new::( - existential_deposit * 30, - existential_deposit, // Vesting over 30 blocks - 5, - ) - .unwrap(); - assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched2)); - - System::set_block_number(9); - // Free balance is equal to the 3 existing schedules total amount. - let free_balance = Balances::free_balance(&2); - assert_eq!(free_balance, existential_deposit * (10 + 20 + 30)); - // sched1 and sched2 are freeing funds at block #9. - assert_eq!( - Vesting::vesting_balance(&2), - Some(free_balance - sched1.per_block() * 9 - sched2.per_block() * 4) - ); + // Add a 2nd schedule that is already unlocking by block #1 + let sched1 = VestingInfo::try_new::( + ED * 10, + ED, // Vesting over 10 blocks + 0, + ) + .unwrap(); + assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched1)); + // Free balance is equal to the two existing schedules total amount. + let free_balance = Balances::free_balance(&2); + assert_eq!(free_balance, ED * (10 + 20)); + // The most recently added schedule exists. + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); + // sched1 has free funds at block #1, but nothing else. + assert_eq!(Vesting::vesting_balance(&2), Some(free_balance - sched1.per_block())); + + // Add a 3rd schedule + let sched2 = VestingInfo::try_new::( + ED * 30, + ED, // Vesting over 30 blocks + 5, + ) + .unwrap(); + assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched2)); + + System::set_block_number(9); + // Free balance is equal to the 3 existing schedules total amount. + let free_balance = Balances::free_balance(&2); + assert_eq!(free_balance, ED * (10 + 20 + 30)); + // sched1 and sched2 are freeing funds at block #9. + assert_eq!( + Vesting::vesting_balance(&2), + Some(free_balance - sched1.per_block() * 9 - sched2.per_block() * 4) + ); - System::set_block_number(20); - // At block #20 sched1 is fully unlocked while sched2 and sched0 are partially unlocked. - assert_eq!( + System::set_block_number(20); + // At block #20 sched1 is fully unlocked while sched2 and sched0 are partially unlocked. + assert_eq!( Vesting::vesting_balance(&2), Some( free_balance - @@ -1210,42 +1211,37 @@ mod tests { #[test] fn vested_transfer_allows_max_schedules() { - let existential_deposit = 256; - ExtBuilder::default() - .existential_deposit(existential_deposit) - .build() - .execute_with(|| { - let mut user_4_free_balance = Balances::free_balance(&4); - let max_schedules = ::MaxVestingSchedules::get(); - let sched = VestingInfo::try_new::( - existential_deposit, - 1, // Vest over 256 blocks. - 10, - ) - .unwrap(); - // Add max amount schedules to user 4. - for _ in 0 .. max_schedules { - assert_ok!(Vesting::vested_transfer(Some(3).into(), 4, sched)); - } - // The schedules count towards vesting balance - let transferred_amount = existential_deposit * max_schedules as u64; - assert_eq!(Vesting::vesting_balance(&4), Some(transferred_amount)); - // and free balance. - user_4_free_balance += transferred_amount; - assert_eq!(Balances::free_balance(&4), user_4_free_balance); - - // Cannot insert a 4th vesting schedule when `MaxVestingSchedules` === 3 - assert_noop!( - Vesting::vested_transfer(Some(3).into(), 4, sched), - Error::::AtMaxVestingSchedules, - ); - // so the free balance does not change. - assert_eq!(Balances::free_balance(&4), user_4_free_balance); + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let mut user_4_free_balance = Balances::free_balance(&4); + let max_schedules = ::MaxVestingSchedules::get(); + let sched = VestingInfo::try_new::( + ED, 1, // Vest over 256 blocks. + 10, + ) + .unwrap(); + // Add max amount schedules to user 4. + for _ in 0 .. max_schedules { + assert_ok!(Vesting::vested_transfer(Some(3).into(), 4, sched)); + } + // The schedules count towards vesting balance + let transferred_amount = ED * max_schedules as u64; + assert_eq!(Vesting::vesting_balance(&4), Some(transferred_amount)); + // and free balance. + user_4_free_balance += transferred_amount; + assert_eq!(Balances::free_balance(&4), user_4_free_balance); + + // Cannot insert a 4th vesting schedule when `MaxVestingSchedules` === 3 + assert_noop!( + Vesting::vested_transfer(Some(3).into(), 4, sched), + Error::::AtMaxVestingSchedules, + ); + // so the free balance does not change. + assert_eq!(Balances::free_balance(&4), user_4_free_balance); - // Account 4 has fully vested when all the schedules end - System::set_block_number(existential_deposit + 10); - assert_eq!(Vesting::vesting_balance(&4), Some(0)); - }); + // Account 4 has fully vested when all the schedules end + System::set_block_number(ED + 10); + assert_eq!(Vesting::vesting_balance(&4), Some(0)); + }); } #[test] @@ -1351,567 +1347,438 @@ mod tests { #[test] fn force_vested_transfer_allows_max_schedules() { - let existential_deposit = 256; - ExtBuilder::default() - .existential_deposit(existential_deposit) - .build() - .execute_with(|| { - let mut user_4_free_balance = Balances::free_balance(&4); - let max_schedules = ::MaxVestingSchedules::get(); - let sched = VestingInfo::try_new::( - existential_deposit, - 1, // Vest over 256 blocks. - 10, - ) - .unwrap(); - // Add max amount schedules to user 4. - for _ in 0 .. max_schedules { - assert_ok!(Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, sched)); - } - // The schedules count towards vesting balance. - let transferred_amount = existential_deposit * max_schedules as u64; - assert_eq!(Vesting::vesting_balance(&4), Some(transferred_amount)); - // and free balance. - user_4_free_balance += transferred_amount; - assert_eq!(Balances::free_balance(&4), user_4_free_balance); - - // Cannot insert a 4th vesting schedule when `MaxVestingSchedules` === 3. - assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, sched), - Error::::AtMaxVestingSchedules, - ); - // so the free balance does not change. - assert_eq!(Balances::free_balance(&4), user_4_free_balance); - - // Account 4 has fully vested when all the schedules end. - System::set_block_number(existential_deposit + 10); - assert_eq!(Vesting::vesting_balance(&4), Some(0)); - }); - } - - #[test] - fn max_vesting_schedules_bounds_vesting_schedules() { - let existential_deposit = 256; - // Test `vested_transfer` - ExtBuilder::default() - .existential_deposit(existential_deposit) - .build() - .execute_with(|| { - let new_vesting_schedule = - VestingInfo::try_new::(existential_deposit * 5, existential_deposit, 10) - .unwrap(); - - assert_eq!(Vesting::vesting(&3), None); - for _ in 0 .. ::MaxVestingSchedules::get() { - assert_ok!(Vesting::vested_transfer(Some(4).into(), 3, new_vesting_schedule)); - } - assert_noop!( - Vesting::vested_transfer(Some(4).into(), 3, new_vesting_schedule), - Error::::AtMaxVestingSchedules, - ); - }); + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let mut user_4_free_balance = Balances::free_balance(&4); + let max_schedules = ::MaxVestingSchedules::get(); + let sched = VestingInfo::try_new::( + ED, 1, // Vest over 256 blocks. + 10, + ) + .unwrap(); + // Add max amount schedules to user 4. + for _ in 0 .. max_schedules { + assert_ok!(Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, sched)); + } + // The schedules count towards vesting balance. + let transferred_amount = ED * max_schedules as u64; + assert_eq!(Vesting::vesting_balance(&4), Some(transferred_amount)); + // and free balance. + user_4_free_balance += transferred_amount; + assert_eq!(Balances::free_balance(&4), user_4_free_balance); + + // Cannot insert a 4th vesting schedule when `MaxVestingSchedules` === 3. + assert_noop!( + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, sched), + Error::::AtMaxVestingSchedules, + ); + // so the free balance does not change. + assert_eq!(Balances::free_balance(&4), user_4_free_balance); - // Test `force_vested_transfer` - ExtBuilder::default() - .existential_deposit(existential_deposit) - .build() - .execute_with(|| { - let new_vesting_schedule = - VestingInfo::try_new::(existential_deposit * 5, existential_deposit, 10) - .unwrap(); - - assert_eq!(Vesting::vesting(&3), None); - for _ in 0 .. ::MaxVestingSchedules::get() { - assert_ok!( - Vesting::force_vested_transfer( - RawOrigin::Root.into(), - 4, - 3, - new_vesting_schedule - ) - ); - } - assert_noop!( - Vesting::force_vested_transfer( - RawOrigin::Root.into(), - 4, - 3, - new_vesting_schedule - ), - Error::::AtMaxVestingSchedules, - ); - }); + // Account 4 has fully vested when all the schedules end. + System::set_block_number(ED + 10); + assert_eq!(Vesting::vesting_balance(&4), Some(0)); + }); } #[test] fn merge_schedules_that_have_not_started() { - // Merging schedules that have not started works - let existential_deposit = 256; - ExtBuilder::default() - .existential_deposit(existential_deposit) - .build() - .execute_with(|| { - // Account 2 should already have a vesting schedule. - let sched0 = - VestingInfo::try_new::(existential_deposit * 20, existential_deposit, 10) - .unwrap(); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); - assert_eq!(Balances::usable_balance(&2), 0); - - // Add a schedule that is identical to the one that already exists - Vesting::vested_transfer(Some(3).into(), 2, sched0).unwrap(); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched0]); - assert_eq!(Balances::usable_balance(&2), 0); - Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); - - // Since we merged identical schedules, the new schedule finishes at the same - // time as the original, just with double the amount - let sched1 = VestingInfo::try_new::( - sched0.locked() * 2, - sched0.per_block() * 2, - 10, // starts at the block the schedules are merged - ) - .unwrap(); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched1]); - - assert_eq!(Balances::usable_balance(&2), 0); - }); + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + // Account 2 should already have a vesting schedule. + let sched0 = VestingInfo::try_new::( + ED * 20, + ED, // Vest over 20 blocks. + 10, + ) + .unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + assert_eq!(Balances::usable_balance(&2), 0); + + // Add a schedule that is identical to the one that already exists + Vesting::vested_transfer(Some(3).into(), 2, sched0).unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched0]); + assert_eq!(Balances::usable_balance(&2), 0); + Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + + // Since we merged identical schedules, the new schedule finishes at the same + // time as the original, just with double the amount + let sched1 = VestingInfo::try_new::( + sched0.locked() * 2, + sched0.per_block() * 2, + 10, // starts at the block the schedules are merged + ) + .unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched1]); + + assert_eq!(Balances::usable_balance(&2), 0); + }); } #[test] fn merge_ongoing_schedules() { // Merging two schedules that have started will vest both before merging - let existential_deposit = 256; - ExtBuilder::default() - .existential_deposit(existential_deposit) - .build() - .execute_with(|| { - // Account 2 should already have a vesting schedule. - let sched0 = VestingInfo::try_new::( - existential_deposit * 20, - existential_deposit, // Vest over 20 blocks - 10, - ) - .unwrap(); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); - - let sched1 = VestingInfo::try_new::( - 300 * 10, - 300, // Vest over 10 blocks - sched0.starting_block() + 5, - ) - .unwrap(); - Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); - - // Got to half way through the second schedule where both schedules are actively vesting - let cur_block = (sched1.ending_block::() - sched1.starting_block()) / 2 + - sched1.starting_block(); - assert_eq!(cur_block, 20); - - System::set_block_number(cur_block); - - // user2 has no usable balances prior to the merge because they have not vested yet - assert_eq!(Balances::usable_balance(&2), 0); - Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); - - // Merging schedules vests all pre-existing schedules prior to merging, which is reflected - // in user2's updated usable balance - let sched0_vested_now = sched0.per_block() * (cur_block - sched0.starting_block()); - let sched1_vested_now = sched1.per_block() * (cur_block - sched1.starting_block()); - assert_eq!(Balances::usable_balance(&2), sched0_vested_now + sched1_vested_now); - - // The locked amount is the sum of schedules locked minus the amount that each schedule - // has vested up until the current block. - let sched2_locked = sched1 - .locked_at::(cur_block) - .saturating_add(sched0.locked_at::(cur_block)); - // End block of the new schedule is the greater of either merged schedule - let sched2_end = - sched1.ending_block::().max(sched0.ending_block::()); - let sched2_remaining_blocks = sched2_end - cur_block; - let sched2_per_block = sched2_locked / sched2_remaining_blocks; - let sched2 = - VestingInfo::try_new::(sched2_locked, sched2_per_block, cur_block) - .unwrap(); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2]); - }); + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + // Account 2 should already have a vesting schedule. + let sched0 = VestingInfo::try_new::( + ED * 20, + ED, // Vest over 20 blocks + 10, + ) + .unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + + let sched1 = VestingInfo::try_new::( + 256 * 10, + 256, // Vest over 10 blocks + sched0.starting_block() + 5, // Start at block 15 + ) + .unwrap(); + Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); + + // Got to half way through the second schedule where both schedules are actively vesting + let cur_block = 20; + System::set_block_number(cur_block); + + // user2 has no usable balances prior to the merge because they have not unlocked + // with `vest` yet. + assert_eq!(Balances::usable_balance(&2), 0); + Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + + // Merging schedules vests all pre-existing schedules prior to merging, which is reflected + // in user2's updated usable balance + let sched0_vested_now = sched0.per_block() * (cur_block - sched0.starting_block()); + let sched1_vested_now = sched1.per_block() * (cur_block - sched1.starting_block()); + assert_eq!(Balances::usable_balance(&2), sched0_vested_now + sched1_vested_now); + + // The locked amount is the sum of what both schedules have locked at the current block. + let sched2_locked = sched1 + .locked_at::(cur_block) + .saturating_add(sched0.locked_at::(cur_block)); + // End block of the new schedule is the greater of either merged schedule + let sched2_end = + sched1.ending_block::().max(sched0.ending_block::()); + let sched2_duration = sched2_end - cur_block; + // Based off the new schedules total locked and its duration, we can calculate the + // amount to unlock per block. + let sched2_per_block = sched2_locked / sched2_duration; + + let sched2 = + VestingInfo::try_new::(sched2_locked, sched2_per_block, cur_block).unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2]); + }); } #[test] fn merging_shifts_other_schedules_index() { // Schedules being merged are filtered out, schedules to the right of any merged - // schedule shift left and the new schedule is last - let existential_deposit = 256; - ExtBuilder::default() - .existential_deposit(existential_deposit) - .build() - .execute_with(|| { - let sched0 = VestingInfo::try_new::( - existential_deposit * 10, - existential_deposit, // Vesting over 10 blocks - 10, - ) - .unwrap(); - let sched1 = VestingInfo::try_new::( - existential_deposit * 11, - existential_deposit, // Vesting over 11 blocks - 11, - ) - .unwrap(); - let sched2 = VestingInfo::try_new::( - existential_deposit * 12, - existential_deposit, // Vesting over 12 blocks - 12, - ) - .unwrap(); - - // Account 3 start out with no schedules - assert_eq!(Vesting::vesting(&3), None); - // and some usable balance. - let usable_balance = Balances::usable_balance(&3); - assert_eq!(usable_balance, 30 * existential_deposit); - - let cur_block = 1; - assert_eq!(System::block_number(), cur_block); - - // Transfer the above 3 schedules to account 3 - Vesting::vested_transfer(Some(4).into(), 3, sched0).unwrap(); - Vesting::vested_transfer(Some(4).into(), 3, sched1).unwrap(); - Vesting::vested_transfer(Some(4).into(), 3, sched2).unwrap(); - - // With no schedules vested or merged they are in the order they are created - assert_eq!(Vesting::vesting(&3).unwrap(), vec![sched0, sched1, sched2]); - // and the usable balance has not changed - assert_eq!(usable_balance, Balances::usable_balance(&3)); - - Vesting::merge_schedules(Some(3).into(), 0, 2).unwrap(); - - // Create the merged schedule of sched0 & sched2. - let sched3_start = - sched1.starting_block().max(sched2.starting_block()).max(cur_block); - let sched3_locked = sched2 - .locked_at::(cur_block) - .saturating_add(sched0.locked_at::(cur_block)); - // End block of the new schedule is the greater of either schedule. - let sched3_end = - sched2.ending_block::().max(sched0.ending_block::()); - let sched3_remaining_blocks = sched3_end - sched3_start; - let sched3_per_block = sched3_locked / sched3_remaining_blocks; - let sched3 = - VestingInfo::try_new::(sched3_locked, sched3_per_block, sched3_start) - .unwrap(); - - let sched0_vested_now = sched0.per_block() * - (cur_block.max(sched0.starting_block()) - sched0.starting_block()); - let sched1_vested_now = sched1.per_block() * - (cur_block.max(sched1.starting_block()) - sched1.starting_block()); - let sched2_vested_now = sched2.per_block() * - (cur_block.max(sched2.starting_block()) - sched2.starting_block()); - // The not touched schedule moves left and the new merged schedule is appended. - assert_eq!(Vesting::vesting(&3).unwrap(), vec![sched1, sched3]); - // and the usable balance increases by how much the original schedules vested through - // the current block - assert_eq!( - Balances::usable_balance(&3), - usable_balance + sched1_vested_now + sched0_vested_now + sched2_vested_now - ); - }); - } - - #[test] - fn merge_ongoing_schedule_and_yet_to_be_started_schedule() { - // Merging an ongoing schedule and one that has not started yet works - let existential_deposit = 256; - ExtBuilder::default() - .existential_deposit(existential_deposit) - .build() - .execute_with(|| { - // Account 2 should already have a vesting schedule. - let sched0 = VestingInfo::try_new::( - existential_deposit * 20, - existential_deposit, // Vesting over 20 blocks - 10, - ) - .unwrap(); - assert_eq!(Vesting::vesting(&2).unwrap()[0], sched0); - assert_eq!(Vesting::vesting(&2).unwrap().len(), 1); - - // Fast forward to half way through the life of sched_1 - let mut cur_block = sched0.starting_block() + sched0.ending_block::() / 2; - System::set_block_number(cur_block); - assert_eq!(Balances::usable_balance(&2), 0); - - // Prior to vesting there is no usable balance - let mut usable_balance = 0; - assert_eq!(Balances::usable_balance(&2), usable_balance); - // We are also testing the behavior of when vest has been called on one of the - // schedules prior to merging. - Vesting::vest(Some(2).into()).unwrap(); - - // After vesting the usable balance increases by the amount the vested amount - let sched0_vested_now = - (cur_block - sched0.starting_block()) * sched0.per_block(); - usable_balance += sched0_vested_now; - assert_eq!(Balances::usable_balance(&2), usable_balance); - - // Go forward a block - cur_block += 1; - - System::set_block_number(cur_block); - - // And add a schedule that starts after this block, but before sched0 finishes. - let sched1 = VestingInfo::try_new::( - existential_deposit * 10, - 1, // Vesting over 256 * 10 (2560) blocks - cur_block + 1, - ) - .unwrap(); - Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); - - // Merge the schedules before sched1 starts. - Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); - // The usable balance only changes by the amount sched0 vested since we last called `vest` - // and is not affected by sched1 which has not started yet. - usable_balance += sched0.per_block(); - assert_eq!(Balances::usable_balance(&2), usable_balance); - - // The resulting schedule will have the later starting block of the two. - let sched2_start = sched1.starting_block(); - let sched2_locked = sched0 - .locked_at::(cur_block) - .saturating_add(sched1.locked_at::(cur_block)); - // End block of the new schedule is the greater of either schedule. - let sched2_end = - sched0.ending_block::().max(sched1.ending_block::()); - let sched2_remaining_blocks = sched2_end - sched2_start; - let sched2_per_block = sched2_locked / sched2_remaining_blocks; - let sched2 = - VestingInfo::try_new::(sched2_locked, sched2_per_block, sched2_start) - .unwrap(); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2]); + // schedule shift left and the new, merged schedule is always last. + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let sched0 = VestingInfo::try_new::( + ED * 10, + ED, // Vesting over 10 blocks + 10, + ) + .unwrap(); + let sched1 = VestingInfo::try_new::( + ED * 11, + ED, // Vesting over 11 blocks + 11, + ) + .unwrap(); + let sched2 = VestingInfo::try_new::( + ED * 12, + ED, // Vesting over 12 blocks + 12, + ) + .unwrap(); - }); + // Account 3 start out with no schedules + assert_eq!(Vesting::vesting(&3), None); + // and some usable balance. + let usable_balance = Balances::usable_balance(&3); + assert_eq!(usable_balance, 30 * ED); + + let cur_block = 1; + assert_eq!(System::block_number(), cur_block); + + // Transfer the above 3 schedules to account 3 + assert_ok!(Vesting::vested_transfer(Some(4).into(), 3, sched0)); + assert_ok!(Vesting::vested_transfer(Some(4).into(), 3, sched1)); + assert_ok!(Vesting::vested_transfer(Some(4).into(), 3, sched2)); + + // With no schedules vested or merged they are in the order they are created + assert_eq!(Vesting::vesting(&3).unwrap(), vec![sched0, sched1, sched2]); + // and the usable balance has not changed. + assert_eq!(usable_balance, Balances::usable_balance(&3)); + + Vesting::merge_schedules(Some(3).into(), 0, 2).unwrap(); + + // Create the merged schedule of sched0 & sched2. + // The merged schedule will have the max possible starting block, + let sched3_start = sched1.starting_block().max(sched2.starting_block()); + // have locked equal to the sum of the two schedules locked through the current block, + let sched3_locked = + sched2.locked_at::(cur_block) + sched0.locked_at::(cur_block); + // and will end at the max possible block. + let sched3_end = + sched2.ending_block::().max(sched0.ending_block::()); + let sched3_duration = sched3_end - sched3_start; + let sched3_per_block = sched3_locked / sched3_duration; + let sched3 = + VestingInfo::try_new::(sched3_locked, sched3_per_block, sched3_start) + .unwrap(); + + // The not touched schedule moves left and the new merged schedule is appended. + assert_eq!(Vesting::vesting(&3).unwrap(), vec![sched1, sched3]); + // The usable balance hasn't changed since none of the schedules have started. + assert_eq!(Balances::usable_balance(&3), usable_balance); + }); } #[test] - fn merge_vesting_info_handles_per_block_0() { - // Faulty schedules with an infinite duration (per_block == 0) can be merged to create - // a schedule that vest 1 per_block (helpful for faulty legacy schedules) - let existential_deposit = 256; - ExtBuilder::default() - .existential_deposit(existential_deposit) - .build() - .execute_with(|| { - // Merge a two schedules both with a duration greater than there - // locked amount - let sched0 = VestingInfo::unsafe_new(existential_deposit, 0, 1); - let sched1 = VestingInfo::unsafe_new(existential_deposit * 2, 0, 10); - - let cur_block = 5; - let merged_locked = sched0.locked_at::(cur_block) + - sched1.locked_at::(cur_block); - let merged_sched = VestingInfo::try_new::( - merged_locked, - // Merged schedule will have a per_block of 1, which corrects existing schedules. - 1, - 10, - ) - .unwrap(); - assert_eq!( - merged_sched, - Vesting::merge_vesting_info(cur_block, sched0, sched1).unwrap() - ); - }); + fn merge_ongoing_and_yet_to_be_started_schedule() { + // Merge an ongoing schedule that has had `vest` called and a schedule that has not already + // started. + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + // Account 2 should already have a vesting schedule. + let sched0 = VestingInfo::try_new::( + ED * 20, + ED, // Vesting over 20 blocks + 10, + ) + .unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + + // Fast forward to half way through the life of sched_1 + let mut cur_block = (sched0.starting_block() + sched0.ending_block::()) / 2; + assert_eq!(cur_block, 20); + System::set_block_number(cur_block); + + // Prior to vesting there is no usable balance. + let mut usable_balance = 0; + assert_eq!(Balances::usable_balance(&2), usable_balance); + // Vest the current schedules (which is just sched0 now). + Vesting::vest(Some(2).into()).unwrap(); + + // After vesting the usable balance increases by the amount the unlocked amount. + let sched0_vested_now = sched0.locked() - sched0.locked_at::(cur_block); + usable_balance += sched0_vested_now; + assert_eq!(Balances::usable_balance(&2), usable_balance); + + // Go forward a block. + cur_block += 1; + System::set_block_number(cur_block); + + // And add a schedule that starts after this block, but before sched0 finishes. + let sched1 = VestingInfo::try_new::( + ED * 10, + 1, // Vesting over 256 * 10 (2560) blocks + cur_block + 1, + ) + .unwrap(); + Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); + + // Merge the schedules before sched1 starts. + Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + // After merging, the usable balance only changes by the amount sched0 vested since we + // last called `vest` (which is just 1 block). The usable balance is not affected by + // sched1 because it has not started yet. + usable_balance += sched0.per_block(); + assert_eq!(Balances::usable_balance(&2), usable_balance); + + // The resulting schedule will have the later starting block of the two, + let sched2_start = sched1.starting_block(); + // have locked equal to the sum of the two schedules locked through the current block, + let sched2_locked = + sched0.locked_at::(cur_block) + sched1.locked_at::(cur_block); + // and will end at the max possible block. + let sched2_end = + sched0.ending_block::().max(sched1.ending_block::()); + let sched2_duration = sched2_end - sched2_start; + let sched2_per_block = sched2_locked / sched2_duration; + + let sched2 = + VestingInfo::try_new::(sched2_locked, sched2_per_block, sched2_start) + .unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2]); + }); } #[test] fn merge_finishing_and_ongoing_schedule() { - // If a schedule finishes by the block we treat the ongoing schedule as the merged one - let existential_deposit = 256; - ExtBuilder::default() - .existential_deposit(existential_deposit) - .build() - .execute_with(|| { - // Account 2 should already have a vesting schedule. - let sched0 = - VestingInfo::try_new::(existential_deposit * 20, existential_deposit, 10) - .unwrap(); - assert_eq!(sched0.ending_block::(), 30); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); - - let sched1 = - VestingInfo::try_new::(existential_deposit * 40, existential_deposit, 10) - .unwrap(); - assert_eq!(sched1.ending_block::(), 50); - Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); - - // Transfer a 3rd schedule, so we can demonstrate how schedule indices change - // (We are not merging this schedule) - let sched2 = - VestingInfo::try_new::(existential_deposit * 30, existential_deposit, 10) - .unwrap(); - Vesting::vested_transfer(Some(3).into(), 2, sched2).unwrap(); - - // Current schedule order, prior to any merging - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1, sched2]); - - // Fast forward to sched0's end block - let current_block = sched0.ending_block::(); - System::set_block_number(current_block); + // If a schedule finishes by the current block we treat the ongoing schedule, + // without any alterations, as the merged one. + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + // Account 2 should already have a vesting schedule. + let sched0 = VestingInfo::try_new::( + ED * 20, + ED, // Duration of 20. + 10, + ) + .unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); - assert_eq!(System::block_number(), 30); - // Prior to merge_schedules and with no vest/vest_other called the user has no usable - // balance. - assert_eq!(Balances::usable_balance(&2), 0); - Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); - - // sched2 is now the first, since sched0 & sched1 get filtered out while "merging". - // sched1 gets treated like the new merged schedule by getting pushed onto back - // of the vesting schedules vec. Note: sched0 finished at the current block. - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2, sched1]); - - let sched0_vested_now = sched0.per_block() * - (sched0.ending_block::() - sched0.starting_block()); - let sched1_vested_now = - sched1.per_block() * (current_block - sched1.starting_block()); - let sched2_vested_now = - sched2.per_block() * (current_block - sched2.starting_block()); - // The users usable balance after merging includes all pre-existing - // schedules vested through the current block, including schedules not merged - assert_eq!( - Balances::usable_balance(&2), - sched0_vested_now + sched1_vested_now + sched2_vested_now - ); - }); + let sched1 = VestingInfo::try_new::( + ED * 40, + ED, // Duration of 40. + 10, + ) + .unwrap(); + assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched1)); + + // Transfer a 3rd schedule, so we can demonstrate how schedule indices change. + // (We are not merging this schedule.) + let sched2 = VestingInfo::try_new::( + ED * 30, + ED, // Duration of 30. + 10, + ) + .unwrap(); + assert_ok!(Vesting::vested_transfer(Some(3).into(), 2, sched2)); + + // The schedules are in expected order prior to merging. + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1, sched2]); + + // Fast forward to sched0's end block. + let cur_block = sched0.ending_block::(); + System::set_block_number(cur_block); + assert_eq!(System::block_number(), 30); + + // Prior to merge_schedules and with no vest/vest_other called the user has no usable + // balance. + assert_eq!(Balances::usable_balance(&2), 0); + Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + + // sched2 is now the first, since sched0 & sched1 get filtered out while "merging". + // sched1 gets treated like the new merged schedule by getting pushed onto back + // of the vesting schedules vec. Note: sched0 finished at the current block. + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2, sched1]); + + // sched0 has finished, so its funds are fully unlocked. + let sched0_unlocked_now = sched0.locked(); + // The remaining schedules are ongoing, so their funds are partially unlocked. + let sched1_unlocked_now = sched1.locked() - sched1.locked_at::(cur_block); + let sched2_unlocked_now = sched2.locked() - sched2.locked_at::(cur_block); + + // Since merging also vests all the schedules, the users usable balance after merging + // includes all pre-existing schedules unlocked through the current block, including + // schedules not merged. + assert_eq!( + Balances::usable_balance(&2), + sched0_unlocked_now + sched1_unlocked_now + sched2_unlocked_now + ); + }); } #[test] fn merge_finishing_schedules_does_not_create_a_new_one() { // If both schedules finish by the current block we don't create new one - let existential_deposit = 256; - ExtBuilder::default() - .existential_deposit(existential_deposit) - .build() - .execute_with(|| { - // Account 2 should already have a vesting schedule. - let sched0 = - VestingInfo::try_new::(existential_deposit * 20, existential_deposit, 10) - .unwrap(); - assert_eq!(sched0.ending_block::(), 30); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); - - let sched1 = - VestingInfo::try_new::(existential_deposit * 30, existential_deposit, 10) - .unwrap(); - assert_eq!(sched1.ending_block::(), 40); - Vesting::vested_transfer(Some(3).into(), 2, sched1).unwrap(); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); - - let all_scheds_end = - sched0.ending_block::().max(sched1.ending_block::()); - assert_eq!(all_scheds_end, 40); - System::set_block_number(all_scheds_end); - // Prior to merge_schedules and with no vest/vest_other called the user has no usable - // balance. - assert_eq!(Balances::usable_balance(&2), 0); - Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); - // The user no longer has any more vesting schedules because they both ended at the - // block they where merged - assert_eq!(Vesting::vesting(&2), None); - - let sched0_vested_now = sched0.per_block() * - (sched0.ending_block::() - sched0.starting_block()); - let sched1_vested_now = sched1.per_block() * - (sched1.ending_block::() - sched1.starting_block()); - assert_eq!(Balances::usable_balance(&2), sched0_vested_now + sched1_vested_now); - }); + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + // Account 2 should already have a vesting schedule. + let sched0 = VestingInfo::try_new::( + ED * 20, + ED, // 20 block duration. + 10, + ) + .unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + + // Create sched1 and transfer it to account 2. + let sched1 = VestingInfo::try_new::( + ED * 30, + ED, // 30 block duration. + 10, + ) + .unwrap(); + assert_ok!(Vesting::vested_transfer(Some(3).into(), 2, sched1)); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); + + let all_scheds_end = + sched0.ending_block::().max(sched1.ending_block::()); + assert_eq!(all_scheds_end, 40); + System::set_block_number(all_scheds_end); + + // Prior to merge_schedules and with no vest/vest_other called the user has no usable + // balance. + assert_eq!(Balances::usable_balance(&2), 0); + + // Merge schedule 0 and 1. + Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + // The user no longer has any more vesting schedules because they both ended at the + // block they where merged. + assert_eq!(Vesting::vesting(&2), None); + // And their usable balance has increased by the total amount locked in the merged + // schedules. + assert_eq!(Balances::usable_balance(&2), sched0.locked() + sched1.locked()); + }); } #[test] fn merge_schedules_throws_proper_errors() { - let existential_deposit = 256; - ExtBuilder::default() - .existential_deposit(existential_deposit) - .build() - .execute_with(|| { - // Account 2 should already have a vesting schedule. - let sched0 = - VestingInfo::try_new::(existential_deposit * 20, existential_deposit, 10) - .unwrap(); + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + // Account 2 should already have a vesting schedule. + let sched0 = VestingInfo::try_new::( + ED * 20, + ED, // 20 block duration. + 10, + ) + .unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); - // Not enough vesting schedules - assert_eq!(Vesting::vesting(&2).unwrap().len(), 1); - assert_noop!( - Vesting::merge_schedules(Some(2).into(), 0, 1), - Error::::ScheduleIndexOutOfBounds - ); + // There is only 1 vesting schedule. + assert_noop!( + Vesting::merge_schedules(Some(2).into(), 0, 1), + Error::::ScheduleIndexOutOfBounds + ); - // Enough schedules to merge but an index is non-existent - Vesting::vested_transfer(Some(3).into(), 2, sched0).unwrap(); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched0]); - assert_noop!( - Vesting::merge_schedules(Some(2).into(), 0, 2), - Error::::ScheduleIndexOutOfBounds - ); + // There are 0 vesting schedules. + assert_eq!(Vesting::vesting(&4), None); + assert_noop!(Vesting::merge_schedules(Some(4).into(), 0, 1), Error::::NotVesting); - // Index >= max allowed schedules - let max_schedules = ::MaxVestingSchedules::get(); - Vesting::vested_transfer(Some(4).into(), 2, sched0).unwrap(); - assert_eq!(Vesting::vesting(&2).unwrap().len(), max_schedules as usize); - assert_noop!( - Vesting::merge_schedules(Some(2).into(), 0, max_schedules), - Error::::ScheduleIndexOutOfBounds - ); + // There are enough schedules to merge but an index is non-existent. + Vesting::vested_transfer(Some(3).into(), 2, sched0).unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched0]); + assert_noop!( + Vesting::merge_schedules(Some(2).into(), 0, 2), + Error::::ScheduleIndexOutOfBounds + ); - // Essentially a noop with no errors if indexes are the same. - let cur_scheds = Vesting::vesting(&2); - let usable_balance = Balances::usable_balance(&2); - assert_ok!(Vesting::merge_schedules(Some(2).into(), 0, 0)); - // The relevant storage is invariant. - assert_eq!(Vesting::vesting(&2), cur_scheds); - assert_eq!(usable_balance, Balances::usable_balance(&2)); - }); + // It is a storage noop with no errors if the indexes are the same. + assert_storage_noop!(Vesting::merge_schedules(Some(2).into(), 0, 0).unwrap()); + }); } #[test] fn generates_multiple_schedules_from_genesis_config() { - let existential_deposit = 256; let vesting_config = vec![ - // 5 * existential_deposit locked - (1, 0, 10, 5 * existential_deposit), - // 1 * existential_deposit locked - (2, 10, 20, 19 * existential_deposit), - // 2 * existential_deposit locked - (2, 10, 20, 18 * existential_deposit), - // 1 * existential_deposit locked - (12, 10, 20, 9 * existential_deposit), - // 2 * existential_deposit locked - (12, 10, 20, 8 * existential_deposit), - // 3 * existential_deposit locked - (12, 10, 20, 7 * existential_deposit), + // 5 * existential deposit locked + (1, 0, 10, 5 * ED), + // 1 * existential deposit locked + (2, 10, 20, 19 * ED), + // 2 * existential deposit locked + (2, 10, 20, 18 * ED), + // 1 * existential deposit locked + (12, 10, 20, 9 * ED), + // 2 * existential deposit locked + (12, 10, 20, 8 * ED), + // 3 * existential deposit locked + (12, 10, 20, 7 * ED), ]; ExtBuilder::default() - .existential_deposit(existential_deposit) + .existential_deposit(ED) .vesting_genesis_config(vesting_config) .build() .execute_with(|| { - let user1_sched1 = - VestingInfo::try_new::(5 * existential_deposit, 128, 0u64).unwrap(); + let user1_sched1 = VestingInfo::try_new::(5 * ED, 128, 0u64).unwrap(); assert_eq!(Vesting::vesting(&1).unwrap(), vec![user1_sched1]); - let user2_sched1 = - VestingInfo::try_new::(1 * existential_deposit, 12, 10u64).unwrap(); - let user2_sched2 = - VestingInfo::try_new::(2 * existential_deposit, 25, 10u64).unwrap(); + let user2_sched1 = VestingInfo::try_new::(1 * ED, 12, 10u64).unwrap(); + let user2_sched2 = VestingInfo::try_new::(2 * ED, 25, 10u64).unwrap(); assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_sched1, user2_sched2]); - let user12_sched1 = - VestingInfo::try_new::(1 * existential_deposit, 12, 10u64).unwrap(); - let user12_sched2 = - VestingInfo::try_new::(2 * existential_deposit, 25, 10u64).unwrap(); - let user12_sched3 = - VestingInfo::try_new::(3 * existential_deposit, 38, 10u64).unwrap(); + let user12_sched1 = VestingInfo::try_new::(1 * ED, 12, 10u64).unwrap(); + let user12_sched2 = VestingInfo::try_new::(2 * ED, 25, 10u64).unwrap(); + let user12_sched3 = VestingInfo::try_new::(3 * ED, 38, 10u64).unwrap(); assert_eq!( Vesting::vesting(&12).unwrap(), vec![user12_sched1, user12_sched2, user12_sched3] @@ -1919,29 +1786,71 @@ mod tests { }); } + #[test] + #[should_panic] + fn multiple_schedules_from_genesis_config_errors() { + // MaxVestingSchedules is 3, but this config has 4 for account 12 so we panic when building + // from genesis. + let vesting_config = + vec![(12, 10, 20, ED), (12, 10, 20, ED), (12, 10, 20, ED), (12, 10, 20, ED)]; + ExtBuilder::default() + .existential_deposit(ED) + .vesting_genesis_config(vesting_config) + .build(); + } + + #[test] + fn merge_vesting_info_handles_per_block_0() { + // Faulty schedules with an infinite duration (per_block == 0) can be merged to create + // a schedule that vest 1 per_block, (helpful for faulty, legacy schedules). + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let sched0 = VestingInfo::unsafe_new(ED, 0, 1); + let sched1 = VestingInfo::unsafe_new(ED * 2, 0, 10); + + let cur_block = 5; + let expected_locked = + sched0.locked_at::(cur_block) + sched1.locked_at::(cur_block); + let expected_sched = VestingInfo::try_new::( + expected_locked, + 1, // per_block of 1, which corrects existing schedules. + 10, + ) + .unwrap(); + assert_eq!( + Vesting::merge_vesting_info(cur_block, sched0, sched1), + Some(expected_sched), + ); + }); + } + #[test] fn vesting_info_validation_works() { let min_transfer = ::MinVestedTransfer::get(); - // `locked` cannot be less than min_transfer (non 0 case) + // `locked` cannot be less than min_transfer (non 0 case). match VestingInfo::try_new::(min_transfer - 1, 1u64, 10u64) { Err(Error::::AmountLow) => (), _ => panic!(), } - // `locked` cannot be 0 + + // `locked` cannot be 0. match VestingInfo::try_new::(0, 1u64, 10u64) { Err(Error::::InvalidScheduleParams) => (), _ => panic!(), } - // `per_block` cannot be 0 + + // `per_block` cannot be 0. match VestingInfo::try_new::(min_transfer + 1, 0u64, 10u64) { Err(Error::::InvalidScheduleParams) => (), _ => panic!(), } - // Valid inputs - let ok_sched = VestingInfo::try_new::(min_transfer, 1u64, 10u64); - assert!(ok_sched.is_ok()); - assert_eq!(ok_sched.unwrap(), VestingInfo::unsafe_new(min_transfer, 1u64, 10u64)); - // `per_block` is never bigger than `locked` + + // With valid inputs it does not error and does not modify the inputs. + assert_eq!( + VestingInfo::try_new::(min_transfer, 1u64, 10u64).unwrap(), + VestingInfo::unsafe_new(min_transfer, 1u64, 10u64) + ); + + // `per_block` is never bigger than `locked`. assert_eq!( VestingInfo::try_new::(256u64, 256 * 2u64, 10u64).unwrap(), VestingInfo::try_new::(256u64, 256u64, 10u64).unwrap() @@ -1950,28 +1859,28 @@ mod tests { #[test] fn vesting_info_ending_block_works() { - // Treats `per_block` 0 as a `per_block` of 1 + // Treats `per_block` 0 as a `per_block` of 1 to accommodate faulty, legacy schedules. let per_block_0 = VestingInfo::unsafe_new(256u32, 0u32, 10u32); - let per_block_1 = VestingInfo::try_new::(256u32, 1u32, 10u32).unwrap(); assert_eq!( per_block_0.ending_block::(), per_block_0.locked() + per_block_0.starting_block() ); + let per_block_1 = VestingInfo::try_new::(256u32, 1u32, 10u32).unwrap(); assert_eq!(per_block_0.ending_block::(), per_block_1.ending_block::()); // `per_block >= locked` always results in a schedule ending the block after it starts let per_block_gt_locked = VestingInfo::unsafe_new(256u32, 256 * 2u32, 10u32); - let per_block_eq_locked = VestingInfo::try_new::(256u32, 256u32, 10u32).unwrap(); assert_eq!( per_block_gt_locked.ending_block::(), 1 + per_block_gt_locked.starting_block() ); + let per_block_eq_locked = VestingInfo::try_new::(256u32, 256u32, 10u32).unwrap(); assert_eq!( per_block_gt_locked.ending_block::(), per_block_eq_locked.ending_block::() ); - // Correctly calcs end if `locked % per_block != 0` + // Correctly calcs end if `locked % per_block != 0`. (We need a block to unlock the remainder). let imperfect_per_block = VestingInfo::try_new::(256u32, 250u32, 10u32).unwrap(); assert_eq!( imperfect_per_block.ending_block::(), @@ -1983,10 +1892,4 @@ mod tests { 0 ); } - - // #[test] - // fn test_benchmarks() { - // use benchmarking::*; - // test_benchmark_last_force_vested_transfer() - // } } From 5e673b807e6b7ff329777cde0752f99ecda90656 Mon Sep 17 00:00:00 2001 From: Zeke Mostov <32168567+emostov@users.noreply.github.com> Date: Mon, 28 Jun 2021 17:26:07 -0700 Subject: [PATCH 013/144] Apply suggestions from code review --- frame/vesting/src/lib.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 029f09a1f9778..bebf42041acfe 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -310,7 +310,7 @@ pub mod pallet { /// At least one of the indexes is out of bounds of the vesting schedules. ScheduleIndexOutOfBounds, /// Failed to create a new schedule because the parameters where invalid. i.e. `per_block` or - /// `locked` was a 0. + /// `locked` was 0. InvalidScheduleParams, } @@ -423,9 +423,10 @@ pub mod pallet { Self::do_vested_transfer(source, target, schedule) } - /// Merge two vesting schedules together, creating a new vesting schedule that vests over - /// that finishes at the highest start and end blocks. If both schedules have already started - /// the current block will be used as the schedule start. + /// Merge two vesting schedules together, creating a new vesting schedule that unlocks over + /// highest possible start and end blocks. If both schedules have already started the current + /// block will be used as the schedule start; with the caveat that if one schedule is finishes by + /// the current block, the other will be treated as the new merged schedule, unmodified. /// /// NOTE: If `schedule1_index == schedule2_index` this is a no-op. /// NOTE: This will unlock all schedules through the current block prior to merging. @@ -438,8 +439,8 @@ pub mod pallet { /// /// The dispatch origin for this call must be _Signed_. /// - /// - `schedule1_index`: TODO - /// - `schedule2_index`: TODO + /// - `schedule1_index`: index of the first schedule to merge. + /// - `schedule2_index`: index of the second schedule to merge. /// /// # /// - `O(1)`. @@ -475,8 +476,8 @@ pub mod pallet { let schedule2 = vesting[schedule2_index]; let filter = Filter::Two((schedule1_index, schedule2_index)); - // The length of vesting decreases by 2 here since we filter out 2 schedules. So we know - // below we have the space to insert the new, merged schedule. + // The length of vesting decreases by 2 here since we filter out 2 schedules. Thus we know + // below that we can safely insert the new merged schedule. let maybe_vesting = Self::update_lock_and_schedules(who.clone(), vesting, filter); // We can't fail from here on because we have potentially removed two schedules. From 1c1a56d8945084f682562010a0f9666b63faa295 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 28 Jun 2021 17:34:49 -0700 Subject: [PATCH 014/144] Update remove_vesting_schedule to error with no index --- frame/vesting/src/lib.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index bebf42041acfe..1217933b6e393 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -704,17 +704,11 @@ where Ok(()) } - /// Remove a vesting schedule for a given account. + /// Remove a vesting schedule for a given account. Will error if `schedule_index` is `None`. fn remove_vesting_schedule(who: &T::AccountId, schedule_index: Option) -> DispatchResult { - let filter = if let Some(schedule_index) = schedule_index { - ensure!( - schedule_index < T::MaxVestingSchedules::get(), - Error::::ScheduleIndexOutOfBounds - ); - Filter::One(schedule_index as usize) - } else { - Filter::Zero - }; + let schedule_index = schedule_index.ok_or(Error::::ScheduleIndexOutOfBounds)?; + let filter = Filter::One(schedule_index as usize); + let vesting = Self::vesting(who).ok_or(Error::::NotVesting)?; if let Some(v) = Self::update_lock_and_schedules(who.clone(), vesting, filter) { Vesting::::insert(&who, v); From 4ea964c781231919c9f40a2fa894b034ebe31600 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 28 Jun 2021 17:37:18 -0700 Subject: [PATCH 015/144] Try reduce spacing diff --- frame/vesting/src/lib.rs | 59 +++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 1217933b6e393..d7650134f37bc 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -866,34 +866,37 @@ mod tests { #[test] fn check_vesting_status() { - ExtBuilder::default().existential_deposit(256).build().execute_with(|| { - let user1_free_balance = Balances::free_balance(&1); - let user2_free_balance = Balances::free_balance(&2); - let user12_free_balance = Balances::free_balance(&12); - assert_eq!(user1_free_balance, 256 * 10); // Account 1 has free balance - assert_eq!(user2_free_balance, 256 * 20); // Account 2 has free balance - assert_eq!(user12_free_balance, 256 * 10); // Account 12 has free balance - let user1_vesting_schedule = VestingInfo::try_new::( - 256 * 5, - 128, // Vesting over 10 blocks - 0, - ) - .unwrap(); - let user2_vesting_schedule = VestingInfo::try_new::( - 256 * 20, - 256, // Vesting over 20 blocks - 10, - ) - .unwrap(); - let user12_vesting_schedule = VestingInfo::try_new::( - 256 * 5, - 64, // Vesting over 20 blocks - 10, - ) - .unwrap(); - assert_eq!(Vesting::vesting(&1).unwrap(), vec![user1_vesting_schedule]); // Account 1 has a vesting schedule - assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); // Account 2 has a vesting schedule - assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); // Account 12 has a vesting schedule + ExtBuilder::default() + .existential_deposit(256) + .build() + .execute_with(|| { + let user1_free_balance = Balances::free_balance(&1); + let user2_free_balance = Balances::free_balance(&2); + let user12_free_balance = Balances::free_balance(&12); + assert_eq!(user1_free_balance, 256 * 10); // Account 1 has free balance + assert_eq!(user2_free_balance, 256 * 20); // Account 2 has free balance + assert_eq!(user12_free_balance, 256 * 10); // Account 12 has free balance + let user1_vesting_schedule = VestingInfo::try_new::( + 256 * 5, + 128, // Vesting over 10 blocks + 0, + ) + .unwrap(); + let user2_vesting_schedule = VestingInfo::try_new::( + 256 * 20, + 256, // Vesting over 20 blocks + 10, + ) + .unwrap(); + let user12_vesting_schedule = VestingInfo::try_new::( + 256 * 5, + 64, // Vesting over 20 blocks + 10, + ) + .unwrap(); + assert_eq!(Vesting::vesting(&1).unwrap(), vec![user1_vesting_schedule]); // Account 1 has a vesting schedule + assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); // Account 2 has a vesting schedule + assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); // Account 12 has a vesting schedule // Account 1 has only 128 units vested from their illiquid 256 * 5 units at block 1 assert_eq!(Vesting::vesting_balance(&1), Some(128 * 9)); From 48d0ec4b69d93d5e3ebb6354224d6296b048eea1 Mon Sep 17 00:00:00 2001 From: Zeke Mostov <32168567+emostov@users.noreply.github.com> Date: Mon, 28 Jun 2021 22:30:08 -0700 Subject: [PATCH 016/144] Apply suggestions from code review --- frame/vesting/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index d7650134f37bc..d4fe46b5630e5 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -674,7 +674,7 @@ where /// Adds a vesting schedule to a given account. /// - /// If there already `MaxVestingSchedules`, an Error is returned and nothing + /// If there are already `MaxVestingSchedules`, an Error is returned and nothing /// is updated. /// /// On success, a linearly reducing amount of funds will be locked. In order to realise any From 1af7527d6ca7aac73c169785500b38ef96b5ab6a Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 29 Jun 2021 10:06:51 -0700 Subject: [PATCH 017/144] Use get on vesting for bounds check; check origin first --- frame/vesting/src/lib.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index d7650134f37bc..6ee07b48770dc 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -457,23 +457,18 @@ pub mod pallet { schedule1_index: u32, schedule2_index: u32, ) -> DispatchResult { + let who = ensure_signed(origin)?; if schedule1_index == schedule2_index { return Ok(()); }; - let who = ensure_signed(origin)?; let schedule1_index = schedule1_index as usize; let schedule2_index = schedule2_index as usize; let vesting = Self::vesting(&who).ok_or(Error::::NotVesting)?; - let len = vesting.len(); - ensure!( - schedule1_index < len && schedule2_index < len, - Error::::ScheduleIndexOutOfBounds - ); // The schedule index is based off of the schedule ordering prior to filtering out any // schedules that may be ending at this block. - let schedule1 = vesting[schedule1_index]; - let schedule2 = vesting[schedule2_index]; + let schedule1 = *vesting.get(schedule1_index).ok_or(Error::::ScheduleIndexOutOfBounds)?; + let schedule2 = *vesting.get(schedule2_index).ok_or(Error::::ScheduleIndexOutOfBounds)?; let filter = Filter::Two((schedule1_index, schedule2_index)); // The length of vesting decreases by 2 here since we filter out 2 schedules. Thus we know From b87b3c0522d844b737c0e1fcccac5e4d0eeb596e Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 29 Jun 2021 10:27:48 -0700 Subject: [PATCH 018/144] No filter tuple; various simplifications --- frame/vesting/src/lib.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index cfa8ed11ded04..4f0056e8d26f6 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -110,6 +110,7 @@ mod vesting_info { /// Instantiate a new `VestingInfo` without param validation. Useful for /// mocking bad inputs in testing. + #[cfg(test)] pub fn unsafe_new( locked: Balance, per_block: Balance, @@ -118,7 +119,7 @@ mod vesting_info { VestingInfo { locked, per_block, starting_block } } - /// Locked amount at genesis. + /// Locked amount at schedule creation. pub fn locked(&self) -> Balance { self.locked } @@ -143,20 +144,17 @@ mod vesting_info { let vested_block_count = n.saturating_sub(self.starting_block); let vested_block_count = BlockNumberToBalance::convert(vested_block_count); // Return amount that is still locked in vesting - let maybe_balance = vested_block_count.checked_mul(&self.per_block); - if let Some(balance) = maybe_balance { - self.locked.saturating_sub(balance) - } else { - Zero::zero() - } + vested_block_count.checked_mul(&self.per_block) + .map(|balance| self.locked.saturating_sub(balance)) + .unwrap_or(Zero::zero()) } - /// Block number at which the schedule ends + /// Block number at which the schedule ends. pub fn ending_block>(&self) -> Balance { let starting_block = BlockNumberToBalance::convert(self.starting_block); let duration = if self.per_block > self.locked { // If `per_block` is bigger than `locked`, the schedule will end - // the block after starting + // the block after starting. 1u32.into() } else if self.per_block.is_zero() { // Check for div by 0 errors, which should only be from legacy @@ -183,16 +181,16 @@ enum Filter { /// Filter out 1 schedule. One(usize), /// Filter out 2 schedules. - Two((usize, usize)), + Two(usize, usize), } impl Filter { - /// Wether or not the filter says the schedule index should be removed. + /// Whether or not the filter says the schedule index should be removed. fn should_remove(&self, index: &usize) -> bool { match self { Self::Zero => false, Self::One(index1) => index1 == index, - Self::Two((index1, index2)) => index1 == index || index2 == index, + Self::Two(index1, index2) => index1 == index || index2 == index, } } } @@ -469,7 +467,7 @@ pub mod pallet { // schedules that may be ending at this block. let schedule1 = *vesting.get(schedule1_index).ok_or(Error::::ScheduleIndexOutOfBounds)?; let schedule2 = *vesting.get(schedule2_index).ok_or(Error::::ScheduleIndexOutOfBounds)?; - let filter = Filter::Two((schedule1_index, schedule2_index)); + let filter = Filter::Two(schedule1_index, schedule2_index); // The length of vesting decreases by 2 here since we filter out 2 schedules. Thus we know // below that we can safely insert the new merged schedule. @@ -489,7 +487,7 @@ pub mod pallet { )); Vesting::::insert(&who, vesting); } else if maybe_vesting.is_some() { - Vesting::::insert(&who, maybe_vesting.unwrap()); + Vesting::::insert(&who, maybe_vesting.expect("checked above; qed")); } else { Vesting::::remove(&who); } From a4296403180b1d06d8f12226207c6f297e51633e Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 29 Jun 2021 10:31:11 -0700 Subject: [PATCH 019/144] unwrap or default when getting user schedules --- frame/vesting/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 4f0056e8d26f6..e5fbcc41f0f36 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -686,7 +686,7 @@ where } let vesting_schedule = VestingInfo::try_new::(locked, per_block, starting_block)?; - let mut vesting = if let Some(v) = Self::vesting(who) { v } else { BoundedVec::default() }; + let mut vesting = Self::vesting(who).unwrap_or_default(); ensure!(vesting.try_push(vesting_schedule).is_ok(), Error::::AtMaxVestingSchedules); if let Some(v) = Self::update_lock_and_schedules(who.clone(), vesting, Filter::Zero) { From bc35a10adf73acd4ac5772a767458a3c30240ea6 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 29 Jun 2021 10:35:14 -0700 Subject: [PATCH 020/144] spaces be gone --- frame/vesting/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index e5fbcc41f0f36..3654e54696a6c 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -252,7 +252,7 @@ pub mod pallet { #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { - fn build(&self) { + fn build(&self) { use sp_runtime::traits::Saturating; // Generate initial vesting configuration From ce49bc8bb6a7c2cf16191d501617080f5f351f98 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 29 Jun 2021 10:37:01 -0700 Subject: [PATCH 021/144] ReadMe fixes --- frame/vesting/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frame/vesting/README.md b/frame/vesting/README.md index 3a05db523c3e7..a785a187010ff 100644 --- a/frame/vesting/README.md +++ b/frame/vesting/README.md @@ -15,8 +15,8 @@ equivalent to the amount remaining to be vested. This is done through a dispatch either `vest` (in typical case where the sender is calling on their own behalf) or `vest_other` in case the sender is calling on another account's behalf. -A user can have multiple vesting schedules, which are capped by an upper bound. With an upper bound ->= 2, users can merge 2 schedules. +A user can have multiple vesting schedules, which are capped by an upper bound. +With an upper bound >= 2, users can merge 2 schedules. ## Interface This module implements the `VestingSchedule` trait. @@ -31,4 +31,5 @@ This module implements the `VestingSchedule` trait. [`Call`]: ./enum.Call.html [`Config`]: ./trait.Config.html + License: Apache-2.0 From 0a234378fd1cd1c6128904fcbc2956ea146773d2 Mon Sep 17 00:00:00 2001 From: Zeke Mostov <32168567+emostov@users.noreply.github.com> Date: Tue, 29 Jun 2021 16:05:34 -0700 Subject: [PATCH 022/144] Update frame/vesting/src/lib.rs Co-authored-by: Peter Goodspeed-Niklaus --- frame/vesting/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 3654e54696a6c..50b0262208b51 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -510,7 +510,7 @@ impl Pallet { let now_as_balance = T::BlockNumberToBalance::convert(now); if schedule1_ending_block <= now_as_balance && schedule2_ending_block <= now_as_balance { - // If both schedules hav ended, we don't merge and exit early. + // If both schedules have ended, we don't merge and exit early. return None; } else if schedule1_ending_block <= now_as_balance { // If one schedule has ended, we treat the one that has not ended as the new From 9225929a64d18d1b5135d84c3f03b9f1657a95c3 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 29 Jun 2021 16:15:44 -0700 Subject: [PATCH 023/144] address some comments for docs --- frame/vesting/src/lib.rs | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 3654e54696a6c..7bf0d5a86335e 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -291,7 +291,7 @@ pub mod pallet { /// An \[account\] has become fully vested. No further vesting can happen. VestingCompleted(T::AccountId), /// 2 vesting schedules where successfully merged together. - ///\[locked, per_block, starting_block\] + /// \[locked, per_block, starting_block\] VestingMergeSuccess(BalanceOf, BalanceOf, T::BlockNumber), } @@ -422,8 +422,8 @@ pub mod pallet { } /// Merge two vesting schedules together, creating a new vesting schedule that unlocks over - /// highest possible start and end blocks. If both schedules have already started the current - /// block will be used as the schedule start; with the caveat that if one schedule is finishes by + /// highest possible start and end blocks. If both schedules have already started the current + /// block will be used as the schedule start; with the caveat that if one schedule is finishes by /// the current block, the other will be treated as the new merged schedule, unmodified. /// /// NOTE: If `schedule1_index == schedule2_index` this is a no-op. @@ -439,13 +439,6 @@ pub mod pallet { /// /// - `schedule1_index`: index of the first schedule to merge. /// - `schedule2_index`: index of the second schedule to merge. - /// - /// # - /// - `O(1)`. - /// - DbWeight: TODO Reads, TODO Writes - /// - Reads: TODO - /// - Writes: TODO - /// # #[pallet::weight( T::WeightInfo::not_unlocking_merge_schedules(MaxLocksOf::::get()) .max(T::WeightInfo::unlocking_merge_schedules(MaxLocksOf::::get())) @@ -509,15 +502,16 @@ impl Pallet { let schedule2_ending_block = schedule2.ending_block::(); let now_as_balance = T::BlockNumberToBalance::convert(now); - if schedule1_ending_block <= now_as_balance && schedule2_ending_block <= now_as_balance { - // If both schedules hav ended, we don't merge and exit early. - return None; - } else if schedule1_ending_block <= now_as_balance { + // Check if one or both schedules have ended. + match (schedule1_ending_block <= now_as_balance, schedule2_ending_block <= now_as_balance) { + // If both schedules have ended, we don't merge and exit early. + (true, true) => return None, // If one schedule has ended, we treat the one that has not ended as the new // merged schedule. - return Some(schedule2); - } else if schedule2_ending_block <= now_as_balance { - return Some(schedule1); + (true, false) => return Some(schedule2), + (false, true) => return Some(schedule1), + // If neither schedule has ended don't exit early. + _ => {}, } let locked = schedule1 From 7fe05617d33cceb4ea4e38ce034b1c98adfc2a28 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 29 Jun 2021 16:24:10 -0700 Subject: [PATCH 024/144] merge sched docs --- frame/vesting/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 7bf0d5a86335e..0e291d2e8d046 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -431,9 +431,9 @@ pub mod pallet { /// NOTE: If both schedules have ended by the current block, no new schedule will be created. /// /// Merged schedule attributes: - /// starting_block: `MAX(schedule1.starting_block, scheduled2.starting_block, current_block)`. - /// ending_block: `MAX(schedule1.ending_block, schedule2.ending_block)`. - /// locked: `schedule1.locked_at(current_block) + schedule2.locked_at(current_block)`. + /// - `starting_block`: `MAX(schedule1.starting_block, scheduled2.starting_block, current_block)`. + /// - `ending_block`: `MAX(schedule1.ending_block, schedule2.ending_block)`. + /// - `locked`: `schedule1.locked_at(current_block) + schedule2.locked_at(current_block)`. /// /// The dispatch origin for this call must be _Signed_. /// From 6c864c7143a3ffb761629f9f6c4b560c8eb9e6e9 Mon Sep 17 00:00:00 2001 From: Zeke Mostov <32168567+emostov@users.noreply.github.com> Date: Tue, 29 Jun 2021 16:27:56 -0700 Subject: [PATCH 025/144] Apply suggestions from code review Co-authored-by: Shawn Tabrizi --- bin/node/runtime/src/lib.rs | 2 +- frame/vesting/src/lib.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 12b8e946a03d0..5fa937ed9f116 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1023,7 +1023,7 @@ impl pallet_society::Config for Runtime { parameter_types! { pub const MinVestedTransfer: Balance = 100 * DOLLARS; - pub const MaxVestingSchedules: u32 = 3; + pub const MaxVestingSchedules: u32 = 20; } impl pallet_vesting::Config for Runtime { diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 0e291d2e8d046..70aa972b92354 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -428,7 +428,8 @@ pub mod pallet { /// /// NOTE: If `schedule1_index == schedule2_index` this is a no-op. /// NOTE: This will unlock all schedules through the current block prior to merging. - /// NOTE: If both schedules have ended by the current block, no new schedule will be created. + /// NOTE: If both schedules have ended by the current block, no new schedule will be created + /// and both will be removed. /// /// Merged schedule attributes: /// - `starting_block`: `MAX(schedule1.starting_block, scheduled2.starting_block, current_block)`. From 620535d68b21b3e24d93a48d2ddde38459717214 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 29 Jun 2021 16:46:14 -0700 Subject: [PATCH 026/144] log error when trying to push to vesting vec --- Cargo.lock | 1 + frame/vesting/Cargo.toml | 1 + frame/vesting/src/lib.rs | 11 +++++++++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ee78c31645b43..ab112e1d22d82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5713,6 +5713,7 @@ dependencies = [ "frame-support", "frame-system", "hex-literal", + "log", "pallet-balances", "parity-scale-codec", "sp-core", diff --git a/frame/vesting/Cargo.toml b/frame/vesting/Cargo.toml index 25890fea038de..da5dcd5945aaf 100644 --- a/frame/vesting/Cargo.toml +++ b/frame/vesting/Cargo.toml @@ -20,6 +20,7 @@ sp-runtime = { version = "3.0.0", default-features = false, path = "../../primit frame-support = { version = "3.0.0", default-features = false, path = "../support" } frame-system = { version = "3.0.0", default-features = false, path = "../system" } frame-benchmarking = { version = "3.1.0", default-features = false, path = "../benchmarking", optional = true } +log = { version = "0.4.0", default-features = false } [dev-dependencies] sp-io = { version = "3.0.0", path = "../../primitives/io" } diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 0e291d2e8d046..045eb8318909e 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -66,6 +66,7 @@ type BalanceOf = <::Currency as Currency< = <::Currency as LockableCurrency<::AccountId>>::MaxLocks; const VESTING_ID: LockIdentifier = *b"vesting "; +const LOG_TARGET: &'static str = "runtime::vesting"; // Module to enforce private fields on `VestingInfo` mod vesting_info { @@ -471,8 +472,14 @@ pub mod pallet { let now = >::block_number(); if let Some(s) = Self::merge_vesting_info(now, schedule1, schedule2) { let mut vesting = maybe_vesting.unwrap_or_default(); - // It shouldn't be possible for this to fail because we removed 2 schedules above. - ensure!(vesting.try_push(s).is_ok(), Error::::AtMaxVestingSchedules); + if let Err(_) = vesting.try_push(s) { + // It shouldn't be possible for this to fail because we removed 2 schedules above. + log::warn!( + target: LOG_TARGET, + "faulty logic led to attempting to add too many vesting schedules", + ); + return Err(Error::::AtMaxVestingSchedules.into()); + } Self::deposit_event(Event::::VestingMergeSuccess( s.locked(), s.per_block(), From afe25d22ed0f6901e8a4cf9d7645daa9cf039bac Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 29 Jun 2021 16:54:20 -0700 Subject: [PATCH 027/144] use let Some, not is_some --- frame/vesting/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index d6c8f47ed833a..7bf7bf3d634a6 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -487,8 +487,8 @@ pub mod pallet { s.starting_block(), )); Vesting::::insert(&who, vesting); - } else if maybe_vesting.is_some() { - Vesting::::insert(&who, maybe_vesting.expect("checked above; qed")); + } else if let Some(vesting) = maybe_vesting { + Vesting::::insert(&who, vesting); } else { Vesting::::remove(&who); } From 8abf51d9d73f91a8cb8b0f43e71b0eac99cca1ce Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 29 Jun 2021 17:02:45 -0700 Subject: [PATCH 028/144] remove_vesting_schedule u32, not optin --- frame/support/src/traits/tokens/currency/lockable.rs | 2 +- frame/vesting/src/lib.rs | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/frame/support/src/traits/tokens/currency/lockable.rs b/frame/support/src/traits/tokens/currency/lockable.rs index 9b331752357fd..d7d50f07edecd 100644 --- a/frame/support/src/traits/tokens/currency/lockable.rs +++ b/frame/support/src/traits/tokens/currency/lockable.rs @@ -103,5 +103,5 @@ pub trait VestingSchedule { /// support multiple schedules. /// /// NOTE: This doesn't alter the free balance of the account. - fn remove_vesting_schedule(who: &AccountId, schedule_index: Option) -> DispatchResult; + fn remove_vesting_schedule(who: &AccountId, schedule_index: u32) -> DispatchResult; } diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 7bf7bf3d634a6..a51cb0ef15dec 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -424,8 +424,8 @@ pub mod pallet { /// Merge two vesting schedules together, creating a new vesting schedule that unlocks over /// highest possible start and end blocks. If both schedules have already started the current - /// block will be used as the schedule start; with the caveat that if one schedule is finishes by - /// the current block, the other will be treated as the new merged schedule, unmodified. + /// block will be used as the schedule start; with the caveat that if one schedule is finished + /// by the current block, the other will be treated as the new merged schedule, unmodified. /// /// NOTE: If `schedule1_index == schedule2_index` this is a no-op. /// NOTE: This will unlock all schedules through the current block prior to merging. @@ -700,10 +700,8 @@ where } /// Remove a vesting schedule for a given account. Will error if `schedule_index` is `None`. - fn remove_vesting_schedule(who: &T::AccountId, schedule_index: Option) -> DispatchResult { - let schedule_index = schedule_index.ok_or(Error::::ScheduleIndexOutOfBounds)?; + fn remove_vesting_schedule(who: &T::AccountId, schedule_index: u32) -> DispatchResult { let filter = Filter::One(schedule_index as usize); - let vesting = Self::vesting(who).ok_or(Error::::NotVesting)?; if let Some(v) = Self::update_lock_and_schedules(who.clone(), vesting, filter) { Vesting::::insert(&who, v); From 23b35d4c15f3636702c4846759d21ba24b0f5919 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 29 Jun 2021 20:07:19 -0700 Subject: [PATCH 029/144] new not try_new, create validate builder; VestingInfo --- frame/vesting/src/lib.rs | 303 ++++++++++++++++++--------------------- 1 file changed, 141 insertions(+), 162 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index a51cb0ef15dec..36aaced09b117 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -85,31 +85,29 @@ mod vesting_info { impl VestingInfo { - /// Instantiate a new `VestingInfo` and validate parameters - pub fn try_new( + /// Instantiate a new `VestingInfo`. + pub fn new( locked: Balance, per_block: Balance, starting_block: BlockNumber, - ) -> Result, Error> { - Self::validate_params(locked, per_block, starting_block)?; - let per_block = if per_block > locked { locked } else { per_block }; - Ok(VestingInfo { locked, per_block, starting_block }) + ) -> VestingInfo { + VestingInfo { locked, per_block, starting_block } } - /// Validate parameters for `VestingInfo`. - pub fn validate_params( - locked: Balance, - per_block: Balance, - _starting_block: BlockNumber, - ) -> Result<(), Error> { - ensure!(!locked.is_zero() && !per_block.is_zero(), Error::::InvalidScheduleParams); - let min_transfer: u32 = T::MinVestedTransfer::get().try_into().unwrap_or(u32::MAX); - let min_transfer = Balance::from(min_transfer); - ensure!(locked >= min_transfer, Error::::AmountLow); - Ok(()) + /// Validate parameters for `VestingInfo`. Note that this does not check + /// against `MinVestedTransfer` or the current block. Additionally it + /// will correct `per_block` if it is greater than `locked`. + pub fn validate(mut self) -> Result> { + ensure!( + !self.locked.is_zero() && !self.per_block.is_zero(), + Error::::InvalidScheduleParams + ); + self.per_block = + if self.per_block > self.locked { self.locked } else { self.per_block }; + Ok(self) } - /// Instantiate a new `VestingInfo` without param validation. Useful for + /// Instantiate a new `VestingInfo` without param modification. Useful for /// mocking bad inputs in testing. #[cfg(test)] pub fn unsafe_new( @@ -268,7 +266,8 @@ pub mod pallet { let locked = balance.saturating_sub(liquid); let length_as_balance = T::BlockNumberToBalance::convert(length); let per_block = locked / length_as_balance.max(sp_runtime::traits::One::one()); - let vesting_info = VestingInfo::try_new::(locked, per_block, begin) + let vesting_info = VestingInfo::new::(locked, per_block, begin) + .validate::() .expect("Invalid VestingInfo params at genesis"); Vesting::::try_append(who, vesting_info) @@ -277,7 +276,7 @@ pub mod pallet { T::Currency::set_lock(VESTING_ID, who, locked, reasons); } } - } + } #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] @@ -549,7 +548,9 @@ impl Pallet { }; // At this point inputs have been validated, so this should always be `Some`. - VestingInfo::try_new::(locked, per_block, starting_block).ok() + let schedule = VestingInfo::new::(locked, per_block, starting_block); + debug_assert!(schedule.validate::().is_ok()); + Some(schedule) } // Execute a vested transfer from `source` to `target` with the given `schedule`. @@ -558,11 +559,8 @@ impl Pallet { target: ::Source, schedule: VestingInfo, T::BlockNumber>, ) -> DispatchResult { - VestingInfo::validate_params::( - schedule.locked(), - schedule.per_block(), - schedule.starting_block(), - )?; + ensure!(schedule.locked() > T::MinVestedTransfer::get(), Error::::AmountLow); + let schedule = schedule.validate::()?; let target = T::Lookup::lookup(target)?; let source = T::Lookup::lookup(source)?; @@ -669,7 +667,7 @@ where /// Adds a vesting schedule to a given account. /// - /// If there are already `MaxVestingSchedules`, an Error is returned and nothing + /// If the account has `MaxVestingSchedules`, an Error is returned and nothing /// is updated. /// /// On success, a linearly reducing amount of funds will be locked. In order to realise any @@ -677,6 +675,7 @@ where /// `vest_other`. /// /// Is a no-op if the amount to be vested is zero. + /// NOTE: it is assumed the function user has done necessary `VestingInfo` param validation. fn add_vesting_schedule( who: &T::AccountId, locked: BalanceOf, @@ -687,7 +686,7 @@ where return Ok(()); } - let vesting_schedule = VestingInfo::try_new::(locked, per_block, starting_block)?; + let vesting_schedule = VestingInfo::new::(locked, per_block, starting_block); let mut vesting = Self::vesting(who).unwrap_or_default(); ensure!(vesting.try_push(vesting_schedule).is_ok(), Error::::AtMaxVestingSchedules); @@ -869,24 +868,21 @@ mod tests { assert_eq!(user1_free_balance, 256 * 10); // Account 1 has free balance assert_eq!(user2_free_balance, 256 * 20); // Account 2 has free balance assert_eq!(user12_free_balance, 256 * 10); // Account 12 has free balance - let user1_vesting_schedule = VestingInfo::try_new::( + let user1_vesting_schedule = VestingInfo::new::( 256 * 5, 128, // Vesting over 10 blocks 0, - ) - .unwrap(); - let user2_vesting_schedule = VestingInfo::try_new::( + ); + let user2_vesting_schedule = VestingInfo::new::( 256 * 20, 256, // Vesting over 20 blocks 10, - ) - .unwrap(); - let user12_vesting_schedule = VestingInfo::try_new::( + ); + let user12_vesting_schedule = VestingInfo::new::( 256 * 5, 64, // Vesting over 20 blocks 10, - ) - .unwrap(); + ); assert_eq!(Vesting::vesting(&1).unwrap(), vec![user1_vesting_schedule]); // Account 1 has a vesting schedule assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); // Account 2 has a vesting schedule assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); // Account 12 has a vesting schedule @@ -921,28 +917,25 @@ mod tests { fn check_vesting_status_for_multi_schedule_account() { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { assert_eq!(System::block_number(), 1); - let sched0 = VestingInfo::try_new::( + let sched0 = VestingInfo::new::( ED * 20, ED, // Vesting over 20 blocks 10, - ) - .unwrap(); - - // Use 2 already has a vesting schedule. + ); + // Account 2 already has a vesting schedule. assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); - // User 2's free balance is from sched0 + // Account 2's free balance is from sched0 let free_balance = Balances::free_balance(&2); assert_eq!(free_balance, ED * (20)); assert_eq!(Vesting::vesting_balance(&2), Some(free_balance)); // Add a 2nd schedule that is already unlocking by block #1 - let sched1 = VestingInfo::try_new::( + let sched1 = VestingInfo::new::( ED * 10, ED, // Vesting over 10 blocks 0, - ) - .unwrap(); + ); assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched1)); // Free balance is equal to the two existing schedules total amount. let free_balance = Balances::free_balance(&2); @@ -953,12 +946,11 @@ mod tests { assert_eq!(Vesting::vesting_balance(&2), Some(free_balance - sched1.per_block())); // Add a 3rd schedule - let sched2 = VestingInfo::try_new::( + let sched2 = VestingInfo::new::( ED * 30, ED, // Vesting over 30 blocks 5, - ) - .unwrap(); + ); assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched2)); System::set_block_number(9); @@ -991,11 +983,15 @@ mod tests { ) ); - // At block #35 sched2 fully unlocks and thus all schedules funds are unlocked. - System::set_block_number(35); - assert_eq!(System::block_number(), 35); - assert_eq!(Vesting::vesting_balance(&2), Some(0)); - }); + // At block #35 sched2 fully unlocks and thus all schedules funds are unlocked. + System::set_block_number(35); + assert_eq!(System::block_number(), 35); + assert_eq!(Vesting::vesting_balance(&2), Some(0)); + // Since we have not called any extrinsics that would unlock funds the schedules + // are still in storage. + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1, sched2]); + assert_ok!(Vesting::vest(Some(2).into())); + }); } #[test] @@ -1085,11 +1081,11 @@ mod tests { assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); // Account 12 has delayed vesting - let user12_vesting_schedule = VestingInfo::try_new::( + let user12_vesting_schedule = VestingInfo::new::( 256 * 5, 64, // Vesting over 20 blocks 10, - ).unwrap(); + ); assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); // Account 12 can still send liquid funds @@ -1110,11 +1106,11 @@ mod tests { // Account 4 should not have any vesting yet. assert_eq!(Vesting::vesting(&4), None); // Make the schedule for the new transfer. - let new_vesting_schedule = VestingInfo::try_new::( + let new_vesting_schedule = VestingInfo::new::( 256 * 5, 64, // Vesting over 20 blocks 10, - ).unwrap(); + ); assert_ok!(Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule)); // Now account 4 should have vesting. assert_eq!(Vesting::vesting(&4).unwrap(), vec![new_vesting_schedule]); @@ -1152,11 +1148,11 @@ mod tests { assert_eq!(user4_free_balance, 256 * 40); // Account 2 should already have a vesting schedule. - let user2_vesting_schedule = VestingInfo::try_new::( + let user2_vesting_schedule = VestingInfo::new::( 256 * 20, 256, // Vesting over 20 blocks 10, - ).unwrap(); + ); assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); // Fails due to too low transfer amount. @@ -1189,7 +1185,7 @@ mod tests { ); assert_noop!( Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), - Error::::InvalidScheduleParams, + Error::::AmountLow, ); // Free balance has not changed. @@ -1205,11 +1201,10 @@ mod tests { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { let mut user_4_free_balance = Balances::free_balance(&4); let max_schedules = ::MaxVestingSchedules::get(); - let sched = VestingInfo::try_new::( + let sched = VestingInfo::new::( ED, 1, // Vest over 256 blocks. 10, - ) - .unwrap(); + ); // Add max amount schedules to user 4. for _ in 0 .. max_schedules { assert_ok!(Vesting::vested_transfer(Some(3).into(), 4, sched)); @@ -1245,12 +1240,11 @@ mod tests { // Account 4 should not have any vesting yet. assert_eq!(Vesting::vesting(&4), None); // Make the schedule for the new transfer. - let new_vesting_schedule = VestingInfo::try_new::( + let new_vesting_schedule = VestingInfo::new::( 256 * 5, 64, // Vesting over 20 blocks 10, - ) - .unwrap(); + ); assert_noop!( Vesting::force_vested_transfer(Some(4).into(), 3, 4, new_vesting_schedule), @@ -1295,17 +1289,16 @@ mod tests { assert_eq!(user2_free_balance, 256 * 20); assert_eq!(user4_free_balance, 256 * 40); // Account 2 should already have a vesting schedule. - let user2_vesting_schedule = VestingInfo::try_new::( + let user2_vesting_schedule = VestingInfo::new::( 256 * 20, 256, // Vesting over 20 blocks 10, - ) - .unwrap(); + ); assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); // Too low transfer amount. let new_vesting_schedule_too_low = - VestingInfo::unsafe_new(::MinVestedTransfer::get() - 1, 64, 10); + VestingInfo::new::(::MinVestedTransfer::get() - 1, 64, 10); assert_noop!( Vesting::force_vested_transfer( RawOrigin::Root.into(), @@ -1316,18 +1309,18 @@ mod tests { Error::::AmountLow, ); - // `per_block` is 0 - let schedule_per_block_0 = VestingInfo::unsafe_new(256, 0, 10); + // `per_block` is 0. + let schedule_per_block_0 = VestingInfo::new::(256, 0, 10); assert_noop!( Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_per_block_0), Error::::InvalidScheduleParams, ); - // `locked` is 0 - let schedule_locked_0 = VestingInfo::unsafe_new(0, 1, 10); + // `locked` is 0. + let schedule_locked_0 = VestingInfo::new::(0, 1, 10); assert_noop!( Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), - Error::::InvalidScheduleParams, + Error::::AmountLow, ); // Verify no currency transfer happened. @@ -1341,11 +1334,10 @@ mod tests { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { let mut user_4_free_balance = Balances::free_balance(&4); let max_schedules = ::MaxVestingSchedules::get(); - let sched = VestingInfo::try_new::( + let sched = VestingInfo::new::( ED, 1, // Vest over 256 blocks. 10, - ) - .unwrap(); + ); // Add max amount schedules to user 4. for _ in 0 .. max_schedules { assert_ok!(Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, sched)); @@ -1375,12 +1367,11 @@ mod tests { fn merge_schedules_that_have_not_started() { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { // Account 2 should already have a vesting schedule. - let sched0 = VestingInfo::try_new::( + let sched0 = VestingInfo::new::( ED * 20, ED, // Vest over 20 blocks. 10, - ) - .unwrap(); + ); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); assert_eq!(Balances::usable_balance(&2), 0); @@ -1392,12 +1383,11 @@ mod tests { // Since we merged identical schedules, the new schedule finishes at the same // time as the original, just with double the amount - let sched1 = VestingInfo::try_new::( + let sched1 = VestingInfo::new::( sched0.locked() * 2, sched0.per_block() * 2, 10, // starts at the block the schedules are merged - ) - .unwrap(); + ); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched1]); assert_eq!(Balances::usable_balance(&2), 0); @@ -1409,20 +1399,18 @@ mod tests { // Merging two schedules that have started will vest both before merging ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { // Account 2 should already have a vesting schedule. - let sched0 = VestingInfo::try_new::( + let sched0 = VestingInfo::new::( ED * 20, ED, // Vest over 20 blocks 10, - ) - .unwrap(); + ); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); - let sched1 = VestingInfo::try_new::( + let sched1 = VestingInfo::new::( 256 * 10, 256, // Vest over 10 blocks sched0.starting_block() + 5, // Start at block 15 - ) - .unwrap(); + ); Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); @@ -1453,8 +1441,7 @@ mod tests { // amount to unlock per block. let sched2_per_block = sched2_locked / sched2_duration; - let sched2 = - VestingInfo::try_new::(sched2_locked, sched2_per_block, cur_block).unwrap(); + let sched2 = VestingInfo::new::(sched2_locked, sched2_per_block, cur_block); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2]); }); } @@ -1464,24 +1451,21 @@ mod tests { // Schedules being merged are filtered out, schedules to the right of any merged // schedule shift left and the new, merged schedule is always last. ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - let sched0 = VestingInfo::try_new::( + let sched0 = VestingInfo::new::( ED * 10, ED, // Vesting over 10 blocks 10, - ) - .unwrap(); - let sched1 = VestingInfo::try_new::( + ); + let sched1 = VestingInfo::new::( ED * 11, ED, // Vesting over 11 blocks 11, - ) - .unwrap(); - let sched2 = VestingInfo::try_new::( + ); + let sched2 = VestingInfo::new::( ED * 12, ED, // Vesting over 12 blocks 12, - ) - .unwrap(); + ); // Account 3 start out with no schedules assert_eq!(Vesting::vesting(&3), None); @@ -1502,7 +1486,7 @@ mod tests { // and the usable balance has not changed. assert_eq!(usable_balance, Balances::usable_balance(&3)); - Vesting::merge_schedules(Some(3).into(), 0, 2).unwrap(); + assert_ok!(Vesting::merge_schedules(Some(3).into(), 0, 2)); // Create the merged schedule of sched0 & sched2. // The merged schedule will have the max possible starting block, @@ -1515,9 +1499,7 @@ mod tests { sched2.ending_block::().max(sched0.ending_block::()); let sched3_duration = sched3_end - sched3_start; let sched3_per_block = sched3_locked / sched3_duration; - let sched3 = - VestingInfo::try_new::(sched3_locked, sched3_per_block, sched3_start) - .unwrap(); + let sched3 = VestingInfo::new::(sched3_locked, sched3_per_block, sched3_start); // The not touched schedule moves left and the new merged schedule is appended. assert_eq!(Vesting::vesting(&3).unwrap(), vec![sched1, sched3]); @@ -1532,12 +1514,11 @@ mod tests { // started. ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { // Account 2 should already have a vesting schedule. - let sched0 = VestingInfo::try_new::( + let sched0 = VestingInfo::new::( ED * 20, ED, // Vesting over 20 blocks 10, - ) - .unwrap(); + ); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); // Fast forward to half way through the life of sched_1 @@ -1561,12 +1542,11 @@ mod tests { System::set_block_number(cur_block); // And add a schedule that starts after this block, but before sched0 finishes. - let sched1 = VestingInfo::try_new::( + let sched1 = VestingInfo::new::( ED * 10, 1, // Vesting over 256 * 10 (2560) blocks cur_block + 1, - ) - .unwrap(); + ); Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); // Merge the schedules before sched1 starts. @@ -1588,9 +1568,7 @@ mod tests { let sched2_duration = sched2_end - sched2_start; let sched2_per_block = sched2_locked / sched2_duration; - let sched2 = - VestingInfo::try_new::(sched2_locked, sched2_per_block, sched2_start) - .unwrap(); + let sched2 = VestingInfo::new::(sched2_locked, sched2_per_block, sched2_start); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2]); }); } @@ -1601,30 +1579,27 @@ mod tests { // without any alterations, as the merged one. ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { // Account 2 should already have a vesting schedule. - let sched0 = VestingInfo::try_new::( + let sched0 = VestingInfo::new::( ED * 20, - ED, // Duration of 20. + ED, // Vesting over 20 blocks. 10, - ) - .unwrap(); + ); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); - let sched1 = VestingInfo::try_new::( + let sched1 = VestingInfo::new::( ED * 40, - ED, // Duration of 40. + ED, // Vesting over 40 blocks. 10, - ) - .unwrap(); + ); assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched1)); // Transfer a 3rd schedule, so we can demonstrate how schedule indices change. // (We are not merging this schedule.) - let sched2 = VestingInfo::try_new::( + let sched2 = VestingInfo::new::( ED * 30, - ED, // Duration of 30. + ED, // Vesting over 30 blocks. 10, - ) - .unwrap(); + ); assert_ok!(Vesting::vested_transfer(Some(3).into(), 2, sched2)); // The schedules are in expected order prior to merging. @@ -1638,7 +1613,7 @@ mod tests { // Prior to merge_schedules and with no vest/vest_other called the user has no usable // balance. assert_eq!(Balances::usable_balance(&2), 0); - Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + assert_ok!(Vesting::merge_schedules(Some(2).into(), 0, 1)); // sched2 is now the first, since sched0 & sched1 get filtered out while "merging". // sched1 gets treated like the new merged schedule by getting pushed onto back @@ -1666,21 +1641,19 @@ mod tests { // If both schedules finish by the current block we don't create new one ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { // Account 2 should already have a vesting schedule. - let sched0 = VestingInfo::try_new::( + let sched0 = VestingInfo::new::( ED * 20, ED, // 20 block duration. 10, - ) - .unwrap(); + ); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); // Create sched1 and transfer it to account 2. - let sched1 = VestingInfo::try_new::( + let sched1 = VestingInfo::new::( ED * 30, ED, // 30 block duration. 10, - ) - .unwrap(); + ); assert_ok!(Vesting::vested_transfer(Some(3).into(), 2, sched1)); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); @@ -1708,12 +1681,11 @@ mod tests { fn merge_schedules_throws_proper_errors() { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { // Account 2 should already have a vesting schedule. - let sched0 = VestingInfo::try_new::( + let sched0 = VestingInfo::new::( ED * 20, ED, // 20 block duration. 10, - ) - .unwrap(); + ); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); // There is only 1 vesting schedule. @@ -1760,16 +1732,16 @@ mod tests { .vesting_genesis_config(vesting_config) .build() .execute_with(|| { - let user1_sched1 = VestingInfo::try_new::(5 * ED, 128, 0u64).unwrap(); + let user1_sched1 = VestingInfo::new::(5 * ED, 128, 0u64); assert_eq!(Vesting::vesting(&1).unwrap(), vec![user1_sched1]); - let user2_sched1 = VestingInfo::try_new::(1 * ED, 12, 10u64).unwrap(); - let user2_sched2 = VestingInfo::try_new::(2 * ED, 25, 10u64).unwrap(); + let user2_sched1 = VestingInfo::new::(1 * ED, 12, 10u64); + let user2_sched2 = VestingInfo::new::(2 * ED, 25, 10u64); assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_sched1, user2_sched2]); - let user12_sched1 = VestingInfo::try_new::(1 * ED, 12, 10u64).unwrap(); - let user12_sched2 = VestingInfo::try_new::(2 * ED, 25, 10u64).unwrap(); - let user12_sched3 = VestingInfo::try_new::(3 * ED, 38, 10u64).unwrap(); + let user12_sched1 = VestingInfo::new::(1 * ED, 12, 10u64); + let user12_sched2 = VestingInfo::new::(2 * ED, 25, 10u64); + let user12_sched3 = VestingInfo::new::(3 * ED, 38, 10u64); assert_eq!( Vesting::vesting(&12).unwrap(), vec![user12_sched1, user12_sched2, user12_sched3] @@ -1795,18 +1767,25 @@ mod tests { // Faulty schedules with an infinite duration (per_block == 0) can be merged to create // a schedule that vest 1 per_block, (helpful for faulty, legacy schedules). ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - let sched0 = VestingInfo::unsafe_new(ED, 0, 1); - let sched1 = VestingInfo::unsafe_new(ED * 2, 0, 10); + let sched0 = VestingInfo::new::( + ED, + 0, // Vesting over infinite blocks. + 1 + ); + let sched1 = VestingInfo::new::( + ED * 2, + 0, // Vesting over infinite blocks. + 10 + ); let cur_block = 5; let expected_locked = sched0.locked_at::(cur_block) + sched1.locked_at::(cur_block); - let expected_sched = VestingInfo::try_new::( + let expected_sched = VestingInfo::new::( expected_locked, 1, // per_block of 1, which corrects existing schedules. 10, - ) - .unwrap(); + ); assert_eq!( Vesting::merge_vesting_info(cur_block, sched0, sched1), Some(expected_sched), @@ -1817,62 +1796,62 @@ mod tests { #[test] fn vesting_info_validation_works() { let min_transfer = ::MinVestedTransfer::get(); - // `locked` cannot be less than min_transfer (non 0 case). - match VestingInfo::try_new::(min_transfer - 1, 1u64, 10u64) { - Err(Error::::AmountLow) => (), + // Does not check for min transfer. + match VestingInfo::new::(min_transfer - 1, 1u64, 10u64).validate::() { + Ok(_) => (), _ => panic!(), } // `locked` cannot be 0. - match VestingInfo::try_new::(0, 1u64, 10u64) { + match VestingInfo::new::(0, 1u64, 10u64).validate::() { Err(Error::::InvalidScheduleParams) => (), _ => panic!(), } // `per_block` cannot be 0. - match VestingInfo::try_new::(min_transfer + 1, 0u64, 10u64) { + match VestingInfo::new::(min_transfer + 1, 0u64, 10u64).validate() { Err(Error::::InvalidScheduleParams) => (), _ => panic!(), } // With valid inputs it does not error and does not modify the inputs. assert_eq!( - VestingInfo::try_new::(min_transfer, 1u64, 10u64).unwrap(), - VestingInfo::unsafe_new(min_transfer, 1u64, 10u64) + VestingInfo::new::(min_transfer, 1u64, 10u64).validate::().unwrap(), + VestingInfo::new::(min_transfer, 1u64, 10u64) ); // `per_block` is never bigger than `locked`. assert_eq!( - VestingInfo::try_new::(256u64, 256 * 2u64, 10u64).unwrap(), - VestingInfo::try_new::(256u64, 256u64, 10u64).unwrap() + VestingInfo::new::(256u64, 256 * 2u64, 10u64).validate::().unwrap(), + VestingInfo::new::(256u64, 256u64, 10u64) ); } #[test] fn vesting_info_ending_block_works() { // Treats `per_block` 0 as a `per_block` of 1 to accommodate faulty, legacy schedules. - let per_block_0 = VestingInfo::unsafe_new(256u32, 0u32, 10u32); + let per_block_0 = VestingInfo::new::(256u32, 0u32, 10u32); assert_eq!( per_block_0.ending_block::(), per_block_0.locked() + per_block_0.starting_block() ); - let per_block_1 = VestingInfo::try_new::(256u32, 1u32, 10u32).unwrap(); + let per_block_1 = VestingInfo::new::(256u32, 1u32, 10u32); assert_eq!(per_block_0.ending_block::(), per_block_1.ending_block::()); // `per_block >= locked` always results in a schedule ending the block after it starts - let per_block_gt_locked = VestingInfo::unsafe_new(256u32, 256 * 2u32, 10u32); + let per_block_gt_locked = VestingInfo::new::(256u32, 256 * 2u32, 10u32); assert_eq!( per_block_gt_locked.ending_block::(), 1 + per_block_gt_locked.starting_block() ); - let per_block_eq_locked = VestingInfo::try_new::(256u32, 256u32, 10u32).unwrap(); + let per_block_eq_locked = VestingInfo::new::(256u32, 256u32, 10u32); assert_eq!( per_block_gt_locked.ending_block::(), per_block_eq_locked.ending_block::() ); // Correctly calcs end if `locked % per_block != 0`. (We need a block to unlock the remainder). - let imperfect_per_block = VestingInfo::try_new::(256u32, 250u32, 10u32).unwrap(); + let imperfect_per_block = VestingInfo::new::(256u32, 250u32, 10u32); assert_eq!( imperfect_per_block.ending_block::(), imperfect_per_block.starting_block() + 2u32, From cb5121dc53e7583f3463cc65e772fddffea3d2e5 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Wed, 30 Jun 2021 10:04:58 -0700 Subject: [PATCH 030/144] Merge prep: break out tests and mock --- frame/vesting/src/benchmarking.rs | 4 +- frame/vesting/src/lib.rs | 1193 +---------------------------- frame/vesting/src/mock.rs | 154 ++++ frame/vesting/src/tests.rs | 984 ++++++++++++++++++++++++ 4 files changed, 1167 insertions(+), 1168 deletions(-) create mode 100644 frame/vesting/src/mock.rs create mode 100644 frame/vesting/src/tests.rs diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index ce486d1e3767b..db786b87b1966 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -423,6 +423,6 @@ benchmarks! { impl_benchmark_test_suite!( Vesting, - crate::tests::ExtBuilder::default().existential_deposit(256).build(), - crate::tests::Test, + crate::mock::ExtBuilder::default().existential_deposit(256).build(), + crate::mock::Test, ); diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 36aaced09b117..3e31d7ecc4f78 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -45,25 +45,39 @@ #![cfg_attr(not(feature = "std"), no_std)] mod benchmarking; +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + pub mod weights; -use sp_std::{prelude::*, fmt::Debug, convert::TryInto}; -use codec::{Encode, Decode}; -use sp_runtime::{RuntimeDebug, traits::{ - StaticLookup, Zero, AtLeast32BitUnsigned, MaybeSerializeDeserialize, Convert, Saturating, CheckedDiv -}}; -use frame_support::{ensure, pallet_prelude::*}; -use frame_support::traits::{ - Currency, LockableCurrency, VestingSchedule, WithdrawReasons, LockIdentifier, - ExistenceRequirement, Get, +use codec::{Decode, Encode}; +use frame_support::{ + ensure, + pallet_prelude::*, + traits::{ + Currency, ExistenceRequirement, Get, LockIdentifier, LockableCurrency, VestingSchedule, + WithdrawReasons, + }, }; -use frame_system::{ensure_signed, ensure_root, pallet_prelude::*}; -pub use weights::WeightInfo; +use frame_system::{ensure_root, ensure_signed, pallet_prelude::*}; pub use pallet::*; +use sp_runtime::{ + traits::{ + AtLeast32BitUnsigned, CheckedDiv, Convert, MaybeSerializeDeserialize, Saturating, + StaticLookup, Zero, + }, + RuntimeDebug, +}; +use sp_std::{convert::TryInto, fmt::Debug, prelude::*}; pub use vesting_info::*; +pub use weights::WeightInfo; -type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; -type MaxLocksOf = <::Currency as LockableCurrency<::AccountId>>::MaxLocks; +type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; +type MaxLocksOf = + <::Currency as LockableCurrency<::AccountId>>::MaxLocks; const VESTING_ID: LockIdentifier = *b"vesting "; const LOG_TARGET: &'static str = "runtime::vesting"; @@ -710,1156 +724,3 @@ where Ok(()) } } - -#[cfg(test)] -mod tests { - use frame_support::{assert_noop, assert_ok, assert_storage_noop, parameter_types}; - use frame_system::RawOrigin; - use sp_core::H256; - use sp_runtime::{ - testing::Header, - traits::{BadOrigin, BlakeTwo256, Identity, IdentityLookup}, - }; - - use super::*; - use crate as pallet_vesting; - - type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; - type Block = frame_system::mocking::MockBlock; - - frame_support::construct_runtime!( - pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - Vesting: pallet_vesting::{Pallet, Call, Storage, Event, Config}, - } - ); - - parameter_types! { - pub const BlockHashCount: u64 = 250; - pub BlockWeights: frame_system::limits::BlockWeights = - frame_system::limits::BlockWeights::simple_max(1024); - } - impl frame_system::Config for Test { - type BaseCallFilter = (); - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type Origin = Origin; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Call = Call; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type Event = Event; - type BlockHashCount = BlockHashCount; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - } - parameter_types! { - pub const MaxLocks: u32 = 10; - } - impl pallet_balances::Config for Test { - type Balance = u64; - type DustRemoval = (); - type Event = Event; - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - type MaxLocks = MaxLocks; - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type WeightInfo = (); - } - parameter_types! { - pub const MinVestedTransfer: u64 = 10; - pub static ExistentialDeposit: u64 = 0; - pub const MaxVestingSchedules: u32 = 3; - } - impl Config for Test { - type Event = Event; - type Currency = Balances; - type BlockNumberToBalance = Identity; - type MinVestedTransfer = MinVestedTransfer; - type WeightInfo = (); - type MaxVestingSchedules = MaxVestingSchedules; - } - - pub struct ExtBuilder { - existential_deposit: u64, - vesting_genesis_config: Option>, - } - - impl Default for ExtBuilder { - fn default() -> Self { - Self { - existential_deposit: 1, - vesting_genesis_config: None, - } - } - } - impl ExtBuilder { - pub fn existential_deposit(mut self, existential_deposit: u64) -> Self { - self.existential_deposit = existential_deposit; - self - } - - pub fn vesting_genesis_config(mut self, config: Vec<(u64, u64, u64, u64)>) -> Self { - self.vesting_genesis_config = Some(config); - self - } - - pub fn build(self) -> sp_io::TestExternalities { - EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - pallet_balances::GenesisConfig:: { - balances: vec![ - (1, 10 * self.existential_deposit), - (2, 20 * self.existential_deposit), - (3, 30 * self.existential_deposit), - (4, 40 * self.existential_deposit), - (12, 10 * self.existential_deposit) - ], - }.assimilate_storage(&mut t).unwrap(); - - let vesting = if let Some(vesting_config) = self.vesting_genesis_config { - vesting_config - } else { - vec![ - (1, 0, 10, 5 * self.existential_deposit), - (2, 10, 20, 0), - (12, 10, 20, 5 * self.existential_deposit) - ] - }; - - pallet_vesting::GenesisConfig:: { - vesting - }.assimilate_storage(&mut t).unwrap(); - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext - } - } - - /// A default existential deposit. - const ED: u64 = 256; - - #[test] - fn check_vesting_status() { - ExtBuilder::default() - .existential_deposit(256) - .build() - .execute_with(|| { - let user1_free_balance = Balances::free_balance(&1); - let user2_free_balance = Balances::free_balance(&2); - let user12_free_balance = Balances::free_balance(&12); - assert_eq!(user1_free_balance, 256 * 10); // Account 1 has free balance - assert_eq!(user2_free_balance, 256 * 20); // Account 2 has free balance - assert_eq!(user12_free_balance, 256 * 10); // Account 12 has free balance - let user1_vesting_schedule = VestingInfo::new::( - 256 * 5, - 128, // Vesting over 10 blocks - 0, - ); - let user2_vesting_schedule = VestingInfo::new::( - 256 * 20, - 256, // Vesting over 20 blocks - 10, - ); - let user12_vesting_schedule = VestingInfo::new::( - 256 * 5, - 64, // Vesting over 20 blocks - 10, - ); - assert_eq!(Vesting::vesting(&1).unwrap(), vec![user1_vesting_schedule]); // Account 1 has a vesting schedule - assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); // Account 2 has a vesting schedule - assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); // Account 12 has a vesting schedule - - // Account 1 has only 128 units vested from their illiquid 256 * 5 units at block 1 - assert_eq!(Vesting::vesting_balance(&1), Some(128 * 9)); - // Account 2 has their full balance locked - assert_eq!(Vesting::vesting_balance(&2), Some(user2_free_balance)); - // Account 12 has only their illiquid funds locked - assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); - - System::set_block_number(10); - assert_eq!(System::block_number(), 10); - - // Account 1 has fully vested by block 10 - assert_eq!(Vesting::vesting_balance(&1), Some(0)); - // Account 2 has started vesting by block 10 - assert_eq!(Vesting::vesting_balance(&2), Some(user2_free_balance)); - // Account 12 has started vesting by block 10 - assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); - - System::set_block_number(30); - assert_eq!(System::block_number(), 30); - - assert_eq!(Vesting::vesting_balance(&1), Some(0)); // Account 1 is still fully vested, and not negative - assert_eq!(Vesting::vesting_balance(&2), Some(0)); // Account 2 has fully vested by block 30 - assert_eq!(Vesting::vesting_balance(&12), Some(0)); // Account 2 has fully vested by block 30 - }); - } - - #[test] - fn check_vesting_status_for_multi_schedule_account() { - ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - assert_eq!(System::block_number(), 1); - let sched0 = VestingInfo::new::( - ED * 20, - ED, // Vesting over 20 blocks - 10, - ); - // Account 2 already has a vesting schedule. - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); - - // Account 2's free balance is from sched0 - let free_balance = Balances::free_balance(&2); - assert_eq!(free_balance, ED * (20)); - assert_eq!(Vesting::vesting_balance(&2), Some(free_balance)); - - // Add a 2nd schedule that is already unlocking by block #1 - let sched1 = VestingInfo::new::( - ED * 10, - ED, // Vesting over 10 blocks - 0, - ); - assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched1)); - // Free balance is equal to the two existing schedules total amount. - let free_balance = Balances::free_balance(&2); - assert_eq!(free_balance, ED * (10 + 20)); - // The most recently added schedule exists. - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); - // sched1 has free funds at block #1, but nothing else. - assert_eq!(Vesting::vesting_balance(&2), Some(free_balance - sched1.per_block())); - - // Add a 3rd schedule - let sched2 = VestingInfo::new::( - ED * 30, - ED, // Vesting over 30 blocks - 5, - ); - assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched2)); - - System::set_block_number(9); - // Free balance is equal to the 3 existing schedules total amount. - let free_balance = Balances::free_balance(&2); - assert_eq!(free_balance, ED * (10 + 20 + 30)); - // sched1 and sched2 are freeing funds at block #9. - assert_eq!( - Vesting::vesting_balance(&2), - Some(free_balance - sched1.per_block() * 9 - sched2.per_block() * 4) - ); - - System::set_block_number(20); - // At block #20 sched1 is fully unlocked while sched2 and sched0 are partially unlocked. - assert_eq!( - Vesting::vesting_balance(&2), - Some( - free_balance - - sched1.locked() - sched2.per_block() * 15 - - sched0.per_block() * 10 - ) - ); - - System::set_block_number(30); - // At block #30 sched0 and sched1 are fully unlocked while sched2 is partially unlocked. - assert_eq!( - Vesting::vesting_balance(&2), - Some( - free_balance - sched1.locked() - sched2.per_block() * 25 - sched0.locked() - ) - ); - - // At block #35 sched2 fully unlocks and thus all schedules funds are unlocked. - System::set_block_number(35); - assert_eq!(System::block_number(), 35); - assert_eq!(Vesting::vesting_balance(&2), Some(0)); - // Since we have not called any extrinsics that would unlock funds the schedules - // are still in storage. - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1, sched2]); - assert_ok!(Vesting::vest(Some(2).into())); - }); - } - - #[test] - fn unvested_balance_should_not_transfer() { - ExtBuilder::default() - .existential_deposit(10) - .build() - .execute_with(|| { - let user1_free_balance = Balances::free_balance(&1); - assert_eq!(user1_free_balance, 100); // Account 1 has free balance - // Account 1 has only 5 units vested at block 1 (plus 50 unvested) - assert_eq!(Vesting::vesting_balance(&1), Some(45)); - assert_noop!( - Balances::transfer(Some(1).into(), 2, 56), - pallet_balances::Error::::LiquidityRestrictions, - ); // Account 1 cannot send more than vested amount - }); - } - - #[test] - fn vested_balance_should_transfer() { - ExtBuilder::default() - .existential_deposit(10) - .build() - .execute_with(|| { - let user1_free_balance = Balances::free_balance(&1); - assert_eq!(user1_free_balance, 100); // Account 1 has free balance - // Account 1 has only 5 units vested at block 1 (plus 50 unvested) - assert_eq!(Vesting::vesting_balance(&1), Some(45)); - assert_ok!(Vesting::vest(Some(1).into())); - assert_ok!(Balances::transfer(Some(1).into(), 2, 55)); - }); - } - - #[test] - fn vested_balance_should_transfer_using_vest_other() { - ExtBuilder::default() - .existential_deposit(10) - .build() - .execute_with(|| { - let user1_free_balance = Balances::free_balance(&1); - assert_eq!(user1_free_balance, 100); // Account 1 has free balance - // Account 1 has only 5 units vested at block 1 (plus 50 unvested) - assert_eq!(Vesting::vesting_balance(&1), Some(45)); - assert_ok!(Vesting::vest_other(Some(2).into(), 1)); - assert_ok!(Balances::transfer(Some(1).into(), 2, 55)); - }); - } - - #[test] - fn extra_balance_should_transfer() { - ExtBuilder::default() - .existential_deposit(10) - .build() - .execute_with(|| { - assert_ok!(Balances::transfer(Some(3).into(), 1, 100)); - assert_ok!(Balances::transfer(Some(3).into(), 2, 100)); - - let user1_free_balance = Balances::free_balance(&1); - assert_eq!(user1_free_balance, 200); // Account 1 has 100 more free balance than normal - - let user2_free_balance = Balances::free_balance(&2); - assert_eq!(user2_free_balance, 300); // Account 2 has 100 more free balance than normal - - // Account 1 has only 5 units vested at block 1 (plus 150 unvested) - assert_eq!(Vesting::vesting_balance(&1), Some(45)); - assert_ok!(Vesting::vest(Some(1).into())); - assert_ok!(Balances::transfer(Some(1).into(), 3, 155)); // Account 1 can send extra units gained - - // Account 2 has no units vested at block 1, but gained 100 - assert_eq!(Vesting::vesting_balance(&2), Some(200)); - assert_ok!(Vesting::vest(Some(2).into())); - assert_ok!(Balances::transfer(Some(2).into(), 3, 100)); // Account 2 can send extra units gained - }); - } - - #[test] - fn liquid_funds_should_transfer_with_delayed_vesting() { - ExtBuilder::default() - .existential_deposit(256) - .build() - .execute_with(|| { - let user12_free_balance = Balances::free_balance(&12); - - assert_eq!(user12_free_balance, 2560); // Account 12 has free balance - // Account 12 has liquid funds - assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); - - // Account 12 has delayed vesting - let user12_vesting_schedule = VestingInfo::new::( - 256 * 5, - 64, // Vesting over 20 blocks - 10, - ); - assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); - - // Account 12 can still send liquid funds - assert_ok!(Balances::transfer(Some(12).into(), 3, 256 * 5)); - }); - } - - #[test] - fn vested_transfer_works() { - ExtBuilder::default() - .existential_deposit(256) - .build() - .execute_with(|| { - let user3_free_balance = Balances::free_balance(&3); - let user4_free_balance = Balances::free_balance(&4); - assert_eq!(user3_free_balance, 256 * 30); - assert_eq!(user4_free_balance, 256 * 40); - // Account 4 should not have any vesting yet. - assert_eq!(Vesting::vesting(&4), None); - // Make the schedule for the new transfer. - let new_vesting_schedule = VestingInfo::new::( - 256 * 5, - 64, // Vesting over 20 blocks - 10, - ); - assert_ok!(Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule)); - // Now account 4 should have vesting. - assert_eq!(Vesting::vesting(&4).unwrap(), vec![new_vesting_schedule]); - // Ensure the transfer happened correctly. - let user3_free_balance_updated = Balances::free_balance(&3); - assert_eq!(user3_free_balance_updated, 256 * 25); - let user4_free_balance_updated = Balances::free_balance(&4); - assert_eq!(user4_free_balance_updated, 256 * 45); - // Account 4 has 5 * 256 locked. - assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5)); - - System::set_block_number(20); - assert_eq!(System::block_number(), 20); - - // Account 4 has 5 * 64 units vested by block 20. - assert_eq!(Vesting::vesting_balance(&4), Some(10 * 64)); - - System::set_block_number(30); - assert_eq!(System::block_number(), 30); - - // Account 4 has fully vested. - assert_eq!(Vesting::vesting_balance(&4), Some(0)); - }); - } - - #[test] - fn vested_transfer_correctly_fails() { - ExtBuilder::default() - .existential_deposit(256) - .build() - .execute_with(|| { - let user2_free_balance = Balances::free_balance(&2); - let user4_free_balance = Balances::free_balance(&4); - assert_eq!(user2_free_balance, 256 * 20); - assert_eq!(user4_free_balance, 256 * 40); - - // Account 2 should already have a vesting schedule. - let user2_vesting_schedule = VestingInfo::new::( - 256 * 20, - 256, // Vesting over 20 blocks - 10, - ); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); - - // Fails due to too low transfer amount. - let new_vesting_schedule_too_low = VestingInfo::unsafe_new( - ::MinVestedTransfer::get() - 1, - 64, - 10, - ); - assert_noop!( - Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule_too_low), - Error::::AmountLow, - ); - - // `per_block` is 0, which would result in a schedule with infinite duration. - let schedule_per_block_0 = VestingInfo::unsafe_new( - 256, - 0, - 10, - ); - assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_per_block_0), - Error::::InvalidScheduleParams, - ); - - // `locked` is 0. - let schedule_locked_0 = VestingInfo::unsafe_new( - 0, - 1, - 10, - ); - assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), - Error::::AmountLow, - ); - - // Free balance has not changed. - assert_eq!(user2_free_balance, Balances::free_balance(&2)); - assert_eq!(user4_free_balance, Balances::free_balance(&4)); - - - }); - } - - #[test] - fn vested_transfer_allows_max_schedules() { - ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - let mut user_4_free_balance = Balances::free_balance(&4); - let max_schedules = ::MaxVestingSchedules::get(); - let sched = VestingInfo::new::( - ED, 1, // Vest over 256 blocks. - 10, - ); - // Add max amount schedules to user 4. - for _ in 0 .. max_schedules { - assert_ok!(Vesting::vested_transfer(Some(3).into(), 4, sched)); - } - // The schedules count towards vesting balance - let transferred_amount = ED * max_schedules as u64; - assert_eq!(Vesting::vesting_balance(&4), Some(transferred_amount)); - // and free balance. - user_4_free_balance += transferred_amount; - assert_eq!(Balances::free_balance(&4), user_4_free_balance); - - // Cannot insert a 4th vesting schedule when `MaxVestingSchedules` === 3 - assert_noop!( - Vesting::vested_transfer(Some(3).into(), 4, sched), - Error::::AtMaxVestingSchedules, - ); - // so the free balance does not change. - assert_eq!(Balances::free_balance(&4), user_4_free_balance); - - // Account 4 has fully vested when all the schedules end - System::set_block_number(ED + 10); - assert_eq!(Vesting::vesting_balance(&4), Some(0)); - }); - } - - #[test] - fn force_vested_transfer_works() { - ExtBuilder::default().existential_deposit(256).build().execute_with(|| { - let user3_free_balance = Balances::free_balance(&3); - let user4_free_balance = Balances::free_balance(&4); - assert_eq!(user3_free_balance, 256 * 30); - assert_eq!(user4_free_balance, 256 * 40); - // Account 4 should not have any vesting yet. - assert_eq!(Vesting::vesting(&4), None); - // Make the schedule for the new transfer. - let new_vesting_schedule = VestingInfo::new::( - 256 * 5, - 64, // Vesting over 20 blocks - 10, - ); - - assert_noop!( - Vesting::force_vested_transfer(Some(4).into(), 3, 4, new_vesting_schedule), - BadOrigin - ); - assert_ok!(Vesting::force_vested_transfer( - RawOrigin::Root.into(), - 3, - 4, - new_vesting_schedule - )); - // Now account 4 should have vesting. - assert_eq!(Vesting::vesting(&4).unwrap()[0], new_vesting_schedule); - assert_eq!(Vesting::vesting(&4).unwrap().len(), 1); - // Ensure the transfer happened correctly. - let user3_free_balance_updated = Balances::free_balance(&3); - assert_eq!(user3_free_balance_updated, 256 * 25); - let user4_free_balance_updated = Balances::free_balance(&4); - assert_eq!(user4_free_balance_updated, 256 * 45); - // Account 4 has 5 * 256 locked. - assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5)); - - System::set_block_number(20); - assert_eq!(System::block_number(), 20); - - // Account 4 has 5 * 64 units vested by block 20. - assert_eq!(Vesting::vesting_balance(&4), Some(10 * 64)); - - System::set_block_number(30); - assert_eq!(System::block_number(), 30); - - // Account 4 has fully vested. - assert_eq!(Vesting::vesting_balance(&4), Some(0)); - }); - } - - #[test] - fn force_vested_transfer_correctly_fails() { - ExtBuilder::default().existential_deposit(256).build().execute_with(|| { - let user2_free_balance = Balances::free_balance(&2); - let user4_free_balance = Balances::free_balance(&4); - assert_eq!(user2_free_balance, 256 * 20); - assert_eq!(user4_free_balance, 256 * 40); - // Account 2 should already have a vesting schedule. - let user2_vesting_schedule = VestingInfo::new::( - 256 * 20, - 256, // Vesting over 20 blocks - 10, - ); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); - - // Too low transfer amount. - let new_vesting_schedule_too_low = - VestingInfo::new::(::MinVestedTransfer::get() - 1, 64, 10); - assert_noop!( - Vesting::force_vested_transfer( - RawOrigin::Root.into(), - 3, - 4, - new_vesting_schedule_too_low - ), - Error::::AmountLow, - ); - - // `per_block` is 0. - let schedule_per_block_0 = VestingInfo::new::(256, 0, 10); - assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_per_block_0), - Error::::InvalidScheduleParams, - ); - - // `locked` is 0. - let schedule_locked_0 = VestingInfo::new::(0, 1, 10); - assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), - Error::::AmountLow, - ); - - // Verify no currency transfer happened. - assert_eq!(user2_free_balance, Balances::free_balance(&2)); - assert_eq!(user4_free_balance, Balances::free_balance(&4)); - }); - } - - #[test] - fn force_vested_transfer_allows_max_schedules() { - ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - let mut user_4_free_balance = Balances::free_balance(&4); - let max_schedules = ::MaxVestingSchedules::get(); - let sched = VestingInfo::new::( - ED, 1, // Vest over 256 blocks. - 10, - ); - // Add max amount schedules to user 4. - for _ in 0 .. max_schedules { - assert_ok!(Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, sched)); - } - // The schedules count towards vesting balance. - let transferred_amount = ED * max_schedules as u64; - assert_eq!(Vesting::vesting_balance(&4), Some(transferred_amount)); - // and free balance. - user_4_free_balance += transferred_amount; - assert_eq!(Balances::free_balance(&4), user_4_free_balance); - - // Cannot insert a 4th vesting schedule when `MaxVestingSchedules` === 3. - assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, sched), - Error::::AtMaxVestingSchedules, - ); - // so the free balance does not change. - assert_eq!(Balances::free_balance(&4), user_4_free_balance); - - // Account 4 has fully vested when all the schedules end. - System::set_block_number(ED + 10); - assert_eq!(Vesting::vesting_balance(&4), Some(0)); - }); - } - - #[test] - fn merge_schedules_that_have_not_started() { - ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - // Account 2 should already have a vesting schedule. - let sched0 = VestingInfo::new::( - ED * 20, - ED, // Vest over 20 blocks. - 10, - ); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); - assert_eq!(Balances::usable_balance(&2), 0); - - // Add a schedule that is identical to the one that already exists - Vesting::vested_transfer(Some(3).into(), 2, sched0).unwrap(); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched0]); - assert_eq!(Balances::usable_balance(&2), 0); - Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); - - // Since we merged identical schedules, the new schedule finishes at the same - // time as the original, just with double the amount - let sched1 = VestingInfo::new::( - sched0.locked() * 2, - sched0.per_block() * 2, - 10, // starts at the block the schedules are merged - ); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched1]); - - assert_eq!(Balances::usable_balance(&2), 0); - }); - } - - #[test] - fn merge_ongoing_schedules() { - // Merging two schedules that have started will vest both before merging - ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - // Account 2 should already have a vesting schedule. - let sched0 = VestingInfo::new::( - ED * 20, - ED, // Vest over 20 blocks - 10, - ); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); - - let sched1 = VestingInfo::new::( - 256 * 10, - 256, // Vest over 10 blocks - sched0.starting_block() + 5, // Start at block 15 - ); - Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); - - // Got to half way through the second schedule where both schedules are actively vesting - let cur_block = 20; - System::set_block_number(cur_block); - - // user2 has no usable balances prior to the merge because they have not unlocked - // with `vest` yet. - assert_eq!(Balances::usable_balance(&2), 0); - Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); - - // Merging schedules vests all pre-existing schedules prior to merging, which is reflected - // in user2's updated usable balance - let sched0_vested_now = sched0.per_block() * (cur_block - sched0.starting_block()); - let sched1_vested_now = sched1.per_block() * (cur_block - sched1.starting_block()); - assert_eq!(Balances::usable_balance(&2), sched0_vested_now + sched1_vested_now); - - // The locked amount is the sum of what both schedules have locked at the current block. - let sched2_locked = sched1 - .locked_at::(cur_block) - .saturating_add(sched0.locked_at::(cur_block)); - // End block of the new schedule is the greater of either merged schedule - let sched2_end = - sched1.ending_block::().max(sched0.ending_block::()); - let sched2_duration = sched2_end - cur_block; - // Based off the new schedules total locked and its duration, we can calculate the - // amount to unlock per block. - let sched2_per_block = sched2_locked / sched2_duration; - - let sched2 = VestingInfo::new::(sched2_locked, sched2_per_block, cur_block); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2]); - }); - } - - #[test] - fn merging_shifts_other_schedules_index() { - // Schedules being merged are filtered out, schedules to the right of any merged - // schedule shift left and the new, merged schedule is always last. - ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - let sched0 = VestingInfo::new::( - ED * 10, - ED, // Vesting over 10 blocks - 10, - ); - let sched1 = VestingInfo::new::( - ED * 11, - ED, // Vesting over 11 blocks - 11, - ); - let sched2 = VestingInfo::new::( - ED * 12, - ED, // Vesting over 12 blocks - 12, - ); - - // Account 3 start out with no schedules - assert_eq!(Vesting::vesting(&3), None); - // and some usable balance. - let usable_balance = Balances::usable_balance(&3); - assert_eq!(usable_balance, 30 * ED); - - let cur_block = 1; - assert_eq!(System::block_number(), cur_block); - - // Transfer the above 3 schedules to account 3 - assert_ok!(Vesting::vested_transfer(Some(4).into(), 3, sched0)); - assert_ok!(Vesting::vested_transfer(Some(4).into(), 3, sched1)); - assert_ok!(Vesting::vested_transfer(Some(4).into(), 3, sched2)); - - // With no schedules vested or merged they are in the order they are created - assert_eq!(Vesting::vesting(&3).unwrap(), vec![sched0, sched1, sched2]); - // and the usable balance has not changed. - assert_eq!(usable_balance, Balances::usable_balance(&3)); - - assert_ok!(Vesting::merge_schedules(Some(3).into(), 0, 2)); - - // Create the merged schedule of sched0 & sched2. - // The merged schedule will have the max possible starting block, - let sched3_start = sched1.starting_block().max(sched2.starting_block()); - // have locked equal to the sum of the two schedules locked through the current block, - let sched3_locked = - sched2.locked_at::(cur_block) + sched0.locked_at::(cur_block); - // and will end at the max possible block. - let sched3_end = - sched2.ending_block::().max(sched0.ending_block::()); - let sched3_duration = sched3_end - sched3_start; - let sched3_per_block = sched3_locked / sched3_duration; - let sched3 = VestingInfo::new::(sched3_locked, sched3_per_block, sched3_start); - - // The not touched schedule moves left and the new merged schedule is appended. - assert_eq!(Vesting::vesting(&3).unwrap(), vec![sched1, sched3]); - // The usable balance hasn't changed since none of the schedules have started. - assert_eq!(Balances::usable_balance(&3), usable_balance); - }); - } - - #[test] - fn merge_ongoing_and_yet_to_be_started_schedule() { - // Merge an ongoing schedule that has had `vest` called and a schedule that has not already - // started. - ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - // Account 2 should already have a vesting schedule. - let sched0 = VestingInfo::new::( - ED * 20, - ED, // Vesting over 20 blocks - 10, - ); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); - - // Fast forward to half way through the life of sched_1 - let mut cur_block = (sched0.starting_block() + sched0.ending_block::()) / 2; - assert_eq!(cur_block, 20); - System::set_block_number(cur_block); - - // Prior to vesting there is no usable balance. - let mut usable_balance = 0; - assert_eq!(Balances::usable_balance(&2), usable_balance); - // Vest the current schedules (which is just sched0 now). - Vesting::vest(Some(2).into()).unwrap(); - - // After vesting the usable balance increases by the amount the unlocked amount. - let sched0_vested_now = sched0.locked() - sched0.locked_at::(cur_block); - usable_balance += sched0_vested_now; - assert_eq!(Balances::usable_balance(&2), usable_balance); - - // Go forward a block. - cur_block += 1; - System::set_block_number(cur_block); - - // And add a schedule that starts after this block, but before sched0 finishes. - let sched1 = VestingInfo::new::( - ED * 10, - 1, // Vesting over 256 * 10 (2560) blocks - cur_block + 1, - ); - Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); - - // Merge the schedules before sched1 starts. - Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); - // After merging, the usable balance only changes by the amount sched0 vested since we - // last called `vest` (which is just 1 block). The usable balance is not affected by - // sched1 because it has not started yet. - usable_balance += sched0.per_block(); - assert_eq!(Balances::usable_balance(&2), usable_balance); - - // The resulting schedule will have the later starting block of the two, - let sched2_start = sched1.starting_block(); - // have locked equal to the sum of the two schedules locked through the current block, - let sched2_locked = - sched0.locked_at::(cur_block) + sched1.locked_at::(cur_block); - // and will end at the max possible block. - let sched2_end = - sched0.ending_block::().max(sched1.ending_block::()); - let sched2_duration = sched2_end - sched2_start; - let sched2_per_block = sched2_locked / sched2_duration; - - let sched2 = VestingInfo::new::(sched2_locked, sched2_per_block, sched2_start); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2]); - }); - } - - #[test] - fn merge_finishing_and_ongoing_schedule() { - // If a schedule finishes by the current block we treat the ongoing schedule, - // without any alterations, as the merged one. - ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - // Account 2 should already have a vesting schedule. - let sched0 = VestingInfo::new::( - ED * 20, - ED, // Vesting over 20 blocks. - 10, - ); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); - - let sched1 = VestingInfo::new::( - ED * 40, - ED, // Vesting over 40 blocks. - 10, - ); - assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched1)); - - // Transfer a 3rd schedule, so we can demonstrate how schedule indices change. - // (We are not merging this schedule.) - let sched2 = VestingInfo::new::( - ED * 30, - ED, // Vesting over 30 blocks. - 10, - ); - assert_ok!(Vesting::vested_transfer(Some(3).into(), 2, sched2)); - - // The schedules are in expected order prior to merging. - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1, sched2]); - - // Fast forward to sched0's end block. - let cur_block = sched0.ending_block::(); - System::set_block_number(cur_block); - assert_eq!(System::block_number(), 30); - - // Prior to merge_schedules and with no vest/vest_other called the user has no usable - // balance. - assert_eq!(Balances::usable_balance(&2), 0); - assert_ok!(Vesting::merge_schedules(Some(2).into(), 0, 1)); - - // sched2 is now the first, since sched0 & sched1 get filtered out while "merging". - // sched1 gets treated like the new merged schedule by getting pushed onto back - // of the vesting schedules vec. Note: sched0 finished at the current block. - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2, sched1]); - - // sched0 has finished, so its funds are fully unlocked. - let sched0_unlocked_now = sched0.locked(); - // The remaining schedules are ongoing, so their funds are partially unlocked. - let sched1_unlocked_now = sched1.locked() - sched1.locked_at::(cur_block); - let sched2_unlocked_now = sched2.locked() - sched2.locked_at::(cur_block); - - // Since merging also vests all the schedules, the users usable balance after merging - // includes all pre-existing schedules unlocked through the current block, including - // schedules not merged. - assert_eq!( - Balances::usable_balance(&2), - sched0_unlocked_now + sched1_unlocked_now + sched2_unlocked_now - ); - }); - } - - #[test] - fn merge_finishing_schedules_does_not_create_a_new_one() { - // If both schedules finish by the current block we don't create new one - ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - // Account 2 should already have a vesting schedule. - let sched0 = VestingInfo::new::( - ED * 20, - ED, // 20 block duration. - 10, - ); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); - - // Create sched1 and transfer it to account 2. - let sched1 = VestingInfo::new::( - ED * 30, - ED, // 30 block duration. - 10, - ); - assert_ok!(Vesting::vested_transfer(Some(3).into(), 2, sched1)); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); - - let all_scheds_end = - sched0.ending_block::().max(sched1.ending_block::()); - assert_eq!(all_scheds_end, 40); - System::set_block_number(all_scheds_end); - - // Prior to merge_schedules and with no vest/vest_other called the user has no usable - // balance. - assert_eq!(Balances::usable_balance(&2), 0); - - // Merge schedule 0 and 1. - Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); - // The user no longer has any more vesting schedules because they both ended at the - // block they where merged. - assert_eq!(Vesting::vesting(&2), None); - // And their usable balance has increased by the total amount locked in the merged - // schedules. - assert_eq!(Balances::usable_balance(&2), sched0.locked() + sched1.locked()); - }); - } - - #[test] - fn merge_schedules_throws_proper_errors() { - ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - // Account 2 should already have a vesting schedule. - let sched0 = VestingInfo::new::( - ED * 20, - ED, // 20 block duration. - 10, - ); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); - - // There is only 1 vesting schedule. - assert_noop!( - Vesting::merge_schedules(Some(2).into(), 0, 1), - Error::::ScheduleIndexOutOfBounds - ); - - // There are 0 vesting schedules. - assert_eq!(Vesting::vesting(&4), None); - assert_noop!(Vesting::merge_schedules(Some(4).into(), 0, 1), Error::::NotVesting); - - // There are enough schedules to merge but an index is non-existent. - Vesting::vested_transfer(Some(3).into(), 2, sched0).unwrap(); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched0]); - assert_noop!( - Vesting::merge_schedules(Some(2).into(), 0, 2), - Error::::ScheduleIndexOutOfBounds - ); - - // It is a storage noop with no errors if the indexes are the same. - assert_storage_noop!(Vesting::merge_schedules(Some(2).into(), 0, 0).unwrap()); - }); - } - - #[test] - fn generates_multiple_schedules_from_genesis_config() { - let vesting_config = vec![ - // 5 * existential deposit locked - (1, 0, 10, 5 * ED), - // 1 * existential deposit locked - (2, 10, 20, 19 * ED), - // 2 * existential deposit locked - (2, 10, 20, 18 * ED), - // 1 * existential deposit locked - (12, 10, 20, 9 * ED), - // 2 * existential deposit locked - (12, 10, 20, 8 * ED), - // 3 * existential deposit locked - (12, 10, 20, 7 * ED), - ]; - ExtBuilder::default() - .existential_deposit(ED) - .vesting_genesis_config(vesting_config) - .build() - .execute_with(|| { - let user1_sched1 = VestingInfo::new::(5 * ED, 128, 0u64); - assert_eq!(Vesting::vesting(&1).unwrap(), vec![user1_sched1]); - - let user2_sched1 = VestingInfo::new::(1 * ED, 12, 10u64); - let user2_sched2 = VestingInfo::new::(2 * ED, 25, 10u64); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_sched1, user2_sched2]); - - let user12_sched1 = VestingInfo::new::(1 * ED, 12, 10u64); - let user12_sched2 = VestingInfo::new::(2 * ED, 25, 10u64); - let user12_sched3 = VestingInfo::new::(3 * ED, 38, 10u64); - assert_eq!( - Vesting::vesting(&12).unwrap(), - vec![user12_sched1, user12_sched2, user12_sched3] - ); - }); - } - - #[test] - #[should_panic] - fn multiple_schedules_from_genesis_config_errors() { - // MaxVestingSchedules is 3, but this config has 4 for account 12 so we panic when building - // from genesis. - let vesting_config = - vec![(12, 10, 20, ED), (12, 10, 20, ED), (12, 10, 20, ED), (12, 10, 20, ED)]; - ExtBuilder::default() - .existential_deposit(ED) - .vesting_genesis_config(vesting_config) - .build(); - } - - #[test] - fn merge_vesting_info_handles_per_block_0() { - // Faulty schedules with an infinite duration (per_block == 0) can be merged to create - // a schedule that vest 1 per_block, (helpful for faulty, legacy schedules). - ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - let sched0 = VestingInfo::new::( - ED, - 0, // Vesting over infinite blocks. - 1 - ); - let sched1 = VestingInfo::new::( - ED * 2, - 0, // Vesting over infinite blocks. - 10 - ); - - let cur_block = 5; - let expected_locked = - sched0.locked_at::(cur_block) + sched1.locked_at::(cur_block); - let expected_sched = VestingInfo::new::( - expected_locked, - 1, // per_block of 1, which corrects existing schedules. - 10, - ); - assert_eq!( - Vesting::merge_vesting_info(cur_block, sched0, sched1), - Some(expected_sched), - ); - }); - } - - #[test] - fn vesting_info_validation_works() { - let min_transfer = ::MinVestedTransfer::get(); - // Does not check for min transfer. - match VestingInfo::new::(min_transfer - 1, 1u64, 10u64).validate::() { - Ok(_) => (), - _ => panic!(), - } - - // `locked` cannot be 0. - match VestingInfo::new::(0, 1u64, 10u64).validate::() { - Err(Error::::InvalidScheduleParams) => (), - _ => panic!(), - } - - // `per_block` cannot be 0. - match VestingInfo::new::(min_transfer + 1, 0u64, 10u64).validate() { - Err(Error::::InvalidScheduleParams) => (), - _ => panic!(), - } - - // With valid inputs it does not error and does not modify the inputs. - assert_eq!( - VestingInfo::new::(min_transfer, 1u64, 10u64).validate::().unwrap(), - VestingInfo::new::(min_transfer, 1u64, 10u64) - ); - - // `per_block` is never bigger than `locked`. - assert_eq!( - VestingInfo::new::(256u64, 256 * 2u64, 10u64).validate::().unwrap(), - VestingInfo::new::(256u64, 256u64, 10u64) - ); - } - - #[test] - fn vesting_info_ending_block_works() { - // Treats `per_block` 0 as a `per_block` of 1 to accommodate faulty, legacy schedules. - let per_block_0 = VestingInfo::new::(256u32, 0u32, 10u32); - assert_eq!( - per_block_0.ending_block::(), - per_block_0.locked() + per_block_0.starting_block() - ); - let per_block_1 = VestingInfo::new::(256u32, 1u32, 10u32); - assert_eq!(per_block_0.ending_block::(), per_block_1.ending_block::()); - - // `per_block >= locked` always results in a schedule ending the block after it starts - let per_block_gt_locked = VestingInfo::new::(256u32, 256 * 2u32, 10u32); - assert_eq!( - per_block_gt_locked.ending_block::(), - 1 + per_block_gt_locked.starting_block() - ); - let per_block_eq_locked = VestingInfo::new::(256u32, 256u32, 10u32); - assert_eq!( - per_block_gt_locked.ending_block::(), - per_block_eq_locked.ending_block::() - ); - - // Correctly calcs end if `locked % per_block != 0`. (We need a block to unlock the remainder). - let imperfect_per_block = VestingInfo::new::(256u32, 250u32, 10u32); - assert_eq!( - imperfect_per_block.ending_block::(), - imperfect_per_block.starting_block() + 2u32, - ); - assert_eq!( - imperfect_per_block - .locked_at::(imperfect_per_block.ending_block::()), - 0 - ); - } -} diff --git a/frame/vesting/src/mock.rs b/frame/vesting/src/mock.rs new file mode 100644 index 0000000000000..62233b2254830 --- /dev/null +++ b/frame/vesting/src/mock.rs @@ -0,0 +1,154 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use frame_support::parameter_types; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, Identity, IdentityLookup}, +}; + +use super::*; +use crate as pallet_vesting; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Vesting: pallet_vesting::{Pallet, Call, Storage, Event, Config}, + } +); + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max(1024); +} +impl frame_system::Config for Test { + type AccountData = pallet_balances::AccountData; + type AccountId = u64; + type BaseCallFilter = (); + type BlockHashCount = BlockHashCount; + type BlockLength = (); + type BlockNumber = u64; + type BlockWeights = (); + type Call = Call; + type DbWeight = (); + type Event = Event; + type Hash = H256; + type Hashing = BlakeTwo256; + type Header = Header; + type Index = u64; + type Lookup = IdentityLookup; + type OnKilledAccount = (); + type OnNewAccount = (); + type OnSetCode = (); + type Origin = Origin; + type PalletInfo = PalletInfo; + type SS58Prefix = (); + type SystemWeightInfo = (); + type Version = (); +} +parameter_types! { + pub const MaxLocks: u32 = 10; +} +impl pallet_balances::Config for Test { + type AccountStore = System; + type Balance = u64; + type DustRemoval = (); + type Event = Event; + type ExistentialDeposit = ExistentialDeposit; + type MaxLocks = MaxLocks; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type WeightInfo = (); +} +parameter_types! { + pub const MinVestedTransfer: u64 = 10; + pub static ExistentialDeposit: u64 = 0; + pub const MaxVestingSchedules: u32 = 3; +} +impl Config for Test { + type BlockNumberToBalance = Identity; + type Currency = Balances; + type Event = Event; + type MaxVestingSchedules = MaxVestingSchedules; + type MinVestedTransfer = MinVestedTransfer; + type WeightInfo = (); +} + +pub struct ExtBuilder { + existential_deposit: u64, + vesting_genesis_config: Option>, +} + +impl Default for ExtBuilder { + fn default() -> Self { + Self { existential_deposit: 1, vesting_genesis_config: None } + } +} +impl ExtBuilder { + pub fn existential_deposit(mut self, existential_deposit: u64) -> Self { + self.existential_deposit = existential_deposit; + self + } + + pub fn vesting_genesis_config(mut self, config: Vec<(u64, u64, u64, u64)>) -> Self { + self.vesting_genesis_config = Some(config); + self + } + + pub fn build(self) -> sp_io::TestExternalities { + EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + pallet_balances::GenesisConfig:: { + balances: vec![ + (1, 10 * self.existential_deposit), + (2, 20 * self.existential_deposit), + (3, 30 * self.existential_deposit), + (4, 40 * self.existential_deposit), + (12, 10 * self.existential_deposit), + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + let vesting = if let Some(vesting_config) = self.vesting_genesis_config { + vesting_config + } else { + vec![ + (1, 0, 10, 5 * self.existential_deposit), + (2, 10, 20, 0), + (12, 10, 20, 5 * self.existential_deposit), + ] + }; + + pallet_vesting::GenesisConfig:: { vesting } + .assimilate_storage(&mut t) + .unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext + } +} diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs new file mode 100644 index 0000000000000..e37876b4c5260 --- /dev/null +++ b/frame/vesting/src/tests.rs @@ -0,0 +1,984 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use frame_support::{assert_noop, assert_ok, assert_storage_noop}; +use frame_system::RawOrigin; +use sp_runtime::traits::{BadOrigin, Identity}; + +use super::*; +use crate::mock::{Balances, ExtBuilder, System, Test, Vesting}; + +/// A default existential deposit. +const ED: u64 = 256; + +#[test] +fn check_vesting_status() { + ExtBuilder::default().existential_deposit(256).build().execute_with(|| { + let user1_free_balance = Balances::free_balance(&1); + let user2_free_balance = Balances::free_balance(&2); + let user12_free_balance = Balances::free_balance(&12); + assert_eq!(user1_free_balance, 256 * 10); // Account 1 has free balance + assert_eq!(user2_free_balance, 256 * 20); // Account 2 has free balance + assert_eq!(user12_free_balance, 256 * 10); // Account 12 has free balance + let user1_vesting_schedule = VestingInfo::new::( + 256 * 5, + 128, // Vesting over 10 blocks + 0, + ); + let user2_vesting_schedule = VestingInfo::new::( + 256 * 20, + 256, // Vesting over 20 blocks + 10, + ); + let user12_vesting_schedule = VestingInfo::new::( + 256 * 5, + 64, // Vesting over 20 blocks + 10, + ); + assert_eq!(Vesting::vesting(&1).unwrap(), vec![user1_vesting_schedule]); // Account 1 has a vesting schedule + assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); // Account 2 has a vesting schedule + assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); // Account 12 has a vesting schedule + + // Account 1 has only 128 units vested from their illiquid 256 * 5 units at block 1 + assert_eq!(Vesting::vesting_balance(&1), Some(128 * 9)); + // Account 2 has their full balance locked + assert_eq!(Vesting::vesting_balance(&2), Some(user2_free_balance)); + // Account 12 has only their illiquid funds locked + assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); + + System::set_block_number(10); + assert_eq!(System::block_number(), 10); + + // Account 1 has fully vested by block 10 + assert_eq!(Vesting::vesting_balance(&1), Some(0)); + // Account 2 has started vesting by block 10 + assert_eq!(Vesting::vesting_balance(&2), Some(user2_free_balance)); + // Account 12 has started vesting by block 10 + assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); + + System::set_block_number(30); + assert_eq!(System::block_number(), 30); + + assert_eq!(Vesting::vesting_balance(&1), Some(0)); // Account 1 is still fully vested, and not negative + assert_eq!(Vesting::vesting_balance(&2), Some(0)); // Account 2 has fully vested by block 30 + assert_eq!(Vesting::vesting_balance(&12), Some(0)); // Account 2 has fully vested by block 30 + }); +} + +#[test] +fn check_vesting_status_for_multi_schedule_account() { + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + assert_eq!(System::block_number(), 1); + let sched0 = VestingInfo::new::( + ED * 20, + ED, // Vesting over 20 blocks + 10, + ); + // Account 2 already has a vesting schedule. + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + + // Account 2's free balance is from sched0 + let free_balance = Balances::free_balance(&2); + assert_eq!(free_balance, ED * (20)); + assert_eq!(Vesting::vesting_balance(&2), Some(free_balance)); + + // Add a 2nd schedule that is already unlocking by block #1 + let sched1 = VestingInfo::new::( + ED * 10, + ED, // Vesting over 10 blocks + 0, + ); + assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched1)); + // Free balance is equal to the two existing schedules total amount. + let free_balance = Balances::free_balance(&2); + assert_eq!(free_balance, ED * (10 + 20)); + // The most recently added schedule exists. + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); + // sched1 has free funds at block #1, but nothing else. + assert_eq!(Vesting::vesting_balance(&2), Some(free_balance - sched1.per_block())); + + // Add a 3rd schedule + let sched2 = VestingInfo::new::( + ED * 30, + ED, // Vesting over 30 blocks + 5, + ); + assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched2)); + + System::set_block_number(9); + // Free balance is equal to the 3 existing schedules total amount. + let free_balance = Balances::free_balance(&2); + assert_eq!(free_balance, ED * (10 + 20 + 30)); + // sched1 and sched2 are freeing funds at block #9. + assert_eq!( + Vesting::vesting_balance(&2), + Some(free_balance - sched1.per_block() * 9 - sched2.per_block() * 4) + ); + + System::set_block_number(20); + // At block #20 sched1 is fully unlocked while sched2 and sched0 are partially unlocked. + assert_eq!( + Vesting::vesting_balance(&2), + Some( + free_balance - sched1.locked() - sched2.per_block() * 15 - sched0.per_block() * 10 + ) + ); + + System::set_block_number(30); + // At block #30 sched0 and sched1 are fully unlocked while sched2 is partially unlocked. + assert_eq!( + Vesting::vesting_balance(&2), + Some(free_balance - sched1.locked() - sched2.per_block() * 25 - sched0.locked()) + ); + + // At block #35 sched2 fully unlocks and thus all schedules funds are unlocked. + System::set_block_number(35); + assert_eq!(System::block_number(), 35); + assert_eq!(Vesting::vesting_balance(&2), Some(0)); + // Since we have not called any extrinsics that would unlock funds the schedules + // are still in storage. + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1, sched2]); + assert_ok!(Vesting::vest(Some(2).into())); + }); +} + +#[test] +fn unvested_balance_should_not_transfer() { + ExtBuilder::default().existential_deposit(10).build().execute_with(|| { + let user1_free_balance = Balances::free_balance(&1); + assert_eq!(user1_free_balance, 100); // Account 1 has free balance + // Account 1 has only 5 units vested at block 1 (plus 50 unvested) + assert_eq!(Vesting::vesting_balance(&1), Some(45)); + assert_noop!( + Balances::transfer(Some(1).into(), 2, 56), + pallet_balances::Error::::LiquidityRestrictions, + ); // Account 1 cannot send more than vested amount + }); +} + +#[test] +fn vested_balance_should_transfer() { + ExtBuilder::default().existential_deposit(10).build().execute_with(|| { + let user1_free_balance = Balances::free_balance(&1); + assert_eq!(user1_free_balance, 100); // Account 1 has free balance + // Account 1 has only 5 units vested at block 1 (plus 50 unvested) + assert_eq!(Vesting::vesting_balance(&1), Some(45)); + assert_ok!(Vesting::vest(Some(1).into())); + assert_ok!(Balances::transfer(Some(1).into(), 2, 55)); + }); +} + +#[test] +fn vested_balance_should_transfer_using_vest_other() { + ExtBuilder::default().existential_deposit(10).build().execute_with(|| { + let user1_free_balance = Balances::free_balance(&1); + assert_eq!(user1_free_balance, 100); // Account 1 has free balance + // Account 1 has only 5 units vested at block 1 (plus 50 unvested) + assert_eq!(Vesting::vesting_balance(&1), Some(45)); + assert_ok!(Vesting::vest_other(Some(2).into(), 1)); + assert_ok!(Balances::transfer(Some(1).into(), 2, 55)); + }); +} + +#[test] +fn extra_balance_should_transfer() { + ExtBuilder::default().existential_deposit(10).build().execute_with(|| { + assert_ok!(Balances::transfer(Some(3).into(), 1, 100)); + assert_ok!(Balances::transfer(Some(3).into(), 2, 100)); + + let user1_free_balance = Balances::free_balance(&1); + assert_eq!(user1_free_balance, 200); // Account 1 has 100 more free balance than normal + + let user2_free_balance = Balances::free_balance(&2); + assert_eq!(user2_free_balance, 300); // Account 2 has 100 more free balance than normal + + // Account 1 has only 5 units vested at block 1 (plus 150 unvested) + assert_eq!(Vesting::vesting_balance(&1), Some(45)); + assert_ok!(Vesting::vest(Some(1).into())); + assert_ok!(Balances::transfer(Some(1).into(), 3, 155)); // Account 1 can send extra units gained + + // Account 2 has no units vested at block 1, but gained 100 + assert_eq!(Vesting::vesting_balance(&2), Some(200)); + assert_ok!(Vesting::vest(Some(2).into())); + assert_ok!(Balances::transfer(Some(2).into(), 3, 100)); // Account 2 can send extra units gained + }); +} + +#[test] +fn liquid_funds_should_transfer_with_delayed_vesting() { + ExtBuilder::default().existential_deposit(256).build().execute_with(|| { + let user12_free_balance = Balances::free_balance(&12); + + assert_eq!(user12_free_balance, 2560); // Account 12 has free balance + // Account 12 has liquid funds + assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); + + // Account 12 has delayed vesting + let user12_vesting_schedule = VestingInfo::new::( + 256 * 5, + 64, // Vesting over 20 blocks + 10, + ); + assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); + + // Account 12 can still send liquid funds + assert_ok!(Balances::transfer(Some(12).into(), 3, 256 * 5)); + }); +} + +#[test] +fn vested_transfer_works() { + ExtBuilder::default().existential_deposit(256).build().execute_with(|| { + let user3_free_balance = Balances::free_balance(&3); + let user4_free_balance = Balances::free_balance(&4); + assert_eq!(user3_free_balance, 256 * 30); + assert_eq!(user4_free_balance, 256 * 40); + // Account 4 should not have any vesting yet. + assert_eq!(Vesting::vesting(&4), None); + // Make the schedule for the new transfer. + let new_vesting_schedule = VestingInfo::new::( + 256 * 5, + 64, // Vesting over 20 blocks + 10, + ); + assert_ok!(Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule)); + // Now account 4 should have vesting. + assert_eq!(Vesting::vesting(&4).unwrap(), vec![new_vesting_schedule]); + // Ensure the transfer happened correctly. + let user3_free_balance_updated = Balances::free_balance(&3); + assert_eq!(user3_free_balance_updated, 256 * 25); + let user4_free_balance_updated = Balances::free_balance(&4); + assert_eq!(user4_free_balance_updated, 256 * 45); + // Account 4 has 5 * 256 locked. + assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5)); + + System::set_block_number(20); + assert_eq!(System::block_number(), 20); + + // Account 4 has 5 * 64 units vested by block 20. + assert_eq!(Vesting::vesting_balance(&4), Some(10 * 64)); + + System::set_block_number(30); + assert_eq!(System::block_number(), 30); + + // Account 4 has fully vested. + assert_eq!(Vesting::vesting_balance(&4), Some(0)); + }); +} + +#[test] +fn vested_transfer_correctly_fails() { + ExtBuilder::default().existential_deposit(256).build().execute_with(|| { + let user2_free_balance = Balances::free_balance(&2); + let user4_free_balance = Balances::free_balance(&4); + assert_eq!(user2_free_balance, 256 * 20); + assert_eq!(user4_free_balance, 256 * 40); + + // Account 2 should already have a vesting schedule. + let user2_vesting_schedule = VestingInfo::new::( + 256 * 20, + 256, // Vesting over 20 blocks + 10, + ); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); + + // Fails due to too low transfer amount. + let new_vesting_schedule_too_low = + VestingInfo::unsafe_new(::MinVestedTransfer::get() - 1, 64, 10); + assert_noop!( + Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule_too_low), + Error::::AmountLow, + ); + + // `per_block` is 0, which would result in a schedule with infinite duration. + let schedule_per_block_0 = VestingInfo::unsafe_new(256, 0, 10); + assert_noop!( + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_per_block_0), + Error::::InvalidScheduleParams, + ); + + // `locked` is 0. + let schedule_locked_0 = VestingInfo::unsafe_new(0, 1, 10); + assert_noop!( + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), + Error::::AmountLow, + ); + + // Free balance has not changed. + assert_eq!(user2_free_balance, Balances::free_balance(&2)); + assert_eq!(user4_free_balance, Balances::free_balance(&4)); + }); +} + +#[test] +fn vested_transfer_allows_max_schedules() { + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let mut user_4_free_balance = Balances::free_balance(&4); + let max_schedules = ::MaxVestingSchedules::get(); + let sched = VestingInfo::new::( + ED, 1, // Vest over 256 blocks. + 10, + ); + // Add max amount schedules to user 4. + for _ in 0 .. max_schedules { + assert_ok!(Vesting::vested_transfer(Some(3).into(), 4, sched)); + } + // The schedules count towards vesting balance + let transferred_amount = ED * max_schedules as u64; + assert_eq!(Vesting::vesting_balance(&4), Some(transferred_amount)); + // and free balance. + user_4_free_balance += transferred_amount; + assert_eq!(Balances::free_balance(&4), user_4_free_balance); + + // Cannot insert a 4th vesting schedule when `MaxVestingSchedules` === 3 + assert_noop!( + Vesting::vested_transfer(Some(3).into(), 4, sched), + Error::::AtMaxVestingSchedules, + ); + // so the free balance does not change. + assert_eq!(Balances::free_balance(&4), user_4_free_balance); + + // Account 4 has fully vested when all the schedules end + System::set_block_number(ED + 10); + assert_eq!(Vesting::vesting_balance(&4), Some(0)); + }); +} + +#[test] +fn force_vested_transfer_works() { + ExtBuilder::default().existential_deposit(256).build().execute_with(|| { + let user3_free_balance = Balances::free_balance(&3); + let user4_free_balance = Balances::free_balance(&4); + assert_eq!(user3_free_balance, 256 * 30); + assert_eq!(user4_free_balance, 256 * 40); + // Account 4 should not have any vesting yet. + assert_eq!(Vesting::vesting(&4), None); + // Make the schedule for the new transfer. + let new_vesting_schedule = VestingInfo::new::( + 256 * 5, + 64, // Vesting over 20 blocks + 10, + ); + + assert_noop!( + Vesting::force_vested_transfer(Some(4).into(), 3, 4, new_vesting_schedule), + BadOrigin + ); + assert_ok!(Vesting::force_vested_transfer( + RawOrigin::Root.into(), + 3, + 4, + new_vesting_schedule + )); + // Now account 4 should have vesting. + assert_eq!(Vesting::vesting(&4).unwrap()[0], new_vesting_schedule); + assert_eq!(Vesting::vesting(&4).unwrap().len(), 1); + // Ensure the transfer happened correctly. + let user3_free_balance_updated = Balances::free_balance(&3); + assert_eq!(user3_free_balance_updated, 256 * 25); + let user4_free_balance_updated = Balances::free_balance(&4); + assert_eq!(user4_free_balance_updated, 256 * 45); + // Account 4 has 5 * 256 locked. + assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5)); + + System::set_block_number(20); + assert_eq!(System::block_number(), 20); + + // Account 4 has 5 * 64 units vested by block 20. + assert_eq!(Vesting::vesting_balance(&4), Some(10 * 64)); + + System::set_block_number(30); + assert_eq!(System::block_number(), 30); + + // Account 4 has fully vested. + assert_eq!(Vesting::vesting_balance(&4), Some(0)); + }); +} + +#[test] +fn force_vested_transfer_correctly_fails() { + ExtBuilder::default().existential_deposit(256).build().execute_with(|| { + let user2_free_balance = Balances::free_balance(&2); + let user4_free_balance = Balances::free_balance(&4); + assert_eq!(user2_free_balance, 256 * 20); + assert_eq!(user4_free_balance, 256 * 40); + // Account 2 should already have a vesting schedule. + let user2_vesting_schedule = VestingInfo::new::( + 256 * 20, + 256, // Vesting over 20 blocks + 10, + ); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); + + // Too low transfer amount. + let new_vesting_schedule_too_low = + VestingInfo::new::(::MinVestedTransfer::get() - 1, 64, 10); + assert_noop!( + Vesting::force_vested_transfer( + RawOrigin::Root.into(), + 3, + 4, + new_vesting_schedule_too_low + ), + Error::::AmountLow, + ); + + // `per_block` is 0. + let schedule_per_block_0 = VestingInfo::new::(256, 0, 10); + assert_noop!( + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_per_block_0), + Error::::InvalidScheduleParams, + ); + + // `locked` is 0. + let schedule_locked_0 = VestingInfo::new::(0, 1, 10); + assert_noop!( + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), + Error::::AmountLow, + ); + + // Verify no currency transfer happened. + assert_eq!(user2_free_balance, Balances::free_balance(&2)); + assert_eq!(user4_free_balance, Balances::free_balance(&4)); + }); +} + +#[test] +fn force_vested_transfer_allows_max_schedules() { + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let mut user_4_free_balance = Balances::free_balance(&4); + let max_schedules = ::MaxVestingSchedules::get(); + let sched = VestingInfo::new::( + ED, 1, // Vest over 256 blocks. + 10, + ); + // Add max amount schedules to user 4. + for _ in 0 .. max_schedules { + assert_ok!(Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, sched)); + } + // The schedules count towards vesting balance. + let transferred_amount = ED * max_schedules as u64; + assert_eq!(Vesting::vesting_balance(&4), Some(transferred_amount)); + // and free balance. + user_4_free_balance += transferred_amount; + assert_eq!(Balances::free_balance(&4), user_4_free_balance); + + // Cannot insert a 4th vesting schedule when `MaxVestingSchedules` === 3. + assert_noop!( + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, sched), + Error::::AtMaxVestingSchedules, + ); + // so the free balance does not change. + assert_eq!(Balances::free_balance(&4), user_4_free_balance); + + // Account 4 has fully vested when all the schedules end. + System::set_block_number(ED + 10); + assert_eq!(Vesting::vesting_balance(&4), Some(0)); + }); +} + +#[test] +fn merge_schedules_that_have_not_started() { + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + // Account 2 should already have a vesting schedule. + let sched0 = VestingInfo::new::( + ED * 20, + ED, // Vest over 20 blocks. + 10, + ); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + assert_eq!(Balances::usable_balance(&2), 0); + + // Add a schedule that is identical to the one that already exists + Vesting::vested_transfer(Some(3).into(), 2, sched0).unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched0]); + assert_eq!(Balances::usable_balance(&2), 0); + Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + + // Since we merged identical schedules, the new schedule finishes at the same + // time as the original, just with double the amount + let sched1 = VestingInfo::new::( + sched0.locked() * 2, + sched0.per_block() * 2, + 10, // starts at the block the schedules are merged + ); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched1]); + + assert_eq!(Balances::usable_balance(&2), 0); + }); +} + +#[test] +fn merge_ongoing_schedules() { + // Merging two schedules that have started will vest both before merging + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + // Account 2 should already have a vesting schedule. + let sched0 = VestingInfo::new::( + ED * 20, + ED, // Vest over 20 blocks + 10, + ); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + + let sched1 = VestingInfo::new::( + 256 * 10, + 256, // Vest over 10 blocks + sched0.starting_block() + 5, // Start at block 15 + ); + Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); + + // Got to half way through the second schedule where both schedules are actively vesting + let cur_block = 20; + System::set_block_number(cur_block); + + // user2 has no usable balances prior to the merge because they have not unlocked + // with `vest` yet. + assert_eq!(Balances::usable_balance(&2), 0); + Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + + // Merging schedules vests all pre-existing schedules prior to merging, which is reflected + // in user2's updated usable balance + let sched0_vested_now = sched0.per_block() * (cur_block - sched0.starting_block()); + let sched1_vested_now = sched1.per_block() * (cur_block - sched1.starting_block()); + assert_eq!(Balances::usable_balance(&2), sched0_vested_now + sched1_vested_now); + + // The locked amount is the sum of what both schedules have locked at the current block. + let sched2_locked = sched1 + .locked_at::(cur_block) + .saturating_add(sched0.locked_at::(cur_block)); + // End block of the new schedule is the greater of either merged schedule + let sched2_end = sched1.ending_block::().max(sched0.ending_block::()); + let sched2_duration = sched2_end - cur_block; + // Based off the new schedules total locked and its duration, we can calculate the + // amount to unlock per block. + let sched2_per_block = sched2_locked / sched2_duration; + + let sched2 = VestingInfo::new::(sched2_locked, sched2_per_block, cur_block); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2]); + }); +} + +#[test] +fn merging_shifts_other_schedules_index() { + // Schedules being merged are filtered out, schedules to the right of any merged + // schedule shift left and the new, merged schedule is always last. + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let sched0 = VestingInfo::new::( + ED * 10, + ED, // Vesting over 10 blocks + 10, + ); + let sched1 = VestingInfo::new::( + ED * 11, + ED, // Vesting over 11 blocks + 11, + ); + let sched2 = VestingInfo::new::( + ED * 12, + ED, // Vesting over 12 blocks + 12, + ); + + // Account 3 start out with no schedules + assert_eq!(Vesting::vesting(&3), None); + // and some usable balance. + let usable_balance = Balances::usable_balance(&3); + assert_eq!(usable_balance, 30 * ED); + + let cur_block = 1; + assert_eq!(System::block_number(), cur_block); + + // Transfer the above 3 schedules to account 3 + assert_ok!(Vesting::vested_transfer(Some(4).into(), 3, sched0)); + assert_ok!(Vesting::vested_transfer(Some(4).into(), 3, sched1)); + assert_ok!(Vesting::vested_transfer(Some(4).into(), 3, sched2)); + + // With no schedules vested or merged they are in the order they are created + assert_eq!(Vesting::vesting(&3).unwrap(), vec![sched0, sched1, sched2]); + // and the usable balance has not changed. + assert_eq!(usable_balance, Balances::usable_balance(&3)); + + assert_ok!(Vesting::merge_schedules(Some(3).into(), 0, 2)); + + // Create the merged schedule of sched0 & sched2. + // The merged schedule will have the max possible starting block, + let sched3_start = sched1.starting_block().max(sched2.starting_block()); + // have locked equal to the sum of the two schedules locked through the current block, + let sched3_locked = + sched2.locked_at::(cur_block) + sched0.locked_at::(cur_block); + // and will end at the max possible block. + let sched3_end = sched2.ending_block::().max(sched0.ending_block::()); + let sched3_duration = sched3_end - sched3_start; + let sched3_per_block = sched3_locked / sched3_duration; + let sched3 = VestingInfo::new::(sched3_locked, sched3_per_block, sched3_start); + + // The not touched schedule moves left and the new merged schedule is appended. + assert_eq!(Vesting::vesting(&3).unwrap(), vec![sched1, sched3]); + // The usable balance hasn't changed since none of the schedules have started. + assert_eq!(Balances::usable_balance(&3), usable_balance); + }); +} + +#[test] +fn merge_ongoing_and_yet_to_be_started_schedule() { + // Merge an ongoing schedule that has had `vest` called and a schedule that has not already + // started. + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + // Account 2 should already have a vesting schedule. + let sched0 = VestingInfo::new::( + ED * 20, + ED, // Vesting over 20 blocks + 10, + ); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + + // Fast forward to half way through the life of sched_1 + let mut cur_block = (sched0.starting_block() + sched0.ending_block::()) / 2; + assert_eq!(cur_block, 20); + System::set_block_number(cur_block); + + // Prior to vesting there is no usable balance. + let mut usable_balance = 0; + assert_eq!(Balances::usable_balance(&2), usable_balance); + // Vest the current schedules (which is just sched0 now). + Vesting::vest(Some(2).into()).unwrap(); + + // After vesting the usable balance increases by the amount the unlocked amount. + let sched0_vested_now = sched0.locked() - sched0.locked_at::(cur_block); + usable_balance += sched0_vested_now; + assert_eq!(Balances::usable_balance(&2), usable_balance); + + // Go forward a block. + cur_block += 1; + System::set_block_number(cur_block); + + // And add a schedule that starts after this block, but before sched0 finishes. + let sched1 = VestingInfo::new::( + ED * 10, + 1, // Vesting over 256 * 10 (2560) blocks + cur_block + 1, + ); + Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); + + // Merge the schedules before sched1 starts. + Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + // After merging, the usable balance only changes by the amount sched0 vested since we + // last called `vest` (which is just 1 block). The usable balance is not affected by + // sched1 because it has not started yet. + usable_balance += sched0.per_block(); + assert_eq!(Balances::usable_balance(&2), usable_balance); + + // The resulting schedule will have the later starting block of the two, + let sched2_start = sched1.starting_block(); + // have locked equal to the sum of the two schedules locked through the current block, + let sched2_locked = + sched0.locked_at::(cur_block) + sched1.locked_at::(cur_block); + // and will end at the max possible block. + let sched2_end = sched0.ending_block::().max(sched1.ending_block::()); + let sched2_duration = sched2_end - sched2_start; + let sched2_per_block = sched2_locked / sched2_duration; + + let sched2 = VestingInfo::new::(sched2_locked, sched2_per_block, sched2_start); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2]); + }); +} + +#[test] +fn merge_finishing_and_ongoing_schedule() { + // If a schedule finishes by the current block we treat the ongoing schedule, + // without any alterations, as the merged one. + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + // Account 2 should already have a vesting schedule. + let sched0 = VestingInfo::new::( + ED * 20, + ED, // Vesting over 20 blocks. + 10, + ); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + + let sched1 = VestingInfo::new::( + ED * 40, + ED, // Vesting over 40 blocks. + 10, + ); + assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched1)); + + // Transfer a 3rd schedule, so we can demonstrate how schedule indices change. + // (We are not merging this schedule.) + let sched2 = VestingInfo::new::( + ED * 30, + ED, // Vesting over 30 blocks. + 10, + ); + assert_ok!(Vesting::vested_transfer(Some(3).into(), 2, sched2)); + + // The schedules are in expected order prior to merging. + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1, sched2]); + + // Fast forward to sched0's end block. + let cur_block = sched0.ending_block::(); + System::set_block_number(cur_block); + assert_eq!(System::block_number(), 30); + + // Prior to merge_schedules and with no vest/vest_other called the user has no usable + // balance. + assert_eq!(Balances::usable_balance(&2), 0); + assert_ok!(Vesting::merge_schedules(Some(2).into(), 0, 1)); + + // sched2 is now the first, since sched0 & sched1 get filtered out while "merging". + // sched1 gets treated like the new merged schedule by getting pushed onto back + // of the vesting schedules vec. Note: sched0 finished at the current block. + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2, sched1]); + + // sched0 has finished, so its funds are fully unlocked. + let sched0_unlocked_now = sched0.locked(); + // The remaining schedules are ongoing, so their funds are partially unlocked. + let sched1_unlocked_now = sched1.locked() - sched1.locked_at::(cur_block); + let sched2_unlocked_now = sched2.locked() - sched2.locked_at::(cur_block); + + // Since merging also vests all the schedules, the users usable balance after merging + // includes all pre-existing schedules unlocked through the current block, including + // schedules not merged. + assert_eq!( + Balances::usable_balance(&2), + sched0_unlocked_now + sched1_unlocked_now + sched2_unlocked_now + ); + }); +} + +#[test] +fn merge_finishing_schedules_does_not_create_a_new_one() { + // If both schedules finish by the current block we don't create new one + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + // Account 2 should already have a vesting schedule. + let sched0 = VestingInfo::new::( + ED * 20, + ED, // 20 block duration. + 10, + ); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + + // Create sched1 and transfer it to account 2. + let sched1 = VestingInfo::new::( + ED * 30, + ED, // 30 block duration. + 10, + ); + assert_ok!(Vesting::vested_transfer(Some(3).into(), 2, sched1)); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); + + let all_scheds_end = + sched0.ending_block::().max(sched1.ending_block::()); + assert_eq!(all_scheds_end, 40); + System::set_block_number(all_scheds_end); + + // Prior to merge_schedules and with no vest/vest_other called the user has no usable + // balance. + assert_eq!(Balances::usable_balance(&2), 0); + + // Merge schedule 0 and 1. + Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + // The user no longer has any more vesting schedules because they both ended at the + // block they where merged. + assert_eq!(Vesting::vesting(&2), None); + // And their usable balance has increased by the total amount locked in the merged + // schedules. + assert_eq!(Balances::usable_balance(&2), sched0.locked() + sched1.locked()); + }); +} + +#[test] +fn merge_schedules_throws_proper_errors() { + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + // Account 2 should already have a vesting schedule. + let sched0 = VestingInfo::new::( + ED * 20, + ED, // 20 block duration. + 10, + ); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + + // There is only 1 vesting schedule. + assert_noop!( + Vesting::merge_schedules(Some(2).into(), 0, 1), + Error::::ScheduleIndexOutOfBounds + ); + + // There are 0 vesting schedules. + assert_eq!(Vesting::vesting(&4), None); + assert_noop!(Vesting::merge_schedules(Some(4).into(), 0, 1), Error::::NotVesting); + + // There are enough schedules to merge but an index is non-existent. + Vesting::vested_transfer(Some(3).into(), 2, sched0).unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched0]); + assert_noop!( + Vesting::merge_schedules(Some(2).into(), 0, 2), + Error::::ScheduleIndexOutOfBounds + ); + + // It is a storage noop with no errors if the indexes are the same. + assert_storage_noop!(Vesting::merge_schedules(Some(2).into(), 0, 0).unwrap()); + }); +} + +#[test] +fn generates_multiple_schedules_from_genesis_config() { + let vesting_config = vec![ + // 5 * existential deposit locked + (1, 0, 10, 5 * ED), + // 1 * existential deposit locked + (2, 10, 20, 19 * ED), + // 2 * existential deposit locked + (2, 10, 20, 18 * ED), + // 1 * existential deposit locked + (12, 10, 20, 9 * ED), + // 2 * existential deposit locked + (12, 10, 20, 8 * ED), + // 3 * existential deposit locked + (12, 10, 20, 7 * ED), + ]; + ExtBuilder::default() + .existential_deposit(ED) + .vesting_genesis_config(vesting_config) + .build() + .execute_with(|| { + let user1_sched1 = VestingInfo::new::(5 * ED, 128, 0u64); + assert_eq!(Vesting::vesting(&1).unwrap(), vec![user1_sched1]); + + let user2_sched1 = VestingInfo::new::(1 * ED, 12, 10u64); + let user2_sched2 = VestingInfo::new::(2 * ED, 25, 10u64); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_sched1, user2_sched2]); + + let user12_sched1 = VestingInfo::new::(1 * ED, 12, 10u64); + let user12_sched2 = VestingInfo::new::(2 * ED, 25, 10u64); + let user12_sched3 = VestingInfo::new::(3 * ED, 38, 10u64); + assert_eq!( + Vesting::vesting(&12).unwrap(), + vec![user12_sched1, user12_sched2, user12_sched3] + ); + }); +} + +#[test] +#[should_panic] +fn multiple_schedules_from_genesis_config_errors() { + // MaxVestingSchedules is 3, but this config has 4 for account 12 so we panic when building + // from genesis. + let vesting_config = + vec![(12, 10, 20, ED), (12, 10, 20, ED), (12, 10, 20, ED), (12, 10, 20, ED)]; + ExtBuilder::default() + .existential_deposit(ED) + .vesting_genesis_config(vesting_config) + .build(); +} + +#[test] +fn merge_vesting_info_handles_per_block_0() { + // Faulty schedules with an infinite duration (per_block == 0) can be merged to create + // a schedule that vest 1 per_block, (helpful for faulty, legacy schedules). + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let sched0 = VestingInfo::new::( + ED, 0, // Vesting over infinite blocks. + 1, + ); + let sched1 = VestingInfo::new::( + ED * 2, + 0, // Vesting over infinite blocks. + 10, + ); + + let cur_block = 5; + let expected_locked = + sched0.locked_at::(cur_block) + sched1.locked_at::(cur_block); + let expected_sched = VestingInfo::new::( + expected_locked, + 1, // per_block of 1, which corrects existing schedules. + 10, + ); + assert_eq!(Vesting::merge_vesting_info(cur_block, sched0, sched1), Some(expected_sched),); + }); +} + +#[test] +fn vesting_info_validation_works() { + let min_transfer = ::MinVestedTransfer::get(); + // Does not check for min transfer. + match VestingInfo::new::(min_transfer - 1, 1u64, 10u64).validate::() { + Ok(_) => (), + _ => panic!(), + } + + // `locked` cannot be 0. + match VestingInfo::new::(0, 1u64, 10u64).validate::() { + Err(Error::::InvalidScheduleParams) => (), + _ => panic!(), + } + + // `per_block` cannot be 0. + match VestingInfo::new::(min_transfer + 1, 0u64, 10u64).validate() { + Err(Error::::InvalidScheduleParams) => (), + _ => panic!(), + } + + // With valid inputs it does not error and does not modify the inputs. + assert_eq!( + VestingInfo::new::(min_transfer, 1u64, 10u64).validate::().unwrap(), + VestingInfo::new::(min_transfer, 1u64, 10u64) + ); + + // `per_block` is never bigger than `locked`. + assert_eq!( + VestingInfo::new::(256u64, 256 * 2u64, 10u64).validate::().unwrap(), + VestingInfo::new::(256u64, 256u64, 10u64) + ); +} + +#[test] +fn vesting_info_ending_block_works() { + // Treats `per_block` 0 as a `per_block` of 1 to accommodate faulty, legacy schedules. + let per_block_0 = VestingInfo::new::(256u32, 0u32, 10u32); + assert_eq!( + per_block_0.ending_block::(), + per_block_0.locked() + per_block_0.starting_block() + ); + let per_block_1 = VestingInfo::new::(256u32, 1u32, 10u32); + assert_eq!(per_block_0.ending_block::(), per_block_1.ending_block::()); + + // `per_block >= locked` always results in a schedule ending the block after it starts + let per_block_gt_locked = VestingInfo::new::(256u32, 256 * 2u32, 10u32); + assert_eq!( + per_block_gt_locked.ending_block::(), + 1 + per_block_gt_locked.starting_block() + ); + let per_block_eq_locked = VestingInfo::new::(256u32, 256u32, 10u32); + assert_eq!( + per_block_gt_locked.ending_block::(), + per_block_eq_locked.ending_block::() + ); + + // Correctly calcs end if `locked % per_block != 0`. (We need a block to unlock the remainder). + let imperfect_per_block = VestingInfo::new::(256u32, 250u32, 10u32); + assert_eq!( + imperfect_per_block.ending_block::(), + imperfect_per_block.starting_block() + 2u32, + ); + assert_eq!( + imperfect_per_block.locked_at::(imperfect_per_block.ending_block::()), + 0 + ); +} From 52ae49f2fc108f2563c944b50ee6e159f5ac86c8 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Wed, 30 Jun 2021 10:09:25 -0700 Subject: [PATCH 031/144] Add files forgot to include in merge --- client/allocator/src/error.rs | 5 +- client/allocator/src/freeing_bump.rs | 51 +- client/allocator/src/lib.rs | 2 +- client/chain-spec/src/lib.rs | 33 +- client/executor/runtime-test/src/lib.rs | 644 ++++++++++++------------ client/executor/wasmtime/src/host.rs | 13 +- client/executor/wasmtime/src/runtime.rs | 27 +- client/rpc-api/src/system/helpers.rs | 5 +- 8 files changed, 396 insertions(+), 384 deletions(-) diff --git a/client/allocator/src/error.rs b/client/allocator/src/error.rs index d28484d34f4cd..90986cb94844f 100644 --- a/client/allocator/src/error.rs +++ b/client/allocator/src/error.rs @@ -16,8 +16,7 @@ // limitations under the License. /// The error type used by the allocators. -#[derive(sp_core::RuntimeDebug)] -#[derive(thiserror::Error)] +#[derive(sp_core::RuntimeDebug, thiserror::Error)] pub enum Error { /// Someone tried to allocate more memory than the allowed maximum per allocation. #[error("Requested allocation size is too large")] @@ -27,5 +26,5 @@ pub enum Error { AllocatorOutOfSpace, /// Some other error occurred. #[error("Other: {0}")] - Other(&'static str) + Other(&'static str), } diff --git a/client/allocator/src/freeing_bump.rs b/client/allocator/src/freeing_bump.rs index 3e9b0c9790360..721371411995c 100644 --- a/client/allocator/src/freeing_bump.rs +++ b/client/allocator/src/freeing_bump.rs @@ -66,10 +66,15 @@ //! 75% (`(3N + ε) / 2 / 2N`) meaning that around 25% of the space in allocation will be wasted. //! This is more pronounced (in terms of absolute heap amounts) with larger allocation sizes. -use crate::Error; -use sp_std::{mem, convert::{TryFrom, TryInto}, ops::{Range, Index, IndexMut}}; +use sp_std::{ + convert::{TryFrom, TryInto}, + mem, + ops::{Index, IndexMut, Range}, +}; use sp_wasm_interface::{Pointer, WordSize}; +use crate::Error; + /// The minimal alignment guaranteed by this allocator. /// /// The alignment of 8 is chosen because it is the maximum size of a primitive type supported by the @@ -221,9 +226,8 @@ impl Link { /// | 0 | next element link | /// +--------------+-------------------+ /// ``` -/// +/// /// ## Occupied header -/// /// ```ignore /// 64 32 0 // +--------------+-------------------+ @@ -297,9 +301,7 @@ struct FreeLists { impl FreeLists { /// Creates the free empty lists. fn new() -> Self { - Self { - heads: [Link::Nil; N_ORDERS] - } + Self { heads: [Link::Nil; N_ORDERS] } } /// Replaces a given link for the specified order and returns the old one. @@ -312,6 +314,7 @@ impl FreeLists { impl Index for FreeLists { type Output = Link; + fn index(&self, index: Order) -> &Link { &self.heads[index.0 as usize] } @@ -368,7 +371,7 @@ impl FreeingBumpHeapAllocator { size: WordSize, ) -> Result, Error> { if self.poisoned { - return Err(error("the allocator has been poisoned")) + return Err(error("the allocator has been poisoned")); } let bomb = PoisonBomb { poisoned: &mut self.poisoned }; @@ -392,11 +395,7 @@ impl FreeingBumpHeapAllocator { } Link::Nil => { // Corresponding free list is empty. Allocate a new item. - Self::bump( - &mut self.bumper, - order.size() + HEADER_SIZE, - mem.size(), - )? + Self::bump(&mut self.bumper, order.size() + HEADER_SIZE, mem.size())? } }; @@ -418,9 +417,13 @@ impl FreeingBumpHeapAllocator { /// /// - `mem` - a slice representing the linear memory on which this allocator operates. /// - `ptr` - pointer to the allocated chunk - pub fn deallocate(&mut self, mem: &mut M, ptr: Pointer) -> Result<(), Error> { + pub fn deallocate( + &mut self, + mem: &mut M, + ptr: Pointer, + ) -> Result<(), Error> { if self.poisoned { - return Err(error("the allocator has been poisoned")) + return Err(error("the allocator has been poisoned")); } let bomb = PoisonBomb { poisoned: &mut self.poisoned }; @@ -491,6 +494,7 @@ impl Memory for [u8] { .expect("[u8] slice of length 8 must be convertible to [u8; 8]"); Ok(u64::from_le_bytes(bytes)) } + fn write_le_u64(&mut self, ptr: u32, val: u64) -> Result<(), Error> { let range = heap_range(ptr, 8, self.len()).ok_or_else(|| error("write out of heap bounds"))?; @@ -498,6 +502,7 @@ impl Memory for [u8] { self[range].copy_from_slice(&bytes[..]); Ok(()) } + fn size(&self) -> u32 { u32::try_from(self.len()).expect("size of Wasm linear memory is <2^32; qed") } @@ -507,7 +512,7 @@ fn heap_range(offset: u32, length: u32, heap_len: usize) -> Option> let start = offset as usize; let end = offset.checked_add(length)? as usize; if end <= heap_len { - Some(start..end) + Some(start .. end) } else { None } @@ -677,7 +682,7 @@ mod tests { // then match ptr.unwrap_err() { - Error::AllocatorOutOfSpace => {}, + Error::AllocatorOutOfSpace => {} e => panic!("Expected allocator out of space error, got: {:?}", e), } } @@ -696,7 +701,7 @@ mod tests { // then // there is no room for another half page incl. its 8 byte prefix match ptr2.unwrap_err() { - Error::AllocatorOutOfSpace => {}, + Error::AllocatorOutOfSpace => {} e => panic!("Expected allocator out of space error, got: {:?}", e), } } @@ -725,7 +730,7 @@ mod tests { // then match ptr.unwrap_err() { - Error::RequestedAllocationTooLarge => {}, + Error::RequestedAllocationTooLarge => {} e => panic!("Expected allocation size too large error, got: {:?}", e), } } @@ -757,7 +762,7 @@ mod tests { // then match ptr.unwrap_err() { - Error::AllocatorOutOfSpace => {}, + Error::AllocatorOutOfSpace => {} e => panic!("Expected allocator out of space error, got: {:?}", e), } } @@ -798,7 +803,7 @@ mod tests { let mut heap = FreeingBumpHeapAllocator::new(19); // when - for _ in 1..10 { + for _ in 1 .. 10 { let ptr = heap.allocate(&mut mem[..], 42).unwrap(); heap.deallocate(&mut mem[..], ptr).unwrap(); } @@ -850,11 +855,11 @@ mod tests { let mut heap = FreeingBumpHeapAllocator::new(0); // Allocate and free some pointers - let ptrs = (0..4).map(|_| heap.allocate(&mut mem[..], 8).unwrap()).collect::>(); + let ptrs = (0 .. 4).map(|_| heap.allocate(&mut mem[..], 8).unwrap()).collect::>(); ptrs.into_iter().for_each(|ptr| heap.deallocate(&mut mem[..], ptr).unwrap()); // Second time we should be able to allocate all of them again. - let _ = (0..4).map(|_| heap.allocate(&mut mem[..], 8).unwrap()).collect::>(); + let _ = (0 .. 4).map(|_| heap.allocate(&mut mem[..], 8).unwrap()).collect::>(); } #[test] diff --git a/client/allocator/src/lib.rs b/client/allocator/src/lib.rs index a82c7542199d4..4493db3c7d146 100644 --- a/client/allocator/src/lib.rs +++ b/client/allocator/src/lib.rs @@ -25,5 +25,5 @@ mod error; mod freeing_bump; -pub use freeing_bump::FreeingBumpHeapAllocator; pub use error::Error; +pub use freeing_bump::FreeingBumpHeapAllocator; diff --git a/client/chain-spec/src/lib.rs b/client/chain-spec/src/lib.rs index 1bfa1808ee556..fb2f35dc41bc4 100644 --- a/client/chain-spec/src/lib.rs +++ b/client/chain-spec/src/lib.rs @@ -35,7 +35,7 @@ //! //! #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ChainSpecExtension)] //! pub struct MyExtension { -//! pub known_blocks: HashMap, +//! pub known_blocks: HashMap, //! } //! //! pub type MyChainSpec = GenericChainSpec; @@ -53,19 +53,19 @@ //! //! #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ChainSpecGroup)] //! pub struct ClientParams { -//! max_block_size: usize, -//! max_extrinsic_size: usize, +//! max_block_size: usize, +//! max_extrinsic_size: usize, //! } //! //! #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ChainSpecGroup)] //! pub struct PoolParams { -//! max_transaction_size: usize, +//! max_transaction_size: usize, //! } //! //! #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ChainSpecGroup, ChainSpecExtension)] //! pub struct Extension { -//! pub client: ClientParams, -//! pub pool: PoolParams, +//! pub client: ClientParams, +//! pub pool: PoolParams, //! } //! //! pub type BlockNumber = u64; @@ -88,20 +88,20 @@ //! //! #[derive(Clone, Debug, Serialize, Deserialize, ChainSpecGroup)] //! pub struct ClientParams { -//! max_block_size: usize, -//! max_extrinsic_size: usize, +//! max_block_size: usize, +//! max_extrinsic_size: usize, //! } //! //! #[derive(Clone, Debug, Serialize, Deserialize, ChainSpecGroup)] //! pub struct PoolParams { -//! max_transaction_size: usize, +//! max_transaction_size: usize, //! } //! //! #[derive(Clone, Debug, Serialize, Deserialize, ChainSpecExtension)] //! pub struct Extension { -//! pub client: ClientParams, -//! #[forks] -//! pub pool: Forks, +//! pub client: ClientParams, +//! #[forks] +//! pub pool: Forks, //! } //! //! pub type MyChainSpec = GenericChainSpec; @@ -111,16 +111,15 @@ mod chain_spec; mod extension; pub use chain_spec::{ - ChainSpec as GenericChainSpec, NoExtension, LightSyncState, SerializableLightSyncState, + ChainSpec as GenericChainSpec, LightSyncState, NoExtension, SerializableLightSyncState, }; -pub use extension::{Group, Fork, Forks, Extension, GetExtension, get_extension}; +pub use extension::{get_extension, Extension, Fork, Forks, GetExtension, Group}; pub use sc_chain_spec_derive::{ChainSpecExtension, ChainSpecGroup}; - -use serde::{Serialize, de::DeserializeOwned}; -use sp_runtime::BuildStorage; use sc_network::config::MultiaddrWithPeerId; use sc_telemetry::TelemetryEndpoints; +use serde::{de::DeserializeOwned, Serialize}; use sp_core::storage::Storage; +use sp_runtime::BuildStorage; /// The type of a chain. /// diff --git a/client/executor/runtime-test/src/lib.rs b/client/executor/runtime-test/src/lib.rs index af0c9edcc32e2..6d5f2adf09cfe 100644 --- a/client/executor/runtime-test/src/lib.rs +++ b/client/executor/runtime-test/src/lib.rs @@ -48,334 +48,334 @@ static mut MUTABLE_STATIC: u64 = 32; static mut MUTABLE_STATIC_BSS: u64 = 0; sp_core::wasm_export_functions! { - fn test_calling_missing_external() { - unsafe { missing_external() } - } - - fn test_calling_yet_another_missing_external() { - unsafe { yet_another_missing_external() } - } - - fn test_data_in(input: Vec) -> Vec { - print("set_storage"); - storage::set(b"input", &input); - - print("storage"); - let foo = storage::get(b"foo").unwrap(); - - print("set_storage"); - storage::set(b"baz", &foo); - - print("finished!"); - b"all ok!".to_vec() - } - - fn test_clear_prefix(input: Vec) -> Vec { - storage::clear_prefix(&input, None); - b"all ok!".to_vec() - } - - fn test_empty_return() {} - - fn test_dirty_plenty_memory(heap_base: u32, heap_pages: u32) { - // This piece of code will dirty multiple pages of memory. The number of pages is given by - // the `heap_pages`. It's unit is a wasm page (64KiB). The first page to be cleared - // is a wasm page that that follows the one that holds the `heap_base` address. - // - // This function dirties the **host** pages. I.e. we dirty 4KiB at a time and it will take - // 16 writes to process a single wasm page. - - let mut heap_ptr = heap_base as usize; - - // Find the next wasm page boundary. - let heap_ptr = round_up_to(heap_ptr, 65536); - - // Make it an actual pointer - let heap_ptr = heap_ptr as *mut u8; - - // Traverse the host pages and make each one dirty - let host_pages = heap_pages as usize * 16; - for i in 0..host_pages { - unsafe { - // technically this is an UB, but there is no way Rust can find this out. - heap_ptr.add(i * 4096).write(0); - } - } - - fn round_up_to(n: usize, divisor: usize) -> usize { - (n + divisor - 1) / divisor - } - } - - fn test_exhaust_heap() -> Vec { Vec::with_capacity(16777216) } - - fn test_panic() { panic!("test panic") } - - fn test_conditional_panic(input: Vec) -> Vec { - if input.len() > 0 { - panic!("test panic") - } - - input - } - - fn test_blake2_256(input: Vec) -> Vec { - blake2_256(&input).to_vec() - } - - fn test_blake2_128(input: Vec) -> Vec { - blake2_128(&input).to_vec() - } - - fn test_sha2_256(input: Vec) -> Vec { - sha2_256(&input).to_vec() - } - - fn test_twox_256(input: Vec) -> Vec { - twox_256(&input).to_vec() - } - - fn test_twox_128(input: Vec) -> Vec { - twox_128(&input).to_vec() - } - - fn test_ed25519_verify(input: Vec) -> bool { - let mut pubkey = [0; 32]; - let mut sig = [0; 64]; - - pubkey.copy_from_slice(&input[0..32]); - sig.copy_from_slice(&input[32..96]); - - let msg = b"all ok!"; - ed25519_verify(&ed25519::Signature(sig), &msg[..], &ed25519::Public(pubkey)) - } - - fn test_sr25519_verify(input: Vec) -> bool { - let mut pubkey = [0; 32]; - let mut sig = [0; 64]; - - pubkey.copy_from_slice(&input[0..32]); - sig.copy_from_slice(&input[32..96]); - - let msg = b"all ok!"; - sr25519_verify(&sr25519::Signature(sig), &msg[..], &sr25519::Public(pubkey)) - } - - fn test_ordered_trie_root() -> Vec { - BlakeTwo256::ordered_trie_root( - vec![ - b"zero"[..].into(), - b"one"[..].into(), - b"two"[..].into(), - ], - ).as_ref().to_vec() - } - - fn test_sandbox(code: Vec) -> bool { - execute_sandboxed(&code, &[]).is_ok() - } - - fn test_sandbox_args(code: Vec) -> bool { - execute_sandboxed( - &code, - &[ - Value::I32(0x12345678), - Value::I64(0x1234567887654321), - ], - ).is_ok() - } - - fn test_sandbox_return_val(code: Vec) -> bool { - let ok = match execute_sandboxed( - &code, - &[ - Value::I32(0x1336), - ] - ) { - Ok(sp_sandbox::ReturnValue::Value(Value::I32(0x1337))) => true, - _ => false, - }; - - ok - } - - fn test_sandbox_instantiate(code: Vec) -> u8 { - let env_builder = sp_sandbox::EnvironmentDefinitionBuilder::new(); - let code = match sp_sandbox::Instance::new(&code, &env_builder, &mut ()) { - Ok(_) => 0, - Err(sp_sandbox::Error::Module) => 1, - Err(sp_sandbox::Error::Execution) => 2, - Err(sp_sandbox::Error::OutOfBounds) => 3, - }; - - code - } - - fn test_sandbox_get_global_val(code: Vec) -> i64 { - let env_builder = sp_sandbox::EnvironmentDefinitionBuilder::new(); - let instance = if let Ok(i) = sp_sandbox::Instance::new(&code, &env_builder, &mut ()) { - i - } else { - return 20; - }; - - match instance.get_global_val("test_global") { - Some(sp_sandbox::Value::I64(val)) => val, - None => 30, - val => 40, - } - } + fn test_calling_missing_external() { + unsafe { missing_external() } + } - fn test_offchain_index_set() { - sp_io::offchain_index::set(b"k", b"v"); - } - - fn test_offchain_local_storage() -> bool { - let kind = sp_core::offchain::StorageKind::PERSISTENT; - assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), None); - sp_io::offchain::local_storage_set(kind, b"test", b"asd"); - assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), Some(b"asd".to_vec())); - - let res = sp_io::offchain::local_storage_compare_and_set( - kind, - b"test", - Some(b"asd".to_vec()), - b"", - ); - assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), Some(b"".to_vec())); - res - } + fn test_calling_yet_another_missing_external() { + unsafe { yet_another_missing_external() } + } - fn test_offchain_local_storage_with_none() { - let kind = sp_core::offchain::StorageKind::PERSISTENT; - assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), None); + fn test_data_in(input: Vec) -> Vec { + print("set_storage"); + storage::set(b"input", &input); - let res = sp_io::offchain::local_storage_compare_and_set(kind, b"test", None, b"value"); - assert_eq!(res, true); - assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), Some(b"value".to_vec())); - } + print("storage"); + let foo = storage::get(b"foo").unwrap(); - fn test_offchain_http() -> bool { - use sp_core::offchain::HttpRequestStatus; - let run = || -> Option<()> { - let id = sp_io::offchain::http_request_start( - "POST", - "http://localhost:12345", - &[], - ).ok()?; - sp_io::offchain::http_request_add_header(id, "X-Auth", "test").ok()?; - sp_io::offchain::http_request_write_body(id, &[1, 2, 3, 4], None).ok()?; - sp_io::offchain::http_request_write_body(id, &[], None).ok()?; - let status = sp_io::offchain::http_response_wait(&[id], None); - assert!(status == vec![HttpRequestStatus::Finished(200)], "Expected Finished(200) status."); - let headers = sp_io::offchain::http_response_headers(id); - assert_eq!(headers, vec![(b"X-Auth".to_vec(), b"hello".to_vec())]); - let mut buffer = vec![0; 64]; - let read = sp_io::offchain::http_response_read_body(id, &mut buffer, None).ok()?; - assert_eq!(read, 3); - assert_eq!(&buffer[0..read as usize], &[1, 2, 3]); - let read = sp_io::offchain::http_response_read_body(id, &mut buffer, None).ok()?; - assert_eq!(read, 0); - - Some(()) - }; - - run().is_some() - } + print("set_storage"); + storage::set(b"baz", &foo); - fn test_enter_span() -> u64 { - wasm_tracing::enter_span(Default::default()) - } - - fn test_exit_span(span_id: u64) { - wasm_tracing::exit(span_id) - } - - fn test_nested_spans() { - sp_io::init_tracing(); - let span_id = wasm_tracing::enter_span(Default::default()); - { - sp_io::init_tracing(); - let span_id = wasm_tracing::enter_span(Default::default()); - wasm_tracing::exit(span_id); - } - wasm_tracing::exit(span_id); - } + print("finished!"); + b"all ok!".to_vec() + } + + fn test_clear_prefix(input: Vec) -> Vec { + storage::clear_prefix(&input, None); + b"all ok!".to_vec() + } - fn returns_mutable_static() -> u64 { - unsafe { - MUTABLE_STATIC += 1; - MUTABLE_STATIC - } - } - - fn returns_mutable_static_bss() -> u64 { - unsafe { - MUTABLE_STATIC_BSS += 1; - MUTABLE_STATIC_BSS - } - } - - fn allocates_huge_stack_array(trap: bool) -> Vec { - // Allocate a stack frame that is approx. 75% of the stack (assuming it is 1MB). - // This will just decrease (stacks in wasm32-u-u grow downwards) the stack - // pointer. This won't trap on the current compilers. - let mut data = [0u8; 1024 * 768]; - - // Then make sure we actually write something to it. - // - // If: - // 1. the stack area is placed at the beginning of the linear memory space, and - // 2. the stack pointer points to out-of-bounds area, and - // 3. a write is performed around the current stack pointer. - // - // then a trap should happen. - // - for (i, v) in data.iter_mut().enumerate() { - *v = i as u8; // deliberate truncation - } - - if trap { - // There is a small chance of this to be pulled up in theory. In practice - // the probability of that is rather low. - panic!() - } - - data.to_vec() - } - - // Check that the heap at `heap_base + offset` don't contains the test message. - // After the check succeeds the test message is written into the heap. - // - // It is expected that the given pointer is not allocated. - fn check_and_set_in_heap(heap_base: u32, offset: u32) { - let test_message = b"Hello invalid heap memory"; - let ptr = unsafe { (heap_base + offset) as *mut u8 }; - - let message_slice = unsafe { sp_std::slice::from_raw_parts_mut(ptr, test_message.len()) }; - - assert_ne!(test_message, message_slice); - message_slice.copy_from_slice(test_message); - } - - fn test_spawn() { - let data = vec![1u8, 2u8]; - let data_new = sp_tasks::spawn(tasks::incrementer, data).join(); - - assert_eq!(data_new, vec![2u8, 3u8]); - } - - fn test_nested_spawn() { - let data = vec![7u8, 13u8]; - let data_new = sp_tasks::spawn(tasks::parallel_incrementer, data).join(); - - assert_eq!(data_new, vec![10u8, 16u8]); - } - - fn test_panic_in_spawned() { - sp_tasks::spawn(tasks::panicker, vec![]).join(); - } - } + fn test_empty_return() {} + + fn test_dirty_plenty_memory(heap_base: u32, heap_pages: u32) { + // This piece of code will dirty multiple pages of memory. The number of pages is given by + // the `heap_pages`. It's unit is a wasm page (64KiB). The first page to be cleared + // is a wasm page that that follows the one that holds the `heap_base` address. + // + // This function dirties the **host** pages. I.e. we dirty 4KiB at a time and it will take + // 16 writes to process a single wasm page. + + let mut heap_ptr = heap_base as usize; + + // Find the next wasm page boundary. + let heap_ptr = round_up_to(heap_ptr, 65536); + + // Make it an actual pointer + let heap_ptr = heap_ptr as *mut u8; + + // Traverse the host pages and make each one dirty + let host_pages = heap_pages as usize * 16; + for i in 0..host_pages { + unsafe { + // technically this is an UB, but there is no way Rust can find this out. + heap_ptr.add(i * 4096).write(0); + } + } + + fn round_up_to(n: usize, divisor: usize) -> usize { + (n + divisor - 1) / divisor + } + } + + fn test_exhaust_heap() -> Vec { Vec::with_capacity(16777216) } + + fn test_panic() { panic!("test panic") } + + fn test_conditional_panic(input: Vec) -> Vec { + if input.len() > 0 { + panic!("test panic") + } + + input + } + + fn test_blake2_256(input: Vec) -> Vec { + blake2_256(&input).to_vec() + } + + fn test_blake2_128(input: Vec) -> Vec { + blake2_128(&input).to_vec() + } + + fn test_sha2_256(input: Vec) -> Vec { + sha2_256(&input).to_vec() + } + + fn test_twox_256(input: Vec) -> Vec { + twox_256(&input).to_vec() + } + + fn test_twox_128(input: Vec) -> Vec { + twox_128(&input).to_vec() + } + + fn test_ed25519_verify(input: Vec) -> bool { + let mut pubkey = [0; 32]; + let mut sig = [0; 64]; + + pubkey.copy_from_slice(&input[0..32]); + sig.copy_from_slice(&input[32..96]); + + let msg = b"all ok!"; + ed25519_verify(&ed25519::Signature(sig), &msg[..], &ed25519::Public(pubkey)) + } + + fn test_sr25519_verify(input: Vec) -> bool { + let mut pubkey = [0; 32]; + let mut sig = [0; 64]; + + pubkey.copy_from_slice(&input[0..32]); + sig.copy_from_slice(&input[32..96]); + + let msg = b"all ok!"; + sr25519_verify(&sr25519::Signature(sig), &msg[..], &sr25519::Public(pubkey)) + } + + fn test_ordered_trie_root() -> Vec { + BlakeTwo256::ordered_trie_root( + vec![ + b"zero"[..].into(), + b"one"[..].into(), + b"two"[..].into(), + ], + ).as_ref().to_vec() + } + + fn test_sandbox(code: Vec) -> bool { + execute_sandboxed(&code, &[]).is_ok() + } + + fn test_sandbox_args(code: Vec) -> bool { + execute_sandboxed( + &code, + &[ + Value::I32(0x12345678), + Value::I64(0x1234567887654321), + ], + ).is_ok() + } + + fn test_sandbox_return_val(code: Vec) -> bool { + let ok = match execute_sandboxed( + &code, + &[ + Value::I32(0x1336), + ] + ) { + Ok(sp_sandbox::ReturnValue::Value(Value::I32(0x1337))) => true, + _ => false, + }; + + ok + } + + fn test_sandbox_instantiate(code: Vec) -> u8 { + let env_builder = sp_sandbox::EnvironmentDefinitionBuilder::new(); + let code = match sp_sandbox::Instance::new(&code, &env_builder, &mut ()) { + Ok(_) => 0, + Err(sp_sandbox::Error::Module) => 1, + Err(sp_sandbox::Error::Execution) => 2, + Err(sp_sandbox::Error::OutOfBounds) => 3, + }; + + code + } + + fn test_sandbox_get_global_val(code: Vec) -> i64 { + let env_builder = sp_sandbox::EnvironmentDefinitionBuilder::new(); + let instance = if let Ok(i) = sp_sandbox::Instance::new(&code, &env_builder, &mut ()) { + i + } else { + return 20; + }; + + match instance.get_global_val("test_global") { + Some(sp_sandbox::Value::I64(val)) => val, + None => 30, + val => 40, + } + } + + fn test_offchain_index_set() { + sp_io::offchain_index::set(b"k", b"v"); + } + + fn test_offchain_local_storage() -> bool { + let kind = sp_core::offchain::StorageKind::PERSISTENT; + assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), None); + sp_io::offchain::local_storage_set(kind, b"test", b"asd"); + assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), Some(b"asd".to_vec())); + + let res = sp_io::offchain::local_storage_compare_and_set( + kind, + b"test", + Some(b"asd".to_vec()), + b"", + ); + assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), Some(b"".to_vec())); + res + } + + fn test_offchain_local_storage_with_none() { + let kind = sp_core::offchain::StorageKind::PERSISTENT; + assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), None); + + let res = sp_io::offchain::local_storage_compare_and_set(kind, b"test", None, b"value"); + assert_eq!(res, true); + assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), Some(b"value".to_vec())); + } + + fn test_offchain_http() -> bool { + use sp_core::offchain::HttpRequestStatus; + let run = || -> Option<()> { + let id = sp_io::offchain::http_request_start( + "POST", + "http://localhost:12345", + &[], + ).ok()?; + sp_io::offchain::http_request_add_header(id, "X-Auth", "test").ok()?; + sp_io::offchain::http_request_write_body(id, &[1, 2, 3, 4], None).ok()?; + sp_io::offchain::http_request_write_body(id, &[], None).ok()?; + let status = sp_io::offchain::http_response_wait(&[id], None); + assert!(status == vec![HttpRequestStatus::Finished(200)], "Expected Finished(200) status."); + let headers = sp_io::offchain::http_response_headers(id); + assert_eq!(headers, vec![(b"X-Auth".to_vec(), b"hello".to_vec())]); + let mut buffer = vec![0; 64]; + let read = sp_io::offchain::http_response_read_body(id, &mut buffer, None).ok()?; + assert_eq!(read, 3); + assert_eq!(&buffer[0..read as usize], &[1, 2, 3]); + let read = sp_io::offchain::http_response_read_body(id, &mut buffer, None).ok()?; + assert_eq!(read, 0); + + Some(()) + }; + + run().is_some() + } + + fn test_enter_span() -> u64 { + wasm_tracing::enter_span(Default::default()) + } + + fn test_exit_span(span_id: u64) { + wasm_tracing::exit(span_id) + } + + fn test_nested_spans() { + sp_io::init_tracing(); + let span_id = wasm_tracing::enter_span(Default::default()); + { + sp_io::init_tracing(); + let span_id = wasm_tracing::enter_span(Default::default()); + wasm_tracing::exit(span_id); + } + wasm_tracing::exit(span_id); + } + + fn returns_mutable_static() -> u64 { + unsafe { + MUTABLE_STATIC += 1; + MUTABLE_STATIC + } + } + + fn returns_mutable_static_bss() -> u64 { + unsafe { + MUTABLE_STATIC_BSS += 1; + MUTABLE_STATIC_BSS + } + } + + fn allocates_huge_stack_array(trap: bool) -> Vec { + // Allocate a stack frame that is approx. 75% of the stack (assuming it is 1MB). + // This will just decrease (stacks in wasm32-u-u grow downwards) the stack + // pointer. This won't trap on the current compilers. + let mut data = [0u8; 1024 * 768]; + + // Then make sure we actually write something to it. + // + // If: + // 1. the stack area is placed at the beginning of the linear memory space, and + // 2. the stack pointer points to out-of-bounds area, and + // 3. a write is performed around the current stack pointer. + // + // then a trap should happen. + // + for (i, v) in data.iter_mut().enumerate() { + *v = i as u8; // deliberate truncation + } + + if trap { + // There is a small chance of this to be pulled up in theory. In practice + // the probability of that is rather low. + panic!() + } + + data.to_vec() + } + + // Check that the heap at `heap_base + offset` don't contains the test message. + // After the check succeeds the test message is written into the heap. + // + // It is expected that the given pointer is not allocated. + fn check_and_set_in_heap(heap_base: u32, offset: u32) { + let test_message = b"Hello invalid heap memory"; + let ptr = unsafe { (heap_base + offset) as *mut u8 }; + + let message_slice = unsafe { sp_std::slice::from_raw_parts_mut(ptr, test_message.len()) }; + + assert_ne!(test_message, message_slice); + message_slice.copy_from_slice(test_message); + } + + fn test_spawn() { + let data = vec![1u8, 2u8]; + let data_new = sp_tasks::spawn(tasks::incrementer, data).join(); + + assert_eq!(data_new, vec![2u8, 3u8]); + } + + fn test_nested_spawn() { + let data = vec![7u8, 13u8]; + let data_new = sp_tasks::spawn(tasks::parallel_incrementer, data).join(); + + assert_eq!(data_new, vec![10u8, 16u8]); + } + + fn test_panic_in_spawned() { + sp_tasks::spawn(tasks::panicker, vec![]).join(); + } +} #[cfg(not(feature = "std"))] mod tasks { diff --git a/client/executor/wasmtime/src/host.rs b/client/executor/wasmtime/src/host.rs index 3f5ac0560a6d7..7e8afa6c43e8c 100644 --- a/client/executor/wasmtime/src/host.rs +++ b/client/executor/wasmtime/src/host.rs @@ -19,18 +19,21 @@ //! This module defines `HostState` and `HostContext` structs which provide logic and state //! required for execution of host. -use crate::instance_wrapper::InstanceWrapper; -use crate::util; use std::{cell::RefCell, rc::Rc}; + +use codec::{Decode, Encode}; use log::trace; -use codec::{Encode, Decode}; use sc_allocator::FreeingBumpHeapAllocator; -use sc_executor_common::error::Result; -use sc_executor_common::sandbox::{self, SandboxCapabilities, SupervisorFuncIndex}; +use sc_executor_common::{ + error::Result, + sandbox::{self, SandboxCapabilities, SupervisorFuncIndex}, +}; use sp_core::sandbox as sandbox_primitives; use sp_wasm_interface::{FunctionContext, MemoryId, Pointer, Sandbox, WordSize}; use wasmtime::{Func, Val}; +use crate::{instance_wrapper::InstanceWrapper, util}; + /// Wrapper type for pointer to a Wasm table entry. /// /// The wrapper type is used to ensure that the function reference is valid as it must be unsafely diff --git a/client/executor/wasmtime/src/runtime.rs b/client/executor/wasmtime/src/runtime.rs index 021377eeb20dc..f540495115484 100644 --- a/client/executor/wasmtime/src/runtime.rs +++ b/client/executor/wasmtime/src/runtime.rs @@ -18,24 +18,29 @@ //! Defines the compiled Wasm runtime that uses Wasmtime internally. -use crate::host::HostState; -use crate::imports::{Imports, resolve_imports}; -use crate::instance_wrapper::{InstanceWrapper, EntryPoint}; -use crate::state_holder; - -use std::{path::PathBuf, rc::Rc}; -use std::sync::Arc; -use std::path::Path; +use std::{ + path::{Path, PathBuf}, + rc::Rc, + sync::Arc, +}; + +use sc_allocator::FreeingBumpHeapAllocator; use sc_executor_common::{ error::{Result, WasmError}, runtime_blob::{DataSegmentsSnapshot, ExposedMutableGlobalsSet, GlobalsSnapshot, RuntimeBlob}, - wasm_runtime::{WasmModule, WasmInstance, InvokeMethod}, + wasm_runtime::{InvokeMethod, WasmInstance, WasmModule}, }; -use sc_allocator::FreeingBumpHeapAllocator; use sp_runtime_interface::unpack_ptr_and_len; -use sp_wasm_interface::{Function, Pointer, WordSize, Value}; +use sp_wasm_interface::{Function, Pointer, Value, WordSize}; use wasmtime::{Engine, Store}; +use crate::{ + host::HostState, + imports::{resolve_imports, Imports}, + instance_wrapper::{EntryPoint, InstanceWrapper}, + state_holder, +}; + enum Strategy { FastInstanceReuse { instance_wrapper: Rc, diff --git a/client/rpc-api/src/system/helpers.rs b/client/rpc-api/src/system/helpers.rs index c8124d9c67526..1b618c9d787ec 100644 --- a/client/rpc-api/src/system/helpers.rs +++ b/client/rpc-api/src/system/helpers.rs @@ -19,8 +19,9 @@ //! Substrate system API helpers. use std::fmt; -use serde::{Serialize, Deserialize}; -use sc_chain_spec::{Properties, ChainType}; + +use sc_chain_spec::{ChainType, Properties}; +use serde::{Deserialize, Serialize}; /// Running node's static details. #[derive(Clone, Debug)] From 14510debbfd07bb4867bccb20187a2599de217d6 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Wed, 30 Jun 2021 10:34:40 -0700 Subject: [PATCH 032/144] revert some accidental changes to merged files --- client/allocator/src/error.rs | 5 +-- client/allocator/src/freeing_bump.rs | 51 +++++++++++++--------------- client/allocator/src/lib.rs | 2 +- client/chain-spec/src/lib.rs | 33 +++++++++--------- frame/vesting/src/lib.rs | 24 ++++--------- frame/vesting/src/tests.rs | 6 ++-- 6 files changed, 54 insertions(+), 67 deletions(-) diff --git a/client/allocator/src/error.rs b/client/allocator/src/error.rs index 90986cb94844f..d28484d34f4cd 100644 --- a/client/allocator/src/error.rs +++ b/client/allocator/src/error.rs @@ -16,7 +16,8 @@ // limitations under the License. /// The error type used by the allocators. -#[derive(sp_core::RuntimeDebug, thiserror::Error)] +#[derive(sp_core::RuntimeDebug)] +#[derive(thiserror::Error)] pub enum Error { /// Someone tried to allocate more memory than the allowed maximum per allocation. #[error("Requested allocation size is too large")] @@ -26,5 +27,5 @@ pub enum Error { AllocatorOutOfSpace, /// Some other error occurred. #[error("Other: {0}")] - Other(&'static str), + Other(&'static str) } diff --git a/client/allocator/src/freeing_bump.rs b/client/allocator/src/freeing_bump.rs index 721371411995c..3e9b0c9790360 100644 --- a/client/allocator/src/freeing_bump.rs +++ b/client/allocator/src/freeing_bump.rs @@ -66,14 +66,9 @@ //! 75% (`(3N + ε) / 2 / 2N`) meaning that around 25% of the space in allocation will be wasted. //! This is more pronounced (in terms of absolute heap amounts) with larger allocation sizes. -use sp_std::{ - convert::{TryFrom, TryInto}, - mem, - ops::{Index, IndexMut, Range}, -}; -use sp_wasm_interface::{Pointer, WordSize}; - use crate::Error; +use sp_std::{mem, convert::{TryFrom, TryInto}, ops::{Range, Index, IndexMut}}; +use sp_wasm_interface::{Pointer, WordSize}; /// The minimal alignment guaranteed by this allocator. /// @@ -226,8 +221,9 @@ impl Link { /// | 0 | next element link | /// +--------------+-------------------+ /// ``` -/// +/// /// ## Occupied header +/// /// ```ignore /// 64 32 0 // +--------------+-------------------+ @@ -301,7 +297,9 @@ struct FreeLists { impl FreeLists { /// Creates the free empty lists. fn new() -> Self { - Self { heads: [Link::Nil; N_ORDERS] } + Self { + heads: [Link::Nil; N_ORDERS] + } } /// Replaces a given link for the specified order and returns the old one. @@ -314,7 +312,6 @@ impl FreeLists { impl Index for FreeLists { type Output = Link; - fn index(&self, index: Order) -> &Link { &self.heads[index.0 as usize] } @@ -371,7 +368,7 @@ impl FreeingBumpHeapAllocator { size: WordSize, ) -> Result, Error> { if self.poisoned { - return Err(error("the allocator has been poisoned")); + return Err(error("the allocator has been poisoned")) } let bomb = PoisonBomb { poisoned: &mut self.poisoned }; @@ -395,7 +392,11 @@ impl FreeingBumpHeapAllocator { } Link::Nil => { // Corresponding free list is empty. Allocate a new item. - Self::bump(&mut self.bumper, order.size() + HEADER_SIZE, mem.size())? + Self::bump( + &mut self.bumper, + order.size() + HEADER_SIZE, + mem.size(), + )? } }; @@ -417,13 +418,9 @@ impl FreeingBumpHeapAllocator { /// /// - `mem` - a slice representing the linear memory on which this allocator operates. /// - `ptr` - pointer to the allocated chunk - pub fn deallocate( - &mut self, - mem: &mut M, - ptr: Pointer, - ) -> Result<(), Error> { + pub fn deallocate(&mut self, mem: &mut M, ptr: Pointer) -> Result<(), Error> { if self.poisoned { - return Err(error("the allocator has been poisoned")); + return Err(error("the allocator has been poisoned")) } let bomb = PoisonBomb { poisoned: &mut self.poisoned }; @@ -494,7 +491,6 @@ impl Memory for [u8] { .expect("[u8] slice of length 8 must be convertible to [u8; 8]"); Ok(u64::from_le_bytes(bytes)) } - fn write_le_u64(&mut self, ptr: u32, val: u64) -> Result<(), Error> { let range = heap_range(ptr, 8, self.len()).ok_or_else(|| error("write out of heap bounds"))?; @@ -502,7 +498,6 @@ impl Memory for [u8] { self[range].copy_from_slice(&bytes[..]); Ok(()) } - fn size(&self) -> u32 { u32::try_from(self.len()).expect("size of Wasm linear memory is <2^32; qed") } @@ -512,7 +507,7 @@ fn heap_range(offset: u32, length: u32, heap_len: usize) -> Option> let start = offset as usize; let end = offset.checked_add(length)? as usize; if end <= heap_len { - Some(start .. end) + Some(start..end) } else { None } @@ -682,7 +677,7 @@ mod tests { // then match ptr.unwrap_err() { - Error::AllocatorOutOfSpace => {} + Error::AllocatorOutOfSpace => {}, e => panic!("Expected allocator out of space error, got: {:?}", e), } } @@ -701,7 +696,7 @@ mod tests { // then // there is no room for another half page incl. its 8 byte prefix match ptr2.unwrap_err() { - Error::AllocatorOutOfSpace => {} + Error::AllocatorOutOfSpace => {}, e => panic!("Expected allocator out of space error, got: {:?}", e), } } @@ -730,7 +725,7 @@ mod tests { // then match ptr.unwrap_err() { - Error::RequestedAllocationTooLarge => {} + Error::RequestedAllocationTooLarge => {}, e => panic!("Expected allocation size too large error, got: {:?}", e), } } @@ -762,7 +757,7 @@ mod tests { // then match ptr.unwrap_err() { - Error::AllocatorOutOfSpace => {} + Error::AllocatorOutOfSpace => {}, e => panic!("Expected allocator out of space error, got: {:?}", e), } } @@ -803,7 +798,7 @@ mod tests { let mut heap = FreeingBumpHeapAllocator::new(19); // when - for _ in 1 .. 10 { + for _ in 1..10 { let ptr = heap.allocate(&mut mem[..], 42).unwrap(); heap.deallocate(&mut mem[..], ptr).unwrap(); } @@ -855,11 +850,11 @@ mod tests { let mut heap = FreeingBumpHeapAllocator::new(0); // Allocate and free some pointers - let ptrs = (0 .. 4).map(|_| heap.allocate(&mut mem[..], 8).unwrap()).collect::>(); + let ptrs = (0..4).map(|_| heap.allocate(&mut mem[..], 8).unwrap()).collect::>(); ptrs.into_iter().for_each(|ptr| heap.deallocate(&mut mem[..], ptr).unwrap()); // Second time we should be able to allocate all of them again. - let _ = (0 .. 4).map(|_| heap.allocate(&mut mem[..], 8).unwrap()).collect::>(); + let _ = (0..4).map(|_| heap.allocate(&mut mem[..], 8).unwrap()).collect::>(); } #[test] diff --git a/client/allocator/src/lib.rs b/client/allocator/src/lib.rs index 4493db3c7d146..a82c7542199d4 100644 --- a/client/allocator/src/lib.rs +++ b/client/allocator/src/lib.rs @@ -25,5 +25,5 @@ mod error; mod freeing_bump; -pub use error::Error; pub use freeing_bump::FreeingBumpHeapAllocator; +pub use error::Error; diff --git a/client/chain-spec/src/lib.rs b/client/chain-spec/src/lib.rs index fb2f35dc41bc4..1bfa1808ee556 100644 --- a/client/chain-spec/src/lib.rs +++ b/client/chain-spec/src/lib.rs @@ -35,7 +35,7 @@ //! //! #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ChainSpecExtension)] //! pub struct MyExtension { -//! pub known_blocks: HashMap, +//! pub known_blocks: HashMap, //! } //! //! pub type MyChainSpec = GenericChainSpec; @@ -53,19 +53,19 @@ //! //! #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ChainSpecGroup)] //! pub struct ClientParams { -//! max_block_size: usize, -//! max_extrinsic_size: usize, +//! max_block_size: usize, +//! max_extrinsic_size: usize, //! } //! //! #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ChainSpecGroup)] //! pub struct PoolParams { -//! max_transaction_size: usize, +//! max_transaction_size: usize, //! } //! //! #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ChainSpecGroup, ChainSpecExtension)] //! pub struct Extension { -//! pub client: ClientParams, -//! pub pool: PoolParams, +//! pub client: ClientParams, +//! pub pool: PoolParams, //! } //! //! pub type BlockNumber = u64; @@ -88,20 +88,20 @@ //! //! #[derive(Clone, Debug, Serialize, Deserialize, ChainSpecGroup)] //! pub struct ClientParams { -//! max_block_size: usize, -//! max_extrinsic_size: usize, +//! max_block_size: usize, +//! max_extrinsic_size: usize, //! } //! //! #[derive(Clone, Debug, Serialize, Deserialize, ChainSpecGroup)] //! pub struct PoolParams { -//! max_transaction_size: usize, +//! max_transaction_size: usize, //! } //! //! #[derive(Clone, Debug, Serialize, Deserialize, ChainSpecExtension)] //! pub struct Extension { -//! pub client: ClientParams, -//! #[forks] -//! pub pool: Forks, +//! pub client: ClientParams, +//! #[forks] +//! pub pool: Forks, //! } //! //! pub type MyChainSpec = GenericChainSpec; @@ -111,15 +111,16 @@ mod chain_spec; mod extension; pub use chain_spec::{ - ChainSpec as GenericChainSpec, LightSyncState, NoExtension, SerializableLightSyncState, + ChainSpec as GenericChainSpec, NoExtension, LightSyncState, SerializableLightSyncState, }; -pub use extension::{get_extension, Extension, Fork, Forks, GetExtension, Group}; +pub use extension::{Group, Fork, Forks, Extension, GetExtension, get_extension}; pub use sc_chain_spec_derive::{ChainSpecExtension, ChainSpecGroup}; + +use serde::{Serialize, de::DeserializeOwned}; +use sp_runtime::BuildStorage; use sc_network::config::MultiaddrWithPeerId; use sc_telemetry::TelemetryEndpoints; -use serde::{de::DeserializeOwned, Serialize}; use sp_core::storage::Storage; -use sp_runtime::BuildStorage; /// The type of a chain. /// diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 3e31d7ecc4f78..2fba2e6b9515d 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -65,7 +65,7 @@ use frame_system::{ensure_root, ensure_signed, pallet_prelude::*}; pub use pallet::*; use sp_runtime::{ traits::{ - AtLeast32BitUnsigned, CheckedDiv, Convert, MaybeSerializeDeserialize, Saturating, + AtLeast32BitUnsigned, CheckedDiv, Convert, MaybeSerializeDeserialize, One, Saturating, StaticLookup, Zero, }, RuntimeDebug, @@ -121,17 +121,6 @@ mod vesting_info { Ok(self) } - /// Instantiate a new `VestingInfo` without param modification. Useful for - /// mocking bad inputs in testing. - #[cfg(test)] - pub fn unsafe_new( - locked: Balance, - per_block: Balance, - starting_block: BlockNumber, - ) -> VestingInfo { - VestingInfo { locked, per_block, starting_block } - } - /// Locked amount at schedule creation. pub fn locked(&self) -> Balance { self.locked @@ -157,9 +146,10 @@ mod vesting_info { let vested_block_count = n.saturating_sub(self.starting_block); let vested_block_count = BlockNumberToBalance::convert(vested_block_count); // Return amount that is still locked in vesting - vested_block_count.checked_mul(&self.per_block) - .map(|balance| self.locked.saturating_sub(balance)) - .unwrap_or(Zero::zero()) + vested_block_count + .checked_mul(&self.per_block) + .map(|to_unlock| self.locked.saturating_sub(to_unlock)) + .unwrap_or(Zero::zero()) } /// Block number at which the schedule ends. @@ -168,7 +158,7 @@ mod vesting_info { let duration = if self.per_block > self.locked { // If `per_block` is bigger than `locked`, the schedule will end // the block after starting. - 1u32.into() + One::one() } else if self.per_block.is_zero() { // Check for div by 0 errors, which should only be from legacy // vesting schedules since new ones are validated for this. @@ -177,7 +167,7 @@ mod vesting_info { let has_remainder = !(self.locked % self.per_block).is_zero(); let maybe_duration = self.locked / self.per_block; if has_remainder { - maybe_duration + 1u32.into() + maybe_duration + One::one() } else { maybe_duration } diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index e37876b4c5260..d3ae796a8f007 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -298,21 +298,21 @@ fn vested_transfer_correctly_fails() { // Fails due to too low transfer amount. let new_vesting_schedule_too_low = - VestingInfo::unsafe_new(::MinVestedTransfer::get() - 1, 64, 10); + VestingInfo::new::(::MinVestedTransfer::get() - 1, 64, 10); assert_noop!( Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule_too_low), Error::::AmountLow, ); // `per_block` is 0, which would result in a schedule with infinite duration. - let schedule_per_block_0 = VestingInfo::unsafe_new(256, 0, 10); + let schedule_per_block_0 = VestingInfo::new::(256, 0, 10); assert_noop!( Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_per_block_0), Error::::InvalidScheduleParams, ); // `locked` is 0. - let schedule_locked_0 = VestingInfo::unsafe_new(0, 1, 10); + let schedule_locked_0 = VestingInfo::new::(0, 1, 10); assert_noop!( Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), Error::::AmountLow, From 0d28e03b2d4804179f5cd67cb93e5ba6b74ee3e7 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Wed, 30 Jun 2021 10:37:36 -0700 Subject: [PATCH 033/144] Revert remaining accidental file changes --- client/executor/wasmtime/src/runtime.rs | 27 ++++++++++--------------- client/rpc-api/src/system/helpers.rs | 5 ++--- frame/vesting/README.md | 4 ---- 3 files changed, 13 insertions(+), 23 deletions(-) diff --git a/client/executor/wasmtime/src/runtime.rs b/client/executor/wasmtime/src/runtime.rs index f540495115484..021377eeb20dc 100644 --- a/client/executor/wasmtime/src/runtime.rs +++ b/client/executor/wasmtime/src/runtime.rs @@ -18,29 +18,24 @@ //! Defines the compiled Wasm runtime that uses Wasmtime internally. -use std::{ - path::{Path, PathBuf}, - rc::Rc, - sync::Arc, -}; - -use sc_allocator::FreeingBumpHeapAllocator; +use crate::host::HostState; +use crate::imports::{Imports, resolve_imports}; +use crate::instance_wrapper::{InstanceWrapper, EntryPoint}; +use crate::state_holder; + +use std::{path::PathBuf, rc::Rc}; +use std::sync::Arc; +use std::path::Path; use sc_executor_common::{ error::{Result, WasmError}, runtime_blob::{DataSegmentsSnapshot, ExposedMutableGlobalsSet, GlobalsSnapshot, RuntimeBlob}, - wasm_runtime::{InvokeMethod, WasmInstance, WasmModule}, + wasm_runtime::{WasmModule, WasmInstance, InvokeMethod}, }; +use sc_allocator::FreeingBumpHeapAllocator; use sp_runtime_interface::unpack_ptr_and_len; -use sp_wasm_interface::{Function, Pointer, Value, WordSize}; +use sp_wasm_interface::{Function, Pointer, WordSize, Value}; use wasmtime::{Engine, Store}; -use crate::{ - host::HostState, - imports::{resolve_imports, Imports}, - instance_wrapper::{EntryPoint, InstanceWrapper}, - state_holder, -}; - enum Strategy { FastInstanceReuse { instance_wrapper: Rc, diff --git a/client/rpc-api/src/system/helpers.rs b/client/rpc-api/src/system/helpers.rs index 1b618c9d787ec..c8124d9c67526 100644 --- a/client/rpc-api/src/system/helpers.rs +++ b/client/rpc-api/src/system/helpers.rs @@ -19,9 +19,8 @@ //! Substrate system API helpers. use std::fmt; - -use sc_chain_spec::{ChainType, Properties}; -use serde::{Deserialize, Serialize}; +use serde::{Serialize, Deserialize}; +use sc_chain_spec::{Properties, ChainType}; /// Running node's static details. #[derive(Clone, Debug)] diff --git a/frame/vesting/README.md b/frame/vesting/README.md index 11eea72db5b51..c3800eb994d4d 100644 --- a/frame/vesting/README.md +++ b/frame/vesting/README.md @@ -15,8 +15,6 @@ equivalent to the amount remaining to be vested. This is done through a dispatch either `vest` (in typical case where the sender is calling on their own behalf) or `vest_other` in case the sender is calling on another account's behalf. -A user can have multiple vesting schedules, which are capped by an upper bound. -With an upper bound >= 2, users can merge 2 schedules. ## Interface This module implements the `VestingSchedule` trait. @@ -26,8 +24,6 @@ This module implements the `VestingSchedule` trait. - `vest` - Update the lock, reducing it in line with the amount "vested" so far. - `vest_other` - Update the lock of another account, reducing it in line with the amount "vested" so far. -- `merge_schedules` - Merge two vesting schedules together, creating a new vesting schedule that - over the "worst case" duration. [`Call`]: ./enum.Call.html [`Config`]: ./trait.Config.html From 26ae6275d308c58e15608265bac74226000be90c Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Wed, 30 Jun 2021 10:42:53 -0700 Subject: [PATCH 034/144] More revert of accidental file change --- client/executor/runtime-test/src/lib.rs | 644 ++++++++++++------------ client/executor/wasmtime/src/host.rs | 13 +- 2 files changed, 327 insertions(+), 330 deletions(-) diff --git a/client/executor/runtime-test/src/lib.rs b/client/executor/runtime-test/src/lib.rs index 6d5f2adf09cfe..af0c9edcc32e2 100644 --- a/client/executor/runtime-test/src/lib.rs +++ b/client/executor/runtime-test/src/lib.rs @@ -48,334 +48,334 @@ static mut MUTABLE_STATIC: u64 = 32; static mut MUTABLE_STATIC_BSS: u64 = 0; sp_core::wasm_export_functions! { - fn test_calling_missing_external() { - unsafe { missing_external() } - } + fn test_calling_missing_external() { + unsafe { missing_external() } + } - fn test_calling_yet_another_missing_external() { - unsafe { yet_another_missing_external() } - } + fn test_calling_yet_another_missing_external() { + unsafe { yet_another_missing_external() } + } - fn test_data_in(input: Vec) -> Vec { - print("set_storage"); - storage::set(b"input", &input); + fn test_data_in(input: Vec) -> Vec { + print("set_storage"); + storage::set(b"input", &input); - print("storage"); - let foo = storage::get(b"foo").unwrap(); + print("storage"); + let foo = storage::get(b"foo").unwrap(); - print("set_storage"); - storage::set(b"baz", &foo); + print("set_storage"); + storage::set(b"baz", &foo); - print("finished!"); - b"all ok!".to_vec() - } - - fn test_clear_prefix(input: Vec) -> Vec { - storage::clear_prefix(&input, None); - b"all ok!".to_vec() - } + print("finished!"); + b"all ok!".to_vec() + } - fn test_empty_return() {} - - fn test_dirty_plenty_memory(heap_base: u32, heap_pages: u32) { - // This piece of code will dirty multiple pages of memory. The number of pages is given by - // the `heap_pages`. It's unit is a wasm page (64KiB). The first page to be cleared - // is a wasm page that that follows the one that holds the `heap_base` address. - // - // This function dirties the **host** pages. I.e. we dirty 4KiB at a time and it will take - // 16 writes to process a single wasm page. - - let mut heap_ptr = heap_base as usize; - - // Find the next wasm page boundary. - let heap_ptr = round_up_to(heap_ptr, 65536); - - // Make it an actual pointer - let heap_ptr = heap_ptr as *mut u8; - - // Traverse the host pages and make each one dirty - let host_pages = heap_pages as usize * 16; - for i in 0..host_pages { - unsafe { - // technically this is an UB, but there is no way Rust can find this out. - heap_ptr.add(i * 4096).write(0); - } - } - - fn round_up_to(n: usize, divisor: usize) -> usize { - (n + divisor - 1) / divisor - } - } - - fn test_exhaust_heap() -> Vec { Vec::with_capacity(16777216) } - - fn test_panic() { panic!("test panic") } - - fn test_conditional_panic(input: Vec) -> Vec { - if input.len() > 0 { - panic!("test panic") - } - - input - } - - fn test_blake2_256(input: Vec) -> Vec { - blake2_256(&input).to_vec() - } - - fn test_blake2_128(input: Vec) -> Vec { - blake2_128(&input).to_vec() - } - - fn test_sha2_256(input: Vec) -> Vec { - sha2_256(&input).to_vec() - } - - fn test_twox_256(input: Vec) -> Vec { - twox_256(&input).to_vec() - } - - fn test_twox_128(input: Vec) -> Vec { - twox_128(&input).to_vec() - } - - fn test_ed25519_verify(input: Vec) -> bool { - let mut pubkey = [0; 32]; - let mut sig = [0; 64]; - - pubkey.copy_from_slice(&input[0..32]); - sig.copy_from_slice(&input[32..96]); - - let msg = b"all ok!"; - ed25519_verify(&ed25519::Signature(sig), &msg[..], &ed25519::Public(pubkey)) - } - - fn test_sr25519_verify(input: Vec) -> bool { - let mut pubkey = [0; 32]; - let mut sig = [0; 64]; - - pubkey.copy_from_slice(&input[0..32]); - sig.copy_from_slice(&input[32..96]); - - let msg = b"all ok!"; - sr25519_verify(&sr25519::Signature(sig), &msg[..], &sr25519::Public(pubkey)) - } - - fn test_ordered_trie_root() -> Vec { - BlakeTwo256::ordered_trie_root( - vec![ - b"zero"[..].into(), - b"one"[..].into(), - b"two"[..].into(), - ], - ).as_ref().to_vec() - } - - fn test_sandbox(code: Vec) -> bool { - execute_sandboxed(&code, &[]).is_ok() - } - - fn test_sandbox_args(code: Vec) -> bool { - execute_sandboxed( - &code, - &[ - Value::I32(0x12345678), - Value::I64(0x1234567887654321), - ], - ).is_ok() - } - - fn test_sandbox_return_val(code: Vec) -> bool { - let ok = match execute_sandboxed( - &code, - &[ - Value::I32(0x1336), - ] - ) { - Ok(sp_sandbox::ReturnValue::Value(Value::I32(0x1337))) => true, - _ => false, - }; - - ok - } - - fn test_sandbox_instantiate(code: Vec) -> u8 { - let env_builder = sp_sandbox::EnvironmentDefinitionBuilder::new(); - let code = match sp_sandbox::Instance::new(&code, &env_builder, &mut ()) { - Ok(_) => 0, - Err(sp_sandbox::Error::Module) => 1, - Err(sp_sandbox::Error::Execution) => 2, - Err(sp_sandbox::Error::OutOfBounds) => 3, - }; - - code - } - - fn test_sandbox_get_global_val(code: Vec) -> i64 { - let env_builder = sp_sandbox::EnvironmentDefinitionBuilder::new(); - let instance = if let Ok(i) = sp_sandbox::Instance::new(&code, &env_builder, &mut ()) { - i - } else { - return 20; - }; - - match instance.get_global_val("test_global") { - Some(sp_sandbox::Value::I64(val)) => val, - None => 30, - val => 40, - } - } - - fn test_offchain_index_set() { - sp_io::offchain_index::set(b"k", b"v"); - } - - fn test_offchain_local_storage() -> bool { - let kind = sp_core::offchain::StorageKind::PERSISTENT; - assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), None); - sp_io::offchain::local_storage_set(kind, b"test", b"asd"); - assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), Some(b"asd".to_vec())); - - let res = sp_io::offchain::local_storage_compare_and_set( - kind, - b"test", - Some(b"asd".to_vec()), - b"", - ); - assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), Some(b"".to_vec())); - res - } - - fn test_offchain_local_storage_with_none() { - let kind = sp_core::offchain::StorageKind::PERSISTENT; - assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), None); - - let res = sp_io::offchain::local_storage_compare_and_set(kind, b"test", None, b"value"); - assert_eq!(res, true); - assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), Some(b"value".to_vec())); - } - - fn test_offchain_http() -> bool { - use sp_core::offchain::HttpRequestStatus; - let run = || -> Option<()> { - let id = sp_io::offchain::http_request_start( - "POST", - "http://localhost:12345", - &[], - ).ok()?; - sp_io::offchain::http_request_add_header(id, "X-Auth", "test").ok()?; - sp_io::offchain::http_request_write_body(id, &[1, 2, 3, 4], None).ok()?; - sp_io::offchain::http_request_write_body(id, &[], None).ok()?; - let status = sp_io::offchain::http_response_wait(&[id], None); - assert!(status == vec![HttpRequestStatus::Finished(200)], "Expected Finished(200) status."); - let headers = sp_io::offchain::http_response_headers(id); - assert_eq!(headers, vec![(b"X-Auth".to_vec(), b"hello".to_vec())]); - let mut buffer = vec![0; 64]; - let read = sp_io::offchain::http_response_read_body(id, &mut buffer, None).ok()?; - assert_eq!(read, 3); - assert_eq!(&buffer[0..read as usize], &[1, 2, 3]); - let read = sp_io::offchain::http_response_read_body(id, &mut buffer, None).ok()?; - assert_eq!(read, 0); - - Some(()) - }; - - run().is_some() - } - - fn test_enter_span() -> u64 { - wasm_tracing::enter_span(Default::default()) - } - - fn test_exit_span(span_id: u64) { - wasm_tracing::exit(span_id) - } - - fn test_nested_spans() { - sp_io::init_tracing(); - let span_id = wasm_tracing::enter_span(Default::default()); - { - sp_io::init_tracing(); - let span_id = wasm_tracing::enter_span(Default::default()); - wasm_tracing::exit(span_id); - } - wasm_tracing::exit(span_id); - } - - fn returns_mutable_static() -> u64 { - unsafe { - MUTABLE_STATIC += 1; - MUTABLE_STATIC - } - } - - fn returns_mutable_static_bss() -> u64 { - unsafe { - MUTABLE_STATIC_BSS += 1; - MUTABLE_STATIC_BSS - } - } - - fn allocates_huge_stack_array(trap: bool) -> Vec { - // Allocate a stack frame that is approx. 75% of the stack (assuming it is 1MB). - // This will just decrease (stacks in wasm32-u-u grow downwards) the stack - // pointer. This won't trap on the current compilers. - let mut data = [0u8; 1024 * 768]; - - // Then make sure we actually write something to it. - // - // If: - // 1. the stack area is placed at the beginning of the linear memory space, and - // 2. the stack pointer points to out-of-bounds area, and - // 3. a write is performed around the current stack pointer. - // - // then a trap should happen. - // - for (i, v) in data.iter_mut().enumerate() { - *v = i as u8; // deliberate truncation - } - - if trap { - // There is a small chance of this to be pulled up in theory. In practice - // the probability of that is rather low. - panic!() - } - - data.to_vec() - } - - // Check that the heap at `heap_base + offset` don't contains the test message. - // After the check succeeds the test message is written into the heap. - // - // It is expected that the given pointer is not allocated. - fn check_and_set_in_heap(heap_base: u32, offset: u32) { - let test_message = b"Hello invalid heap memory"; - let ptr = unsafe { (heap_base + offset) as *mut u8 }; - - let message_slice = unsafe { sp_std::slice::from_raw_parts_mut(ptr, test_message.len()) }; - - assert_ne!(test_message, message_slice); - message_slice.copy_from_slice(test_message); - } - - fn test_spawn() { - let data = vec![1u8, 2u8]; - let data_new = sp_tasks::spawn(tasks::incrementer, data).join(); - - assert_eq!(data_new, vec![2u8, 3u8]); - } - - fn test_nested_spawn() { - let data = vec![7u8, 13u8]; - let data_new = sp_tasks::spawn(tasks::parallel_incrementer, data).join(); - - assert_eq!(data_new, vec![10u8, 16u8]); - } - - fn test_panic_in_spawned() { - sp_tasks::spawn(tasks::panicker, vec![]).join(); - } -} + fn test_clear_prefix(input: Vec) -> Vec { + storage::clear_prefix(&input, None); + b"all ok!".to_vec() + } + + fn test_empty_return() {} + + fn test_dirty_plenty_memory(heap_base: u32, heap_pages: u32) { + // This piece of code will dirty multiple pages of memory. The number of pages is given by + // the `heap_pages`. It's unit is a wasm page (64KiB). The first page to be cleared + // is a wasm page that that follows the one that holds the `heap_base` address. + // + // This function dirties the **host** pages. I.e. we dirty 4KiB at a time and it will take + // 16 writes to process a single wasm page. + + let mut heap_ptr = heap_base as usize; + + // Find the next wasm page boundary. + let heap_ptr = round_up_to(heap_ptr, 65536); + + // Make it an actual pointer + let heap_ptr = heap_ptr as *mut u8; + + // Traverse the host pages and make each one dirty + let host_pages = heap_pages as usize * 16; + for i in 0..host_pages { + unsafe { + // technically this is an UB, but there is no way Rust can find this out. + heap_ptr.add(i * 4096).write(0); + } + } + + fn round_up_to(n: usize, divisor: usize) -> usize { + (n + divisor - 1) / divisor + } + } + + fn test_exhaust_heap() -> Vec { Vec::with_capacity(16777216) } + + fn test_panic() { panic!("test panic") } + + fn test_conditional_panic(input: Vec) -> Vec { + if input.len() > 0 { + panic!("test panic") + } + + input + } + + fn test_blake2_256(input: Vec) -> Vec { + blake2_256(&input).to_vec() + } + + fn test_blake2_128(input: Vec) -> Vec { + blake2_128(&input).to_vec() + } + + fn test_sha2_256(input: Vec) -> Vec { + sha2_256(&input).to_vec() + } + + fn test_twox_256(input: Vec) -> Vec { + twox_256(&input).to_vec() + } + + fn test_twox_128(input: Vec) -> Vec { + twox_128(&input).to_vec() + } + + fn test_ed25519_verify(input: Vec) -> bool { + let mut pubkey = [0; 32]; + let mut sig = [0; 64]; + + pubkey.copy_from_slice(&input[0..32]); + sig.copy_from_slice(&input[32..96]); + + let msg = b"all ok!"; + ed25519_verify(&ed25519::Signature(sig), &msg[..], &ed25519::Public(pubkey)) + } + + fn test_sr25519_verify(input: Vec) -> bool { + let mut pubkey = [0; 32]; + let mut sig = [0; 64]; + + pubkey.copy_from_slice(&input[0..32]); + sig.copy_from_slice(&input[32..96]); + + let msg = b"all ok!"; + sr25519_verify(&sr25519::Signature(sig), &msg[..], &sr25519::Public(pubkey)) + } + + fn test_ordered_trie_root() -> Vec { + BlakeTwo256::ordered_trie_root( + vec![ + b"zero"[..].into(), + b"one"[..].into(), + b"two"[..].into(), + ], + ).as_ref().to_vec() + } + + fn test_sandbox(code: Vec) -> bool { + execute_sandboxed(&code, &[]).is_ok() + } + + fn test_sandbox_args(code: Vec) -> bool { + execute_sandboxed( + &code, + &[ + Value::I32(0x12345678), + Value::I64(0x1234567887654321), + ], + ).is_ok() + } + + fn test_sandbox_return_val(code: Vec) -> bool { + let ok = match execute_sandboxed( + &code, + &[ + Value::I32(0x1336), + ] + ) { + Ok(sp_sandbox::ReturnValue::Value(Value::I32(0x1337))) => true, + _ => false, + }; + + ok + } + + fn test_sandbox_instantiate(code: Vec) -> u8 { + let env_builder = sp_sandbox::EnvironmentDefinitionBuilder::new(); + let code = match sp_sandbox::Instance::new(&code, &env_builder, &mut ()) { + Ok(_) => 0, + Err(sp_sandbox::Error::Module) => 1, + Err(sp_sandbox::Error::Execution) => 2, + Err(sp_sandbox::Error::OutOfBounds) => 3, + }; + + code + } + + fn test_sandbox_get_global_val(code: Vec) -> i64 { + let env_builder = sp_sandbox::EnvironmentDefinitionBuilder::new(); + let instance = if let Ok(i) = sp_sandbox::Instance::new(&code, &env_builder, &mut ()) { + i + } else { + return 20; + }; + + match instance.get_global_val("test_global") { + Some(sp_sandbox::Value::I64(val)) => val, + None => 30, + val => 40, + } + } + + fn test_offchain_index_set() { + sp_io::offchain_index::set(b"k", b"v"); + } + + fn test_offchain_local_storage() -> bool { + let kind = sp_core::offchain::StorageKind::PERSISTENT; + assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), None); + sp_io::offchain::local_storage_set(kind, b"test", b"asd"); + assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), Some(b"asd".to_vec())); + + let res = sp_io::offchain::local_storage_compare_and_set( + kind, + b"test", + Some(b"asd".to_vec()), + b"", + ); + assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), Some(b"".to_vec())); + res + } + + fn test_offchain_local_storage_with_none() { + let kind = sp_core::offchain::StorageKind::PERSISTENT; + assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), None); + + let res = sp_io::offchain::local_storage_compare_and_set(kind, b"test", None, b"value"); + assert_eq!(res, true); + assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), Some(b"value".to_vec())); + } + + fn test_offchain_http() -> bool { + use sp_core::offchain::HttpRequestStatus; + let run = || -> Option<()> { + let id = sp_io::offchain::http_request_start( + "POST", + "http://localhost:12345", + &[], + ).ok()?; + sp_io::offchain::http_request_add_header(id, "X-Auth", "test").ok()?; + sp_io::offchain::http_request_write_body(id, &[1, 2, 3, 4], None).ok()?; + sp_io::offchain::http_request_write_body(id, &[], None).ok()?; + let status = sp_io::offchain::http_response_wait(&[id], None); + assert!(status == vec![HttpRequestStatus::Finished(200)], "Expected Finished(200) status."); + let headers = sp_io::offchain::http_response_headers(id); + assert_eq!(headers, vec![(b"X-Auth".to_vec(), b"hello".to_vec())]); + let mut buffer = vec![0; 64]; + let read = sp_io::offchain::http_response_read_body(id, &mut buffer, None).ok()?; + assert_eq!(read, 3); + assert_eq!(&buffer[0..read as usize], &[1, 2, 3]); + let read = sp_io::offchain::http_response_read_body(id, &mut buffer, None).ok()?; + assert_eq!(read, 0); + + Some(()) + }; + + run().is_some() + } + + fn test_enter_span() -> u64 { + wasm_tracing::enter_span(Default::default()) + } + + fn test_exit_span(span_id: u64) { + wasm_tracing::exit(span_id) + } + + fn test_nested_spans() { + sp_io::init_tracing(); + let span_id = wasm_tracing::enter_span(Default::default()); + { + sp_io::init_tracing(); + let span_id = wasm_tracing::enter_span(Default::default()); + wasm_tracing::exit(span_id); + } + wasm_tracing::exit(span_id); + } + + fn returns_mutable_static() -> u64 { + unsafe { + MUTABLE_STATIC += 1; + MUTABLE_STATIC + } + } + + fn returns_mutable_static_bss() -> u64 { + unsafe { + MUTABLE_STATIC_BSS += 1; + MUTABLE_STATIC_BSS + } + } + + fn allocates_huge_stack_array(trap: bool) -> Vec { + // Allocate a stack frame that is approx. 75% of the stack (assuming it is 1MB). + // This will just decrease (stacks in wasm32-u-u grow downwards) the stack + // pointer. This won't trap on the current compilers. + let mut data = [0u8; 1024 * 768]; + + // Then make sure we actually write something to it. + // + // If: + // 1. the stack area is placed at the beginning of the linear memory space, and + // 2. the stack pointer points to out-of-bounds area, and + // 3. a write is performed around the current stack pointer. + // + // then a trap should happen. + // + for (i, v) in data.iter_mut().enumerate() { + *v = i as u8; // deliberate truncation + } + + if trap { + // There is a small chance of this to be pulled up in theory. In practice + // the probability of that is rather low. + panic!() + } + + data.to_vec() + } + + // Check that the heap at `heap_base + offset` don't contains the test message. + // After the check succeeds the test message is written into the heap. + // + // It is expected that the given pointer is not allocated. + fn check_and_set_in_heap(heap_base: u32, offset: u32) { + let test_message = b"Hello invalid heap memory"; + let ptr = unsafe { (heap_base + offset) as *mut u8 }; + + let message_slice = unsafe { sp_std::slice::from_raw_parts_mut(ptr, test_message.len()) }; + + assert_ne!(test_message, message_slice); + message_slice.copy_from_slice(test_message); + } + + fn test_spawn() { + let data = vec![1u8, 2u8]; + let data_new = sp_tasks::spawn(tasks::incrementer, data).join(); + + assert_eq!(data_new, vec![2u8, 3u8]); + } + + fn test_nested_spawn() { + let data = vec![7u8, 13u8]; + let data_new = sp_tasks::spawn(tasks::parallel_incrementer, data).join(); + + assert_eq!(data_new, vec![10u8, 16u8]); + } + + fn test_panic_in_spawned() { + sp_tasks::spawn(tasks::panicker, vec![]).join(); + } + } #[cfg(not(feature = "std"))] mod tasks { diff --git a/client/executor/wasmtime/src/host.rs b/client/executor/wasmtime/src/host.rs index 7e8afa6c43e8c..3f5ac0560a6d7 100644 --- a/client/executor/wasmtime/src/host.rs +++ b/client/executor/wasmtime/src/host.rs @@ -19,21 +19,18 @@ //! This module defines `HostState` and `HostContext` structs which provide logic and state //! required for execution of host. +use crate::instance_wrapper::InstanceWrapper; +use crate::util; use std::{cell::RefCell, rc::Rc}; - -use codec::{Decode, Encode}; use log::trace; +use codec::{Encode, Decode}; use sc_allocator::FreeingBumpHeapAllocator; -use sc_executor_common::{ - error::Result, - sandbox::{self, SandboxCapabilities, SupervisorFuncIndex}, -}; +use sc_executor_common::error::Result; +use sc_executor_common::sandbox::{self, SandboxCapabilities, SupervisorFuncIndex}; use sp_core::sandbox as sandbox_primitives; use sp_wasm_interface::{FunctionContext, MemoryId, Pointer, Sandbox, WordSize}; use wasmtime::{Func, Val}; -use crate::{instance_wrapper::InstanceWrapper, util}; - /// Wrapper type for pointer to a Wasm table entry. /// /// The wrapper type is used to ensure that the function reference is valid as it must be unsafely From fed0878940ae0c444c67a4d2d98f6439db455930 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Wed, 30 Jun 2021 10:51:41 -0700 Subject: [PATCH 035/144] Try to reduce diff on tests --- frame/vesting/src/tests.rs | 564 +++++++++++++++++++------------------ 1 file changed, 297 insertions(+), 267 deletions(-) diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index d3ae796a8f007..f801ae00da462 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -27,56 +27,59 @@ const ED: u64 = 256; #[test] fn check_vesting_status() { - ExtBuilder::default().existential_deposit(256).build().execute_with(|| { - let user1_free_balance = Balances::free_balance(&1); - let user2_free_balance = Balances::free_balance(&2); - let user12_free_balance = Balances::free_balance(&12); - assert_eq!(user1_free_balance, 256 * 10); // Account 1 has free balance - assert_eq!(user2_free_balance, 256 * 20); // Account 2 has free balance - assert_eq!(user12_free_balance, 256 * 10); // Account 12 has free balance - let user1_vesting_schedule = VestingInfo::new::( - 256 * 5, - 128, // Vesting over 10 blocks - 0, - ); - let user2_vesting_schedule = VestingInfo::new::( - 256 * 20, - 256, // Vesting over 20 blocks - 10, - ); - let user12_vesting_schedule = VestingInfo::new::( - 256 * 5, - 64, // Vesting over 20 blocks - 10, - ); - assert_eq!(Vesting::vesting(&1).unwrap(), vec![user1_vesting_schedule]); // Account 1 has a vesting schedule - assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); // Account 2 has a vesting schedule - assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); // Account 12 has a vesting schedule - - // Account 1 has only 128 units vested from their illiquid 256 * 5 units at block 1 - assert_eq!(Vesting::vesting_balance(&1), Some(128 * 9)); - // Account 2 has their full balance locked - assert_eq!(Vesting::vesting_balance(&2), Some(user2_free_balance)); - // Account 12 has only their illiquid funds locked - assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); - - System::set_block_number(10); - assert_eq!(System::block_number(), 10); - - // Account 1 has fully vested by block 10 - assert_eq!(Vesting::vesting_balance(&1), Some(0)); - // Account 2 has started vesting by block 10 - assert_eq!(Vesting::vesting_balance(&2), Some(user2_free_balance)); - // Account 12 has started vesting by block 10 - assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); - - System::set_block_number(30); - assert_eq!(System::block_number(), 30); - - assert_eq!(Vesting::vesting_balance(&1), Some(0)); // Account 1 is still fully vested, and not negative - assert_eq!(Vesting::vesting_balance(&2), Some(0)); // Account 2 has fully vested by block 30 - assert_eq!(Vesting::vesting_balance(&12), Some(0)); // Account 2 has fully vested by block 30 - }); + ExtBuilder::default() + .existential_deposit(256) + .build() + .execute_with(|| { + let user1_free_balance = Balances::free_balance(&1); + let user2_free_balance = Balances::free_balance(&2); + let user12_free_balance = Balances::free_balance(&12); + assert_eq!(user1_free_balance, 256 * 10); // Account 1 has free balance + assert_eq!(user2_free_balance, 256 * 20); // Account 2 has free balance + assert_eq!(user12_free_balance, 256 * 10); // Account 12 has free balance + let user1_vesting_schedule = VestingInfo::new::( + 256 * 5, + 128, // Vesting over 10 blocks + 0, + ); + let user2_vesting_schedule = VestingInfo::new::( + 256 * 20, + 256, // Vesting over 20 blocks + 10, + ); + let user12_vesting_schedule = VestingInfo::new::( + 256 * 5, + 64, // Vesting over 20 blocks + 10, + ); + assert_eq!(Vesting::vesting(&1).unwrap(), vec![user1_vesting_schedule]); // Account 1 has a vesting schedule + assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); // Account 2 has a vesting schedule + assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); // Account 12 has a vesting schedule + + // Account 1 has only 128 units vested from their illiquid 256 * 5 units at block 1 + assert_eq!(Vesting::vesting_balance(&1), Some(128 * 9)); + // Account 2 has their full balance locked + assert_eq!(Vesting::vesting_balance(&2), Some(user2_free_balance)); + // Account 12 has only their illiquid funds locked + assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); + + System::set_block_number(10); + assert_eq!(System::block_number(), 10); + + // Account 1 has fully vested by block 10 + assert_eq!(Vesting::vesting_balance(&1), Some(0)); + // Account 2 has started vesting by block 10 + assert_eq!(Vesting::vesting_balance(&2), Some(user2_free_balance)); + // Account 12 has started vesting by block 10 + assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); + + System::set_block_number(30); + assert_eq!(System::block_number(), 30); + + assert_eq!(Vesting::vesting_balance(&1), Some(0)); // Account 1 is still fully vested, and not negative + assert_eq!(Vesting::vesting_balance(&2), Some(0)); // Account 2 has fully vested by block 30 + assert_eq!(Vesting::vesting_balance(&12), Some(0)); // Account 2 has fully vested by block 30 + }); } #[test] @@ -158,170 +161,191 @@ fn check_vesting_status_for_multi_schedule_account() { #[test] fn unvested_balance_should_not_transfer() { - ExtBuilder::default().existential_deposit(10).build().execute_with(|| { - let user1_free_balance = Balances::free_balance(&1); - assert_eq!(user1_free_balance, 100); // Account 1 has free balance - // Account 1 has only 5 units vested at block 1 (plus 50 unvested) - assert_eq!(Vesting::vesting_balance(&1), Some(45)); - assert_noop!( - Balances::transfer(Some(1).into(), 2, 56), - pallet_balances::Error::::LiquidityRestrictions, - ); // Account 1 cannot send more than vested amount - }); + ExtBuilder::default() + .existential_deposit(10) + .build() + .execute_with(|| { + let user1_free_balance = Balances::free_balance(&1); + assert_eq!(user1_free_balance, 100); // Account 1 has free balance + // Account 1 has only 5 units vested at block 1 (plus 50 unvested) + assert_eq!(Vesting::vesting_balance(&1), Some(45)); + assert_noop!( + Balances::transfer(Some(1).into(), 2, 56), + pallet_balances::Error::::LiquidityRestrictions, + ); // Account 1 cannot send more than vested amount + }); } #[test] fn vested_balance_should_transfer() { - ExtBuilder::default().existential_deposit(10).build().execute_with(|| { - let user1_free_balance = Balances::free_balance(&1); - assert_eq!(user1_free_balance, 100); // Account 1 has free balance - // Account 1 has only 5 units vested at block 1 (plus 50 unvested) - assert_eq!(Vesting::vesting_balance(&1), Some(45)); - assert_ok!(Vesting::vest(Some(1).into())); - assert_ok!(Balances::transfer(Some(1).into(), 2, 55)); - }); + ExtBuilder::default() + .existential_deposit(10) + .build() + .execute_with(|| { + let user1_free_balance = Balances::free_balance(&1); + assert_eq!(user1_free_balance, 100); // Account 1 has free balance + // Account 1 has only 5 units vested at block 1 (plus 50 unvested) + assert_eq!(Vesting::vesting_balance(&1), Some(45)); + assert_ok!(Vesting::vest(Some(1).into())); + assert_ok!(Balances::transfer(Some(1).into(), 2, 55)); + }); } #[test] fn vested_balance_should_transfer_using_vest_other() { - ExtBuilder::default().existential_deposit(10).build().execute_with(|| { - let user1_free_balance = Balances::free_balance(&1); - assert_eq!(user1_free_balance, 100); // Account 1 has free balance - // Account 1 has only 5 units vested at block 1 (plus 50 unvested) - assert_eq!(Vesting::vesting_balance(&1), Some(45)); - assert_ok!(Vesting::vest_other(Some(2).into(), 1)); - assert_ok!(Balances::transfer(Some(1).into(), 2, 55)); - }); + ExtBuilder::default() + .existential_deposit(10) + .build() + .execute_with(|| { + let user1_free_balance = Balances::free_balance(&1); + assert_eq!(user1_free_balance, 100); // Account 1 has free balance + // Account 1 has only 5 units vested at block 1 (plus 50 unvested) + assert_eq!(Vesting::vesting_balance(&1), Some(45)); + assert_ok!(Vesting::vest_other(Some(2).into(), 1)); + assert_ok!(Balances::transfer(Some(1).into(), 2, 55)); + }); } #[test] fn extra_balance_should_transfer() { - ExtBuilder::default().existential_deposit(10).build().execute_with(|| { - assert_ok!(Balances::transfer(Some(3).into(), 1, 100)); - assert_ok!(Balances::transfer(Some(3).into(), 2, 100)); + ExtBuilder::default() + .existential_deposit(10) + .build() + .execute_with(|| { + assert_ok!(Balances::transfer(Some(3).into(), 1, 100)); + assert_ok!(Balances::transfer(Some(3).into(), 2, 100)); - let user1_free_balance = Balances::free_balance(&1); - assert_eq!(user1_free_balance, 200); // Account 1 has 100 more free balance than normal + let user1_free_balance = Balances::free_balance(&1); + assert_eq!(user1_free_balance, 200); // Account 1 has 100 more free balance than normal - let user2_free_balance = Balances::free_balance(&2); - assert_eq!(user2_free_balance, 300); // Account 2 has 100 more free balance than normal + let user2_free_balance = Balances::free_balance(&2); + assert_eq!(user2_free_balance, 300); // Account 2 has 100 more free balance than normal - // Account 1 has only 5 units vested at block 1 (plus 150 unvested) - assert_eq!(Vesting::vesting_balance(&1), Some(45)); - assert_ok!(Vesting::vest(Some(1).into())); - assert_ok!(Balances::transfer(Some(1).into(), 3, 155)); // Account 1 can send extra units gained + // Account 1 has only 5 units vested at block 1 (plus 150 unvested) + assert_eq!(Vesting::vesting_balance(&1), Some(45)); + assert_ok!(Vesting::vest(Some(1).into())); + assert_ok!(Balances::transfer(Some(1).into(), 3, 155)); // Account 1 can send extra units gained - // Account 2 has no units vested at block 1, but gained 100 - assert_eq!(Vesting::vesting_balance(&2), Some(200)); - assert_ok!(Vesting::vest(Some(2).into())); - assert_ok!(Balances::transfer(Some(2).into(), 3, 100)); // Account 2 can send extra units gained - }); + // Account 2 has no units vested at block 1, but gained 100 + assert_eq!(Vesting::vesting_balance(&2), Some(200)); + assert_ok!(Vesting::vest(Some(2).into())); + assert_ok!(Balances::transfer(Some(2).into(), 3, 100)); // Account 2 can send extra units gained + }); } #[test] fn liquid_funds_should_transfer_with_delayed_vesting() { - ExtBuilder::default().existential_deposit(256).build().execute_with(|| { - let user12_free_balance = Balances::free_balance(&12); + ExtBuilder::default() + .existential_deposit(256) + .build() + .execute_with(|| { + let user12_free_balance = Balances::free_balance(&12); - assert_eq!(user12_free_balance, 2560); // Account 12 has free balance - // Account 12 has liquid funds - assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); + assert_eq!(user12_free_balance, 2560); // Account 12 has free balance + // Account 12 has liquid funds + assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); - // Account 12 has delayed vesting - let user12_vesting_schedule = VestingInfo::new::( - 256 * 5, - 64, // Vesting over 20 blocks - 10, - ); - assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); + // Account 12 has delayed vesting + let user12_vesting_schedule = VestingInfo::new::( + 256 * 5, + 64, // Vesting over 20 blocks + 10, + ); + assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); - // Account 12 can still send liquid funds - assert_ok!(Balances::transfer(Some(12).into(), 3, 256 * 5)); - }); + // Account 12 can still send liquid funds + assert_ok!(Balances::transfer(Some(12).into(), 3, 256 * 5)); + }); } #[test] fn vested_transfer_works() { - ExtBuilder::default().existential_deposit(256).build().execute_with(|| { - let user3_free_balance = Balances::free_balance(&3); - let user4_free_balance = Balances::free_balance(&4); - assert_eq!(user3_free_balance, 256 * 30); - assert_eq!(user4_free_balance, 256 * 40); - // Account 4 should not have any vesting yet. - assert_eq!(Vesting::vesting(&4), None); - // Make the schedule for the new transfer. - let new_vesting_schedule = VestingInfo::new::( - 256 * 5, - 64, // Vesting over 20 blocks - 10, - ); - assert_ok!(Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule)); - // Now account 4 should have vesting. - assert_eq!(Vesting::vesting(&4).unwrap(), vec![new_vesting_schedule]); - // Ensure the transfer happened correctly. - let user3_free_balance_updated = Balances::free_balance(&3); - assert_eq!(user3_free_balance_updated, 256 * 25); - let user4_free_balance_updated = Balances::free_balance(&4); - assert_eq!(user4_free_balance_updated, 256 * 45); - // Account 4 has 5 * 256 locked. - assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5)); - - System::set_block_number(20); - assert_eq!(System::block_number(), 20); - - // Account 4 has 5 * 64 units vested by block 20. - assert_eq!(Vesting::vesting_balance(&4), Some(10 * 64)); - - System::set_block_number(30); - assert_eq!(System::block_number(), 30); - - // Account 4 has fully vested. - assert_eq!(Vesting::vesting_balance(&4), Some(0)); - }); + ExtBuilder::default() + .existential_deposit(256) + .build() + .execute_with(|| { + let user3_free_balance = Balances::free_balance(&3); + let user4_free_balance = Balances::free_balance(&4); + assert_eq!(user3_free_balance, 256 * 30); + assert_eq!(user4_free_balance, 256 * 40); + // Account 4 should not have any vesting yet. + assert_eq!(Vesting::vesting(&4), None); + // Make the schedule for the new transfer. + let new_vesting_schedule = VestingInfo::new::( + 256 * 5, + 64, // Vesting over 20 blocks + 10, + ); + assert_ok!(Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule)); + // Now account 4 should have vesting. + assert_eq!(Vesting::vesting(&4).unwrap(), vec![new_vesting_schedule]); + // Ensure the transfer happened correctly. + let user3_free_balance_updated = Balances::free_balance(&3); + assert_eq!(user3_free_balance_updated, 256 * 25); + let user4_free_balance_updated = Balances::free_balance(&4); + assert_eq!(user4_free_balance_updated, 256 * 45); + // Account 4 has 5 * 256 locked. + assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5)); + + System::set_block_number(20); + assert_eq!(System::block_number(), 20); + + // Account 4 has 5 * 64 units vested by block 20. + assert_eq!(Vesting::vesting_balance(&4), Some(10 * 64)); + + System::set_block_number(30); + assert_eq!(System::block_number(), 30); + + // Account 4 has fully vested. + assert_eq!(Vesting::vesting_balance(&4), Some(0)); + }); } #[test] fn vested_transfer_correctly_fails() { - ExtBuilder::default().existential_deposit(256).build().execute_with(|| { - let user2_free_balance = Balances::free_balance(&2); - let user4_free_balance = Balances::free_balance(&4); - assert_eq!(user2_free_balance, 256 * 20); - assert_eq!(user4_free_balance, 256 * 40); - - // Account 2 should already have a vesting schedule. - let user2_vesting_schedule = VestingInfo::new::( - 256 * 20, - 256, // Vesting over 20 blocks - 10, - ); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); - - // Fails due to too low transfer amount. - let new_vesting_schedule_too_low = - VestingInfo::new::(::MinVestedTransfer::get() - 1, 64, 10); - assert_noop!( - Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule_too_low), - Error::::AmountLow, - ); + ExtBuilder::default() + .existential_deposit(256) + .build() + .execute_with(|| { + let user2_free_balance = Balances::free_balance(&2); + let user4_free_balance = Balances::free_balance(&4); + assert_eq!(user2_free_balance, 256 * 20); + assert_eq!(user4_free_balance, 256 * 40); + + // Account 2 should already have a vesting schedule. + let user2_vesting_schedule = VestingInfo::new::( + 256 * 20, + 256, // Vesting over 20 blocks + 10, + ); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); + + // Fails due to too low transfer amount. + let new_vesting_schedule_too_low = + VestingInfo::new::(::MinVestedTransfer::get() - 1, 64, 10); + assert_noop!( + Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule_too_low), + Error::::AmountLow, + ); - // `per_block` is 0, which would result in a schedule with infinite duration. - let schedule_per_block_0 = VestingInfo::new::(256, 0, 10); - assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_per_block_0), - Error::::InvalidScheduleParams, - ); + // `per_block` is 0, which would result in a schedule with infinite duration. + let schedule_per_block_0 = VestingInfo::new::(256, 0, 10); + assert_noop!( + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_per_block_0), + Error::::InvalidScheduleParams, + ); - // `locked` is 0. - let schedule_locked_0 = VestingInfo::new::(0, 1, 10); - assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), - Error::::AmountLow, - ); + // `locked` is 0. + let schedule_locked_0 = VestingInfo::new::(0, 1, 10); + assert_noop!( + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), + Error::::AmountLow, + ); - // Free balance has not changed. - assert_eq!(user2_free_balance, Balances::free_balance(&2)); - assert_eq!(user4_free_balance, Balances::free_balance(&4)); - }); + // Free balance has not changed. + assert_eq!(user2_free_balance, Balances::free_balance(&2)); + assert_eq!(user4_free_balance, Balances::free_balance(&4)); + }); } #[test] @@ -360,101 +384,107 @@ fn vested_transfer_allows_max_schedules() { #[test] fn force_vested_transfer_works() { - ExtBuilder::default().existential_deposit(256).build().execute_with(|| { - let user3_free_balance = Balances::free_balance(&3); - let user4_free_balance = Balances::free_balance(&4); - assert_eq!(user3_free_balance, 256 * 30); - assert_eq!(user4_free_balance, 256 * 40); - // Account 4 should not have any vesting yet. - assert_eq!(Vesting::vesting(&4), None); - // Make the schedule for the new transfer. - let new_vesting_schedule = VestingInfo::new::( - 256 * 5, - 64, // Vesting over 20 blocks - 10, - ); - - assert_noop!( - Vesting::force_vested_transfer(Some(4).into(), 3, 4, new_vesting_schedule), - BadOrigin - ); - assert_ok!(Vesting::force_vested_transfer( - RawOrigin::Root.into(), - 3, - 4, - new_vesting_schedule - )); - // Now account 4 should have vesting. - assert_eq!(Vesting::vesting(&4).unwrap()[0], new_vesting_schedule); - assert_eq!(Vesting::vesting(&4).unwrap().len(), 1); - // Ensure the transfer happened correctly. - let user3_free_balance_updated = Balances::free_balance(&3); - assert_eq!(user3_free_balance_updated, 256 * 25); - let user4_free_balance_updated = Balances::free_balance(&4); - assert_eq!(user4_free_balance_updated, 256 * 45); - // Account 4 has 5 * 256 locked. - assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5)); - - System::set_block_number(20); - assert_eq!(System::block_number(), 20); - - // Account 4 has 5 * 64 units vested by block 20. - assert_eq!(Vesting::vesting_balance(&4), Some(10 * 64)); - - System::set_block_number(30); - assert_eq!(System::block_number(), 30); + ExtBuilder::default() + .existential_deposit(256) + .build() + .execute_with(|| { + let user3_free_balance = Balances::free_balance(&3); + let user4_free_balance = Balances::free_balance(&4); + assert_eq!(user3_free_balance, 256 * 30); + assert_eq!(user4_free_balance, 256 * 40); + // Account 4 should not have any vesting yet. + assert_eq!(Vesting::vesting(&4), None); + // Make the schedule for the new transfer. + let new_vesting_schedule = VestingInfo::new::( + 256 * 5, + 64, // Vesting over 20 blocks + 10, + ); - // Account 4 has fully vested. - assert_eq!(Vesting::vesting_balance(&4), Some(0)); - }); + assert_noop!( + Vesting::force_vested_transfer(Some(4).into(), 3, 4, new_vesting_schedule), + BadOrigin + ); + assert_ok!(Vesting::force_vested_transfer( + RawOrigin::Root.into(), + 3, + 4, + new_vesting_schedule + )); + // Now account 4 should have vesting. + assert_eq!(Vesting::vesting(&4).unwrap()[0], new_vesting_schedule); + assert_eq!(Vesting::vesting(&4).unwrap().len(), 1); + // Ensure the transfer happened correctly. + let user3_free_balance_updated = Balances::free_balance(&3); + assert_eq!(user3_free_balance_updated, 256 * 25); + let user4_free_balance_updated = Balances::free_balance(&4); + assert_eq!(user4_free_balance_updated, 256 * 45); + // Account 4 has 5 * 256 locked. + assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5)); + + System::set_block_number(20); + assert_eq!(System::block_number(), 20); + + // Account 4 has 5 * 64 units vested by block 20. + assert_eq!(Vesting::vesting_balance(&4), Some(10 * 64)); + + System::set_block_number(30); + assert_eq!(System::block_number(), 30); + + // Account 4 has fully vested. + assert_eq!(Vesting::vesting_balance(&4), Some(0)); + }); } #[test] fn force_vested_transfer_correctly_fails() { - ExtBuilder::default().existential_deposit(256).build().execute_with(|| { - let user2_free_balance = Balances::free_balance(&2); - let user4_free_balance = Balances::free_balance(&4); - assert_eq!(user2_free_balance, 256 * 20); - assert_eq!(user4_free_balance, 256 * 40); - // Account 2 should already have a vesting schedule. - let user2_vesting_schedule = VestingInfo::new::( - 256 * 20, - 256, // Vesting over 20 blocks - 10, - ); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); - - // Too low transfer amount. - let new_vesting_schedule_too_low = - VestingInfo::new::(::MinVestedTransfer::get() - 1, 64, 10); - assert_noop!( - Vesting::force_vested_transfer( - RawOrigin::Root.into(), - 3, - 4, - new_vesting_schedule_too_low - ), - Error::::AmountLow, - ); + ExtBuilder::default() + .existential_deposit(256) + .build() + .execute_with(|| { + let user2_free_balance = Balances::free_balance(&2); + let user4_free_balance = Balances::free_balance(&4); + assert_eq!(user2_free_balance, 256 * 20); + assert_eq!(user4_free_balance, 256 * 40); + // Account 2 should already have a vesting schedule. + let user2_vesting_schedule = VestingInfo::new::( + 256 * 20, + 256, // Vesting over 20 blocks + 10, + ); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); + + // Too low transfer amount. + let new_vesting_schedule_too_low = + VestingInfo::new::(::MinVestedTransfer::get() - 1, 64, 10); + assert_noop!( + Vesting::force_vested_transfer( + RawOrigin::Root.into(), + 3, + 4, + new_vesting_schedule_too_low + ), + Error::::AmountLow, + ); - // `per_block` is 0. - let schedule_per_block_0 = VestingInfo::new::(256, 0, 10); - assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_per_block_0), - Error::::InvalidScheduleParams, - ); + // `per_block` is 0. + let schedule_per_block_0 = VestingInfo::new::(256, 0, 10); + assert_noop!( + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_per_block_0), + Error::::InvalidScheduleParams, + ); - // `locked` is 0. - let schedule_locked_0 = VestingInfo::new::(0, 1, 10); - assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), - Error::::AmountLow, - ); + // `locked` is 0. + let schedule_locked_0 = VestingInfo::new::(0, 1, 10); + assert_noop!( + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), + Error::::AmountLow, + ); - // Verify no currency transfer happened. - assert_eq!(user2_free_balance, Balances::free_balance(&2)); - assert_eq!(user4_free_balance, Balances::free_balance(&4)); - }); + // Verify no currency transfer happened. + assert_eq!(user2_free_balance, Balances::free_balance(&2)); + assert_eq!(user4_free_balance, Balances::free_balance(&4)); + }); } #[test] From e60275dc434dc56ccd569a33c5b508f513b0e051 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Wed, 30 Jun 2021 11:43:51 -0700 Subject: [PATCH 036/144] namespace Vesting; check key when key should not exist; --- frame/vesting/src/tests.rs | 250 ++++++++++++++++++++----------------- 1 file changed, 136 insertions(+), 114 deletions(-) diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index f801ae00da462..54146b56ae8c3 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -20,7 +20,7 @@ use frame_system::RawOrigin; use sp_runtime::traits::{BadOrigin, Identity}; use super::*; -use crate::mock::{Balances, ExtBuilder, System, Test, Vesting}; +use crate::mock::{Balances, ExtBuilder, System, Test}; /// A default existential deposit. const ED: u64 = 256; @@ -52,33 +52,41 @@ fn check_vesting_status() { 64, // Vesting over 20 blocks 10, ); - assert_eq!(Vesting::vesting(&1).unwrap(), vec![user1_vesting_schedule]); // Account 1 has a vesting schedule - assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); // Account 2 has a vesting schedule - assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); // Account 12 has a vesting schedule + assert_eq!(mock::Vesting::vesting(&1).unwrap(), vec![user1_vesting_schedule]); // Account 1 has a vesting schedule + assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); // Account 2 has a vesting schedule + assert_eq!(mock::Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); // Account 12 has a vesting schedule // Account 1 has only 128 units vested from their illiquid 256 * 5 units at block 1 - assert_eq!(Vesting::vesting_balance(&1), Some(128 * 9)); + assert_eq!(mock::Vesting::vesting_balance(&1), Some(128 * 9)); // Account 2 has their full balance locked - assert_eq!(Vesting::vesting_balance(&2), Some(user2_free_balance)); + assert_eq!(mock::Vesting::vesting_balance(&2), Some(user2_free_balance)); // Account 12 has only their illiquid funds locked - assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); + assert_eq!(mock::Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); System::set_block_number(10); assert_eq!(System::block_number(), 10); // Account 1 has fully vested by block 10 - assert_eq!(Vesting::vesting_balance(&1), Some(0)); + assert_eq!(mock::Vesting::vesting_balance(&1), Some(0)); // Account 2 has started vesting by block 10 - assert_eq!(Vesting::vesting_balance(&2), Some(user2_free_balance)); + assert_eq!(mock::Vesting::vesting_balance(&2), Some(user2_free_balance)); // Account 12 has started vesting by block 10 - assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); + assert_eq!(mock::Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); System::set_block_number(30); assert_eq!(System::block_number(), 30); - assert_eq!(Vesting::vesting_balance(&1), Some(0)); // Account 1 is still fully vested, and not negative - assert_eq!(Vesting::vesting_balance(&2), Some(0)); // Account 2 has fully vested by block 30 - assert_eq!(Vesting::vesting_balance(&12), Some(0)); // Account 2 has fully vested by block 30 + assert_eq!(mock::Vesting::vesting_balance(&1), Some(0)); // Account 1 is still fully vested, and not negative + assert_eq!(mock::Vesting::vesting_balance(&2), Some(0)); // Account 2 has fully vested by block 30 + assert_eq!(mock::Vesting::vesting_balance(&12), Some(0)); // Account 2 has fully vested by block 30 + + // Once we unlock the funds, they are removed from storage. + assert_ok!(mock::Vesting::vest(Some(1).into())); + assert!(!>::contains_key(&1)); + assert_ok!(mock::Vesting::vest(Some(2).into())); + assert!(!>::contains_key(&2)); + assert_ok!(mock::Vesting::vest(Some(12).into())); + assert!(!>::contains_key(&12)); }); } @@ -92,12 +100,12 @@ fn check_vesting_status_for_multi_schedule_account() { 10, ); // Account 2 already has a vesting schedule. - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0]); // Account 2's free balance is from sched0 let free_balance = Balances::free_balance(&2); assert_eq!(free_balance, ED * (20)); - assert_eq!(Vesting::vesting_balance(&2), Some(free_balance)); + assert_eq!(mock::Vesting::vesting_balance(&2), Some(free_balance)); // Add a 2nd schedule that is already unlocking by block #1 let sched1 = VestingInfo::new::( @@ -105,14 +113,14 @@ fn check_vesting_status_for_multi_schedule_account() { ED, // Vesting over 10 blocks 0, ); - assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched1)); + assert_ok!(mock::Vesting::vested_transfer(Some(4).into(), 2, sched1)); // Free balance is equal to the two existing schedules total amount. let free_balance = Balances::free_balance(&2); assert_eq!(free_balance, ED * (10 + 20)); // The most recently added schedule exists. - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); + assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); // sched1 has free funds at block #1, but nothing else. - assert_eq!(Vesting::vesting_balance(&2), Some(free_balance - sched1.per_block())); + assert_eq!(mock::Vesting::vesting_balance(&2), Some(free_balance - sched1.per_block())); // Add a 3rd schedule let sched2 = VestingInfo::new::( @@ -120,7 +128,7 @@ fn check_vesting_status_for_multi_schedule_account() { ED, // Vesting over 30 blocks 5, ); - assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched2)); + assert_ok!(mock::Vesting::vested_transfer(Some(4).into(), 2, sched2)); System::set_block_number(9); // Free balance is equal to the 3 existing schedules total amount. @@ -128,14 +136,14 @@ fn check_vesting_status_for_multi_schedule_account() { assert_eq!(free_balance, ED * (10 + 20 + 30)); // sched1 and sched2 are freeing funds at block #9. assert_eq!( - Vesting::vesting_balance(&2), + mock::Vesting::vesting_balance(&2), Some(free_balance - sched1.per_block() * 9 - sched2.per_block() * 4) ); System::set_block_number(20); // At block #20 sched1 is fully unlocked while sched2 and sched0 are partially unlocked. assert_eq!( - Vesting::vesting_balance(&2), + mock::Vesting::vesting_balance(&2), Some( free_balance - sched1.locked() - sched2.per_block() * 15 - sched0.per_block() * 10 ) @@ -144,18 +152,20 @@ fn check_vesting_status_for_multi_schedule_account() { System::set_block_number(30); // At block #30 sched0 and sched1 are fully unlocked while sched2 is partially unlocked. assert_eq!( - Vesting::vesting_balance(&2), + mock::Vesting::vesting_balance(&2), Some(free_balance - sched1.locked() - sched2.per_block() * 25 - sched0.locked()) ); // At block #35 sched2 fully unlocks and thus all schedules funds are unlocked. System::set_block_number(35); assert_eq!(System::block_number(), 35); - assert_eq!(Vesting::vesting_balance(&2), Some(0)); + assert_eq!(mock::Vesting::vesting_balance(&2), Some(0)); // Since we have not called any extrinsics that would unlock funds the schedules // are still in storage. - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1, sched2]); - assert_ok!(Vesting::vest(Some(2).into())); + assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0, sched1, sched2]); + // But once we unlock the funds, they are removed from storage. + assert_ok!(mock::Vesting::vest(Some(2).into())); + assert!(!>::contains_key(&2)); }); } @@ -168,7 +178,7 @@ fn unvested_balance_should_not_transfer() { let user1_free_balance = Balances::free_balance(&1); assert_eq!(user1_free_balance, 100); // Account 1 has free balance // Account 1 has only 5 units vested at block 1 (plus 50 unvested) - assert_eq!(Vesting::vesting_balance(&1), Some(45)); + assert_eq!(mock::Vesting::vesting_balance(&1), Some(45)); assert_noop!( Balances::transfer(Some(1).into(), 2, 56), pallet_balances::Error::::LiquidityRestrictions, @@ -185,8 +195,8 @@ fn vested_balance_should_transfer() { let user1_free_balance = Balances::free_balance(&1); assert_eq!(user1_free_balance, 100); // Account 1 has free balance // Account 1 has only 5 units vested at block 1 (plus 50 unvested) - assert_eq!(Vesting::vesting_balance(&1), Some(45)); - assert_ok!(Vesting::vest(Some(1).into())); + assert_eq!(mock::Vesting::vesting_balance(&1), Some(45)); + assert_ok!(mock::Vesting::vest(Some(1).into())); assert_ok!(Balances::transfer(Some(1).into(), 2, 55)); }); } @@ -200,8 +210,8 @@ fn vested_balance_should_transfer_using_vest_other() { let user1_free_balance = Balances::free_balance(&1); assert_eq!(user1_free_balance, 100); // Account 1 has free balance // Account 1 has only 5 units vested at block 1 (plus 50 unvested) - assert_eq!(Vesting::vesting_balance(&1), Some(45)); - assert_ok!(Vesting::vest_other(Some(2).into(), 1)); + assert_eq!(mock::Vesting::vesting_balance(&1), Some(45)); + assert_ok!(mock::Vesting::vest_other(Some(2).into(), 1)); assert_ok!(Balances::transfer(Some(1).into(), 2, 55)); }); } @@ -222,13 +232,13 @@ fn extra_balance_should_transfer() { assert_eq!(user2_free_balance, 300); // Account 2 has 100 more free balance than normal // Account 1 has only 5 units vested at block 1 (plus 150 unvested) - assert_eq!(Vesting::vesting_balance(&1), Some(45)); - assert_ok!(Vesting::vest(Some(1).into())); + assert_eq!(mock::Vesting::vesting_balance(&1), Some(45)); + assert_ok!(mock::Vesting::vest(Some(1).into())); assert_ok!(Balances::transfer(Some(1).into(), 3, 155)); // Account 1 can send extra units gained // Account 2 has no units vested at block 1, but gained 100 - assert_eq!(Vesting::vesting_balance(&2), Some(200)); - assert_ok!(Vesting::vest(Some(2).into())); + assert_eq!(mock::Vesting::vesting_balance(&2), Some(200)); + assert_ok!(mock::Vesting::vest(Some(2).into())); assert_ok!(Balances::transfer(Some(2).into(), 3, 100)); // Account 2 can send extra units gained }); } @@ -243,7 +253,7 @@ fn liquid_funds_should_transfer_with_delayed_vesting() { assert_eq!(user12_free_balance, 2560); // Account 12 has free balance // Account 12 has liquid funds - assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); + assert_eq!(mock::Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); // Account 12 has delayed vesting let user12_vesting_schedule = VestingInfo::new::( @@ -251,7 +261,7 @@ fn liquid_funds_should_transfer_with_delayed_vesting() { 64, // Vesting over 20 blocks 10, ); - assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); + assert_eq!(mock::Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); // Account 12 can still send liquid funds assert_ok!(Balances::transfer(Some(12).into(), 3, 256 * 5)); @@ -269,35 +279,38 @@ fn vested_transfer_works() { assert_eq!(user3_free_balance, 256 * 30); assert_eq!(user4_free_balance, 256 * 40); // Account 4 should not have any vesting yet. - assert_eq!(Vesting::vesting(&4), None); + assert_eq!(mock::Vesting::vesting(&4), None); // Make the schedule for the new transfer. let new_vesting_schedule = VestingInfo::new::( 256 * 5, 64, // Vesting over 20 blocks 10, ); - assert_ok!(Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule)); + assert_ok!(mock::Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule)); // Now account 4 should have vesting. - assert_eq!(Vesting::vesting(&4).unwrap(), vec![new_vesting_schedule]); + assert_eq!(mock::Vesting::vesting(&4).unwrap(), vec![new_vesting_schedule]); // Ensure the transfer happened correctly. let user3_free_balance_updated = Balances::free_balance(&3); assert_eq!(user3_free_balance_updated, 256 * 25); let user4_free_balance_updated = Balances::free_balance(&4); assert_eq!(user4_free_balance_updated, 256 * 45); // Account 4 has 5 * 256 locked. - assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5)); + assert_eq!(mock::Vesting::vesting_balance(&4), Some(256 * 5)); System::set_block_number(20); assert_eq!(System::block_number(), 20); // Account 4 has 5 * 64 units vested by block 20. - assert_eq!(Vesting::vesting_balance(&4), Some(10 * 64)); + assert_eq!(mock::Vesting::vesting_balance(&4), Some(10 * 64)); System::set_block_number(30); assert_eq!(System::block_number(), 30); - // Account 4 has fully vested. - assert_eq!(Vesting::vesting_balance(&4), Some(0)); + // Account 4 has fully vested + assert_eq!(mock::Vesting::vesting_balance(&4), Some(0)); + // and after unlocking its schedules are removed from storage. + assert_ok!(mock::Vesting::vest(Some(4).into())); + assert!(!>::contains_key(&4)); }); } @@ -318,27 +331,27 @@ fn vested_transfer_correctly_fails() { 256, // Vesting over 20 blocks 10, ); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); + assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); // Fails due to too low transfer amount. let new_vesting_schedule_too_low = VestingInfo::new::(::MinVestedTransfer::get() - 1, 64, 10); assert_noop!( - Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule_too_low), + mock::Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule_too_low), Error::::AmountLow, ); // `per_block` is 0, which would result in a schedule with infinite duration. let schedule_per_block_0 = VestingInfo::new::(256, 0, 10); assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_per_block_0), + mock::Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_per_block_0), Error::::InvalidScheduleParams, ); // `locked` is 0. let schedule_locked_0 = VestingInfo::new::(0, 1, 10); assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), + mock::Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), Error::::AmountLow, ); @@ -359,18 +372,18 @@ fn vested_transfer_allows_max_schedules() { ); // Add max amount schedules to user 4. for _ in 0 .. max_schedules { - assert_ok!(Vesting::vested_transfer(Some(3).into(), 4, sched)); + assert_ok!(mock::Vesting::vested_transfer(Some(3).into(), 4, sched)); } // The schedules count towards vesting balance let transferred_amount = ED * max_schedules as u64; - assert_eq!(Vesting::vesting_balance(&4), Some(transferred_amount)); + assert_eq!(mock::Vesting::vesting_balance(&4), Some(transferred_amount)); // and free balance. user_4_free_balance += transferred_amount; assert_eq!(Balances::free_balance(&4), user_4_free_balance); // Cannot insert a 4th vesting schedule when `MaxVestingSchedules` === 3 assert_noop!( - Vesting::vested_transfer(Some(3).into(), 4, sched), + mock::Vesting::vested_transfer(Some(3).into(), 4, sched), Error::::AtMaxVestingSchedules, ); // so the free balance does not change. @@ -378,7 +391,10 @@ fn vested_transfer_allows_max_schedules() { // Account 4 has fully vested when all the schedules end System::set_block_number(ED + 10); - assert_eq!(Vesting::vesting_balance(&4), Some(0)); + assert_eq!(mock::Vesting::vesting_balance(&4), Some(0)); + // and after unlocking its schedules are removed from storage. + assert_ok!(mock::Vesting::vest(Some(4).into())); + assert!(!>::contains_key(&4)); }); } @@ -393,7 +409,7 @@ fn force_vested_transfer_works() { assert_eq!(user3_free_balance, 256 * 30); assert_eq!(user4_free_balance, 256 * 40); // Account 4 should not have any vesting yet. - assert_eq!(Vesting::vesting(&4), None); + assert_eq!(mock::Vesting::vesting(&4), None); // Make the schedule for the new transfer. let new_vesting_schedule = VestingInfo::new::( 256 * 5, @@ -402,37 +418,40 @@ fn force_vested_transfer_works() { ); assert_noop!( - Vesting::force_vested_transfer(Some(4).into(), 3, 4, new_vesting_schedule), + mock::Vesting::force_vested_transfer(Some(4).into(), 3, 4, new_vesting_schedule), BadOrigin ); - assert_ok!(Vesting::force_vested_transfer( + assert_ok!(mock::Vesting::force_vested_transfer( RawOrigin::Root.into(), 3, 4, new_vesting_schedule )); // Now account 4 should have vesting. - assert_eq!(Vesting::vesting(&4).unwrap()[0], new_vesting_schedule); - assert_eq!(Vesting::vesting(&4).unwrap().len(), 1); + assert_eq!(mock::Vesting::vesting(&4).unwrap()[0], new_vesting_schedule); + assert_eq!(mock::Vesting::vesting(&4).unwrap().len(), 1); // Ensure the transfer happened correctly. let user3_free_balance_updated = Balances::free_balance(&3); assert_eq!(user3_free_balance_updated, 256 * 25); let user4_free_balance_updated = Balances::free_balance(&4); assert_eq!(user4_free_balance_updated, 256 * 45); // Account 4 has 5 * 256 locked. - assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5)); + assert_eq!(mock::Vesting::vesting_balance(&4), Some(256 * 5)); System::set_block_number(20); assert_eq!(System::block_number(), 20); // Account 4 has 5 * 64 units vested by block 20. - assert_eq!(Vesting::vesting_balance(&4), Some(10 * 64)); + assert_eq!(mock::Vesting::vesting_balance(&4), Some(10 * 64)); System::set_block_number(30); assert_eq!(System::block_number(), 30); // Account 4 has fully vested. - assert_eq!(Vesting::vesting_balance(&4), Some(0)); + assert_eq!(mock::Vesting::vesting_balance(&4), Some(0)); + // and after unlocking its schedules are removed from storage. + assert_ok!(mock::Vesting::vest(Some(4).into())); + assert!(!>::contains_key(&4)); }); } @@ -452,13 +471,13 @@ fn force_vested_transfer_correctly_fails() { 256, // Vesting over 20 blocks 10, ); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); + assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); // Too low transfer amount. let new_vesting_schedule_too_low = VestingInfo::new::(::MinVestedTransfer::get() - 1, 64, 10); assert_noop!( - Vesting::force_vested_transfer( + mock::Vesting::force_vested_transfer( RawOrigin::Root.into(), 3, 4, @@ -470,14 +489,14 @@ fn force_vested_transfer_correctly_fails() { // `per_block` is 0. let schedule_per_block_0 = VestingInfo::new::(256, 0, 10); assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_per_block_0), + mock::Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_per_block_0), Error::::InvalidScheduleParams, ); // `locked` is 0. let schedule_locked_0 = VestingInfo::new::(0, 1, 10); assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), + mock::Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), Error::::AmountLow, ); @@ -498,26 +517,29 @@ fn force_vested_transfer_allows_max_schedules() { ); // Add max amount schedules to user 4. for _ in 0 .. max_schedules { - assert_ok!(Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, sched)); + assert_ok!(mock::Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, sched)); } // The schedules count towards vesting balance. let transferred_amount = ED * max_schedules as u64; - assert_eq!(Vesting::vesting_balance(&4), Some(transferred_amount)); + assert_eq!(mock::Vesting::vesting_balance(&4), Some(transferred_amount)); // and free balance. user_4_free_balance += transferred_amount; assert_eq!(Balances::free_balance(&4), user_4_free_balance); - // Cannot insert a 4th vesting schedule when `MaxVestingSchedules` === 3. + // Cannot insert a 4th vesting schedule when `MaxVestingSchedules` === 3 assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, sched), + mock::Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, sched), Error::::AtMaxVestingSchedules, ); // so the free balance does not change. assert_eq!(Balances::free_balance(&4), user_4_free_balance); - // Account 4 has fully vested when all the schedules end. + // Account 4 has fully vested when all the schedules end System::set_block_number(ED + 10); - assert_eq!(Vesting::vesting_balance(&4), Some(0)); + assert_eq!(mock::Vesting::vesting_balance(&4), Some(0)); + // and after unlocking its schedules are removed from storage. + assert_ok!(mock::Vesting::vest(Some(4).into())); + assert!(!>::contains_key(&4)); }); } @@ -530,14 +552,14 @@ fn merge_schedules_that_have_not_started() { ED, // Vest over 20 blocks. 10, ); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0]); assert_eq!(Balances::usable_balance(&2), 0); // Add a schedule that is identical to the one that already exists - Vesting::vested_transfer(Some(3).into(), 2, sched0).unwrap(); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched0]); + mock::Vesting::vested_transfer(Some(3).into(), 2, sched0).unwrap(); + assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0, sched0]); assert_eq!(Balances::usable_balance(&2), 0); - Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + mock::Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); // Since we merged identical schedules, the new schedule finishes at the same // time as the original, just with double the amount @@ -546,7 +568,7 @@ fn merge_schedules_that_have_not_started() { sched0.per_block() * 2, 10, // starts at the block the schedules are merged ); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched1]); + assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched1]); assert_eq!(Balances::usable_balance(&2), 0); }); @@ -562,15 +584,15 @@ fn merge_ongoing_schedules() { ED, // Vest over 20 blocks 10, ); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0]); let sched1 = VestingInfo::new::( 256 * 10, 256, // Vest over 10 blocks sched0.starting_block() + 5, // Start at block 15 ); - Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); + mock::Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); + assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); // Got to half way through the second schedule where both schedules are actively vesting let cur_block = 20; @@ -579,7 +601,7 @@ fn merge_ongoing_schedules() { // user2 has no usable balances prior to the merge because they have not unlocked // with `vest` yet. assert_eq!(Balances::usable_balance(&2), 0); - Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + mock::Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); // Merging schedules vests all pre-existing schedules prior to merging, which is reflected // in user2's updated usable balance @@ -599,7 +621,7 @@ fn merge_ongoing_schedules() { let sched2_per_block = sched2_locked / sched2_duration; let sched2 = VestingInfo::new::(sched2_locked, sched2_per_block, cur_block); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2]); + assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched2]); }); } @@ -625,7 +647,7 @@ fn merging_shifts_other_schedules_index() { ); // Account 3 start out with no schedules - assert_eq!(Vesting::vesting(&3), None); + assert_eq!(mock::Vesting::vesting(&3), None); // and some usable balance. let usable_balance = Balances::usable_balance(&3); assert_eq!(usable_balance, 30 * ED); @@ -634,16 +656,16 @@ fn merging_shifts_other_schedules_index() { assert_eq!(System::block_number(), cur_block); // Transfer the above 3 schedules to account 3 - assert_ok!(Vesting::vested_transfer(Some(4).into(), 3, sched0)); - assert_ok!(Vesting::vested_transfer(Some(4).into(), 3, sched1)); - assert_ok!(Vesting::vested_transfer(Some(4).into(), 3, sched2)); + assert_ok!(mock::Vesting::vested_transfer(Some(4).into(), 3, sched0)); + assert_ok!(mock::Vesting::vested_transfer(Some(4).into(), 3, sched1)); + assert_ok!(mock::Vesting::vested_transfer(Some(4).into(), 3, sched2)); // With no schedules vested or merged they are in the order they are created - assert_eq!(Vesting::vesting(&3).unwrap(), vec![sched0, sched1, sched2]); + assert_eq!(mock::Vesting::vesting(&3).unwrap(), vec![sched0, sched1, sched2]); // and the usable balance has not changed. assert_eq!(usable_balance, Balances::usable_balance(&3)); - assert_ok!(Vesting::merge_schedules(Some(3).into(), 0, 2)); + assert_ok!(mock::Vesting::merge_schedules(Some(3).into(), 0, 2)); // Create the merged schedule of sched0 & sched2. // The merged schedule will have the max possible starting block, @@ -658,7 +680,7 @@ fn merging_shifts_other_schedules_index() { let sched3 = VestingInfo::new::(sched3_locked, sched3_per_block, sched3_start); // The not touched schedule moves left and the new merged schedule is appended. - assert_eq!(Vesting::vesting(&3).unwrap(), vec![sched1, sched3]); + assert_eq!(mock::Vesting::vesting(&3).unwrap(), vec![sched1, sched3]); // The usable balance hasn't changed since none of the schedules have started. assert_eq!(Balances::usable_balance(&3), usable_balance); }); @@ -675,7 +697,7 @@ fn merge_ongoing_and_yet_to_be_started_schedule() { ED, // Vesting over 20 blocks 10, ); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0]); // Fast forward to half way through the life of sched_1 let mut cur_block = (sched0.starting_block() + sched0.ending_block::()) / 2; @@ -686,7 +708,7 @@ fn merge_ongoing_and_yet_to_be_started_schedule() { let mut usable_balance = 0; assert_eq!(Balances::usable_balance(&2), usable_balance); // Vest the current schedules (which is just sched0 now). - Vesting::vest(Some(2).into()).unwrap(); + mock::Vesting::vest(Some(2).into()).unwrap(); // After vesting the usable balance increases by the amount the unlocked amount. let sched0_vested_now = sched0.locked() - sched0.locked_at::(cur_block); @@ -703,10 +725,10 @@ fn merge_ongoing_and_yet_to_be_started_schedule() { 1, // Vesting over 256 * 10 (2560) blocks cur_block + 1, ); - Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); + mock::Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); // Merge the schedules before sched1 starts. - Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + mock::Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); // After merging, the usable balance only changes by the amount sched0 vested since we // last called `vest` (which is just 1 block). The usable balance is not affected by // sched1 because it has not started yet. @@ -724,7 +746,7 @@ fn merge_ongoing_and_yet_to_be_started_schedule() { let sched2_per_block = sched2_locked / sched2_duration; let sched2 = VestingInfo::new::(sched2_locked, sched2_per_block, sched2_start); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2]); + assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched2]); }); } @@ -739,14 +761,14 @@ fn merge_finishing_and_ongoing_schedule() { ED, // Vesting over 20 blocks. 10, ); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0]); let sched1 = VestingInfo::new::( ED * 40, ED, // Vesting over 40 blocks. 10, ); - assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched1)); + assert_ok!(mock::Vesting::vested_transfer(Some(4).into(), 2, sched1)); // Transfer a 3rd schedule, so we can demonstrate how schedule indices change. // (We are not merging this schedule.) @@ -755,10 +777,10 @@ fn merge_finishing_and_ongoing_schedule() { ED, // Vesting over 30 blocks. 10, ); - assert_ok!(Vesting::vested_transfer(Some(3).into(), 2, sched2)); + assert_ok!(mock::Vesting::vested_transfer(Some(3).into(), 2, sched2)); // The schedules are in expected order prior to merging. - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1, sched2]); + assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0, sched1, sched2]); // Fast forward to sched0's end block. let cur_block = sched0.ending_block::(); @@ -768,12 +790,12 @@ fn merge_finishing_and_ongoing_schedule() { // Prior to merge_schedules and with no vest/vest_other called the user has no usable // balance. assert_eq!(Balances::usable_balance(&2), 0); - assert_ok!(Vesting::merge_schedules(Some(2).into(), 0, 1)); + assert_ok!(mock::Vesting::merge_schedules(Some(2).into(), 0, 1)); // sched2 is now the first, since sched0 & sched1 get filtered out while "merging". // sched1 gets treated like the new merged schedule by getting pushed onto back // of the vesting schedules vec. Note: sched0 finished at the current block. - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2, sched1]); + assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched2, sched1]); // sched0 has finished, so its funds are fully unlocked. let sched0_unlocked_now = sched0.locked(); @@ -801,7 +823,7 @@ fn merge_finishing_schedules_does_not_create_a_new_one() { ED, // 20 block duration. 10, ); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0]); // Create sched1 and transfer it to account 2. let sched1 = VestingInfo::new::( @@ -809,8 +831,8 @@ fn merge_finishing_schedules_does_not_create_a_new_one() { ED, // 30 block duration. 10, ); - assert_ok!(Vesting::vested_transfer(Some(3).into(), 2, sched1)); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); + assert_ok!(mock::Vesting::vested_transfer(Some(3).into(), 2, sched1)); + assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); let all_scheds_end = sched0.ending_block::().max(sched1.ending_block::()); @@ -822,10 +844,10 @@ fn merge_finishing_schedules_does_not_create_a_new_one() { assert_eq!(Balances::usable_balance(&2), 0); // Merge schedule 0 and 1. - Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + mock::Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); // The user no longer has any more vesting schedules because they both ended at the // block they where merged. - assert_eq!(Vesting::vesting(&2), None); + assert!(!>::contains_key(&2)); // And their usable balance has increased by the total amount locked in the merged // schedules. assert_eq!(Balances::usable_balance(&2), sched0.locked() + sched1.locked()); @@ -841,28 +863,28 @@ fn merge_schedules_throws_proper_errors() { ED, // 20 block duration. 10, ); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0]); // There is only 1 vesting schedule. assert_noop!( - Vesting::merge_schedules(Some(2).into(), 0, 1), + mock::Vesting::merge_schedules(Some(2).into(), 0, 1), Error::::ScheduleIndexOutOfBounds ); // There are 0 vesting schedules. - assert_eq!(Vesting::vesting(&4), None); - assert_noop!(Vesting::merge_schedules(Some(4).into(), 0, 1), Error::::NotVesting); + assert_eq!(mock::Vesting::vesting(&4), None); + assert_noop!(mock::Vesting::merge_schedules(Some(4).into(), 0, 1), Error::::NotVesting); // There are enough schedules to merge but an index is non-existent. - Vesting::vested_transfer(Some(3).into(), 2, sched0).unwrap(); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched0]); + mock::Vesting::vested_transfer(Some(3).into(), 2, sched0).unwrap(); + assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0, sched0]); assert_noop!( - Vesting::merge_schedules(Some(2).into(), 0, 2), + mock::Vesting::merge_schedules(Some(2).into(), 0, 2), Error::::ScheduleIndexOutOfBounds ); // It is a storage noop with no errors if the indexes are the same. - assert_storage_noop!(Vesting::merge_schedules(Some(2).into(), 0, 0).unwrap()); + assert_storage_noop!(mock::Vesting::merge_schedules(Some(2).into(), 0, 0).unwrap()); }); } @@ -888,17 +910,17 @@ fn generates_multiple_schedules_from_genesis_config() { .build() .execute_with(|| { let user1_sched1 = VestingInfo::new::(5 * ED, 128, 0u64); - assert_eq!(Vesting::vesting(&1).unwrap(), vec![user1_sched1]); + assert_eq!(mock::Vesting::vesting(&1).unwrap(), vec![user1_sched1]); let user2_sched1 = VestingInfo::new::(1 * ED, 12, 10u64); let user2_sched2 = VestingInfo::new::(2 * ED, 25, 10u64); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_sched1, user2_sched2]); + assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![user2_sched1, user2_sched2]); let user12_sched1 = VestingInfo::new::(1 * ED, 12, 10u64); let user12_sched2 = VestingInfo::new::(2 * ED, 25, 10u64); let user12_sched3 = VestingInfo::new::(3 * ED, 38, 10u64); assert_eq!( - Vesting::vesting(&12).unwrap(), + mock::Vesting::vesting(&12).unwrap(), vec![user12_sched1, user12_sched2, user12_sched3] ); }); @@ -940,7 +962,7 @@ fn merge_vesting_info_handles_per_block_0() { 1, // per_block of 1, which corrects existing schedules. 10, ); - assert_eq!(Vesting::merge_vesting_info(cur_block, sched0, sched1), Some(expected_sched),); + assert_eq!(mock::Vesting::merge_vesting_info(cur_block, sched0, sched1), Some(expected_sched),); }); } From 089f5da417f42c0bbddb30147b4be818eddb2911 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Wed, 30 Jun 2021 13:35:10 -0700 Subject: [PATCH 037/144] ending_block throws error on per_block of 0 --- frame/vesting/src/lib.rs | 67 ++++---- frame/vesting/src/tests.rs | 305 +++++++++++++++++++------------------ 2 files changed, 190 insertions(+), 182 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 2fba2e6b9515d..883c08466bbfa 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -153,26 +153,28 @@ mod vesting_info { } /// Block number at which the schedule ends. - pub fn ending_block>(&self) -> Balance { + pub fn ending_block, T: Config>( + &self, + ) -> Result { let starting_block = BlockNumberToBalance::convert(self.starting_block); - let duration = if self.per_block > self.locked { + let duration = if self.per_block >= self.locked { // If `per_block` is bigger than `locked`, the schedule will end // the block after starting. One::one() } else if self.per_block.is_zero() { // Check for div by 0 errors, which should only be from legacy // vesting schedules since new ones are validated for this. - self.locked + return Err(Error::::InfiniteSchedule.into()); } else { - let has_remainder = !(self.locked % self.per_block).is_zero(); - let maybe_duration = self.locked / self.per_block; - if has_remainder { - maybe_duration + One::one() - } else { - maybe_duration - } + self.locked / self.per_block + + if (self.locked % self.per_block).is_zero() { + One::one() + } else { + Zero::zero() + } }; - starting_block.saturating_add(duration) + + Ok(starting_block.saturating_add(duration)) } } } @@ -314,6 +316,9 @@ pub mod pallet { /// Failed to create a new schedule because the parameters where invalid. i.e. `per_block` or /// `locked` was 0. InvalidScheduleParams, + /// An existing schedule was encountered that contained a `per_block` of 0, thus rendering + /// it unable to ever unlock funds. + InfiniteSchedule, } #[pallet::call] @@ -370,7 +375,7 @@ pub mod pallet { /// /// The dispatch origin for this call must be _Signed_. /// - /// - `target`: The account that should be transferred the vested funds. + /// - `target`: The account receiving the vested funds. /// - `schedule`: The vesting schedule attached to the transfer. /// /// Emits `VestingCreated`. @@ -474,7 +479,7 @@ pub mod pallet { // We can't fail from here on because we have potentially removed two schedules. let now = >::block_number(); - if let Some(s) = Self::merge_vesting_info(now, schedule1, schedule2) { + if let Some(s) = Self::merge_vesting_info(now, schedule1, schedule2)? { let mut vesting = maybe_vesting.unwrap_or_default(); if let Err(_) = vesting.try_push(s) { // It shouldn't be possible for this to fail because we removed 2 schedules above. @@ -508,21 +513,21 @@ impl Pallet { now: T::BlockNumber, schedule1: VestingInfo, T::BlockNumber>, schedule2: VestingInfo, T::BlockNumber>, - ) -> Option, T::BlockNumber>> { - let schedule1_ending_block = schedule1.ending_block::(); - let schedule2_ending_block = schedule2.ending_block::(); + ) -> Result, T::BlockNumber>>, DispatchError> { + let schedule1_ending_block = schedule1.ending_block::()?; + let schedule2_ending_block = schedule2.ending_block::()?; let now_as_balance = T::BlockNumberToBalance::convert(now); // Check if one or both schedules have ended. match (schedule1_ending_block <= now_as_balance, schedule2_ending_block <= now_as_balance) { // If both schedules have ended, we don't merge and exit early. - (true, true) => return None, + (true, true) => return Ok(None), // If one schedule has ended, we treat the one that has not ended as the new // merged schedule. - (true, false) => return Some(schedule2), - (false, true) => return Some(schedule1), + (true, false) => return Ok(Some(schedule2)), + (false, true) => return Ok(Some(schedule1)), // If neither schedule has ended don't exit early. - _ => {}, + _ => {} } let locked = schedule1 @@ -530,31 +535,33 @@ impl Pallet { .saturating_add(schedule2.locked_at::(now)); // This shouldn't happen because we know at least one ending block is greater than now. if locked.is_zero() { - return None; + return Ok(None); } let ending_block = schedule1_ending_block.max(schedule2_ending_block); let starting_block = now.max(schedule1.starting_block()).max(schedule2.starting_block()); let duration = ending_block.saturating_sub(T::BlockNumberToBalance::convert(starting_block)); - let per_block = if duration.is_zero() { - // The logic of `ending_block` guarantees that each schedule ends at least a block - // after it starts and since we take the max starting and ending_block we should never - // get here - locked - } else if duration > locked { + let per_block = if duration > locked { // This would mean we have a per_block of less than 1, which should not be not possible // because when we create the new schedule is at most the same duration as the longest, // but never greater. - 1u32.into() + One::one() } else { - locked.checked_div(&duration)? + match locked.checked_div(&duration) { + // The logic of `ending_block` guarantees that each schedule ends at least a block + // after it starts and since we take the max starting and ending_block we should never + // get here + None => locked, + Some(per_block) => per_block, + } }; // At this point inputs have been validated, so this should always be `Some`. let schedule = VestingInfo::new::(locked, per_block, starting_block); debug_assert!(schedule.validate::().is_ok()); - Some(schedule) + + Ok(Some(schedule)) } // Execute a vested transfer from `source` to `target` with the given `schedule`. diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index 54146b56ae8c3..7484118bd1137 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -19,8 +19,8 @@ use frame_support::{assert_noop, assert_ok, assert_storage_noop}; use frame_system::RawOrigin; use sp_runtime::traits::{BadOrigin, Identity}; -use super::*; -use crate::mock::{Balances, ExtBuilder, System, Test}; +use super::{Vesting as VestingStorage, *}; +use crate::mock::{Balances, ExtBuilder, System, Test, Vesting}; /// A default existential deposit. const ED: u64 = 256; @@ -52,41 +52,41 @@ fn check_vesting_status() { 64, // Vesting over 20 blocks 10, ); - assert_eq!(mock::Vesting::vesting(&1).unwrap(), vec![user1_vesting_schedule]); // Account 1 has a vesting schedule - assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); // Account 2 has a vesting schedule - assert_eq!(mock::Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); // Account 12 has a vesting schedule + assert_eq!(Vesting::vesting(&1).unwrap(), vec![user1_vesting_schedule]); // Account 1 has a vesting schedule + assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); // Account 2 has a vesting schedule + assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); // Account 12 has a vesting schedule // Account 1 has only 128 units vested from their illiquid 256 * 5 units at block 1 - assert_eq!(mock::Vesting::vesting_balance(&1), Some(128 * 9)); + assert_eq!(Vesting::vesting_balance(&1), Some(128 * 9)); // Account 2 has their full balance locked - assert_eq!(mock::Vesting::vesting_balance(&2), Some(user2_free_balance)); + assert_eq!(Vesting::vesting_balance(&2), Some(user2_free_balance)); // Account 12 has only their illiquid funds locked - assert_eq!(mock::Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); + assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); System::set_block_number(10); assert_eq!(System::block_number(), 10); // Account 1 has fully vested by block 10 - assert_eq!(mock::Vesting::vesting_balance(&1), Some(0)); + assert_eq!(Vesting::vesting_balance(&1), Some(0)); // Account 2 has started vesting by block 10 - assert_eq!(mock::Vesting::vesting_balance(&2), Some(user2_free_balance)); + assert_eq!(Vesting::vesting_balance(&2), Some(user2_free_balance)); // Account 12 has started vesting by block 10 - assert_eq!(mock::Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); + assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); System::set_block_number(30); assert_eq!(System::block_number(), 30); - assert_eq!(mock::Vesting::vesting_balance(&1), Some(0)); // Account 1 is still fully vested, and not negative - assert_eq!(mock::Vesting::vesting_balance(&2), Some(0)); // Account 2 has fully vested by block 30 - assert_eq!(mock::Vesting::vesting_balance(&12), Some(0)); // Account 2 has fully vested by block 30 + assert_eq!(Vesting::vesting_balance(&1), Some(0)); // Account 1 is still fully vested, and not negative + assert_eq!(Vesting::vesting_balance(&2), Some(0)); // Account 2 has fully vested by block 30 + assert_eq!(Vesting::vesting_balance(&12), Some(0)); // Account 2 has fully vested by block 30 // Once we unlock the funds, they are removed from storage. - assert_ok!(mock::Vesting::vest(Some(1).into())); - assert!(!>::contains_key(&1)); - assert_ok!(mock::Vesting::vest(Some(2).into())); - assert!(!>::contains_key(&2)); - assert_ok!(mock::Vesting::vest(Some(12).into())); - assert!(!>::contains_key(&12)); + assert_ok!(Vesting::vest(Some(1).into())); + assert!(!>::contains_key(&1)); + assert_ok!(Vesting::vest(Some(2).into())); + assert!(!>::contains_key(&2)); + assert_ok!(Vesting::vest(Some(12).into())); + assert!(!>::contains_key(&12)); }); } @@ -100,12 +100,12 @@ fn check_vesting_status_for_multi_schedule_account() { 10, ); // Account 2 already has a vesting schedule. - assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0]); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); // Account 2's free balance is from sched0 let free_balance = Balances::free_balance(&2); assert_eq!(free_balance, ED * (20)); - assert_eq!(mock::Vesting::vesting_balance(&2), Some(free_balance)); + assert_eq!(Vesting::vesting_balance(&2), Some(free_balance)); // Add a 2nd schedule that is already unlocking by block #1 let sched1 = VestingInfo::new::( @@ -113,14 +113,14 @@ fn check_vesting_status_for_multi_schedule_account() { ED, // Vesting over 10 blocks 0, ); - assert_ok!(mock::Vesting::vested_transfer(Some(4).into(), 2, sched1)); + assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched1)); // Free balance is equal to the two existing schedules total amount. let free_balance = Balances::free_balance(&2); assert_eq!(free_balance, ED * (10 + 20)); // The most recently added schedule exists. - assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); // sched1 has free funds at block #1, but nothing else. - assert_eq!(mock::Vesting::vesting_balance(&2), Some(free_balance - sched1.per_block())); + assert_eq!(Vesting::vesting_balance(&2), Some(free_balance - sched1.per_block())); // Add a 3rd schedule let sched2 = VestingInfo::new::( @@ -128,7 +128,7 @@ fn check_vesting_status_for_multi_schedule_account() { ED, // Vesting over 30 blocks 5, ); - assert_ok!(mock::Vesting::vested_transfer(Some(4).into(), 2, sched2)); + assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched2)); System::set_block_number(9); // Free balance is equal to the 3 existing schedules total amount. @@ -136,14 +136,14 @@ fn check_vesting_status_for_multi_schedule_account() { assert_eq!(free_balance, ED * (10 + 20 + 30)); // sched1 and sched2 are freeing funds at block #9. assert_eq!( - mock::Vesting::vesting_balance(&2), + Vesting::vesting_balance(&2), Some(free_balance - sched1.per_block() * 9 - sched2.per_block() * 4) ); System::set_block_number(20); // At block #20 sched1 is fully unlocked while sched2 and sched0 are partially unlocked. assert_eq!( - mock::Vesting::vesting_balance(&2), + Vesting::vesting_balance(&2), Some( free_balance - sched1.locked() - sched2.per_block() * 15 - sched0.per_block() * 10 ) @@ -152,20 +152,20 @@ fn check_vesting_status_for_multi_schedule_account() { System::set_block_number(30); // At block #30 sched0 and sched1 are fully unlocked while sched2 is partially unlocked. assert_eq!( - mock::Vesting::vesting_balance(&2), + Vesting::vesting_balance(&2), Some(free_balance - sched1.locked() - sched2.per_block() * 25 - sched0.locked()) ); // At block #35 sched2 fully unlocks and thus all schedules funds are unlocked. System::set_block_number(35); assert_eq!(System::block_number(), 35); - assert_eq!(mock::Vesting::vesting_balance(&2), Some(0)); + assert_eq!(Vesting::vesting_balance(&2), Some(0)); // Since we have not called any extrinsics that would unlock funds the schedules // are still in storage. - assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0, sched1, sched2]); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1, sched2]); // But once we unlock the funds, they are removed from storage. - assert_ok!(mock::Vesting::vest(Some(2).into())); - assert!(!>::contains_key(&2)); + assert_ok!(Vesting::vest(Some(2).into())); + assert!(!>::contains_key(&2)); }); } @@ -178,7 +178,7 @@ fn unvested_balance_should_not_transfer() { let user1_free_balance = Balances::free_balance(&1); assert_eq!(user1_free_balance, 100); // Account 1 has free balance // Account 1 has only 5 units vested at block 1 (plus 50 unvested) - assert_eq!(mock::Vesting::vesting_balance(&1), Some(45)); + assert_eq!(Vesting::vesting_balance(&1), Some(45)); assert_noop!( Balances::transfer(Some(1).into(), 2, 56), pallet_balances::Error::::LiquidityRestrictions, @@ -195,8 +195,8 @@ fn vested_balance_should_transfer() { let user1_free_balance = Balances::free_balance(&1); assert_eq!(user1_free_balance, 100); // Account 1 has free balance // Account 1 has only 5 units vested at block 1 (plus 50 unvested) - assert_eq!(mock::Vesting::vesting_balance(&1), Some(45)); - assert_ok!(mock::Vesting::vest(Some(1).into())); + assert_eq!(Vesting::vesting_balance(&1), Some(45)); + assert_ok!(Vesting::vest(Some(1).into())); assert_ok!(Balances::transfer(Some(1).into(), 2, 55)); }); } @@ -210,8 +210,8 @@ fn vested_balance_should_transfer_using_vest_other() { let user1_free_balance = Balances::free_balance(&1); assert_eq!(user1_free_balance, 100); // Account 1 has free balance // Account 1 has only 5 units vested at block 1 (plus 50 unvested) - assert_eq!(mock::Vesting::vesting_balance(&1), Some(45)); - assert_ok!(mock::Vesting::vest_other(Some(2).into(), 1)); + assert_eq!(Vesting::vesting_balance(&1), Some(45)); + assert_ok!(Vesting::vest_other(Some(2).into(), 1)); assert_ok!(Balances::transfer(Some(1).into(), 2, 55)); }); } @@ -232,13 +232,13 @@ fn extra_balance_should_transfer() { assert_eq!(user2_free_balance, 300); // Account 2 has 100 more free balance than normal // Account 1 has only 5 units vested at block 1 (plus 150 unvested) - assert_eq!(mock::Vesting::vesting_balance(&1), Some(45)); - assert_ok!(mock::Vesting::vest(Some(1).into())); + assert_eq!(Vesting::vesting_balance(&1), Some(45)); + assert_ok!(Vesting::vest(Some(1).into())); assert_ok!(Balances::transfer(Some(1).into(), 3, 155)); // Account 1 can send extra units gained // Account 2 has no units vested at block 1, but gained 100 - assert_eq!(mock::Vesting::vesting_balance(&2), Some(200)); - assert_ok!(mock::Vesting::vest(Some(2).into())); + assert_eq!(Vesting::vesting_balance(&2), Some(200)); + assert_ok!(Vesting::vest(Some(2).into())); assert_ok!(Balances::transfer(Some(2).into(), 3, 100)); // Account 2 can send extra units gained }); } @@ -253,7 +253,7 @@ fn liquid_funds_should_transfer_with_delayed_vesting() { assert_eq!(user12_free_balance, 2560); // Account 12 has free balance // Account 12 has liquid funds - assert_eq!(mock::Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); + assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); // Account 12 has delayed vesting let user12_vesting_schedule = VestingInfo::new::( @@ -261,7 +261,7 @@ fn liquid_funds_should_transfer_with_delayed_vesting() { 64, // Vesting over 20 blocks 10, ); - assert_eq!(mock::Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); + assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); // Account 12 can still send liquid funds assert_ok!(Balances::transfer(Some(12).into(), 3, 256 * 5)); @@ -279,38 +279,38 @@ fn vested_transfer_works() { assert_eq!(user3_free_balance, 256 * 30); assert_eq!(user4_free_balance, 256 * 40); // Account 4 should not have any vesting yet. - assert_eq!(mock::Vesting::vesting(&4), None); + assert_eq!(Vesting::vesting(&4), None); // Make the schedule for the new transfer. let new_vesting_schedule = VestingInfo::new::( 256 * 5, 64, // Vesting over 20 blocks 10, ); - assert_ok!(mock::Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule)); + assert_ok!(Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule)); // Now account 4 should have vesting. - assert_eq!(mock::Vesting::vesting(&4).unwrap(), vec![new_vesting_schedule]); + assert_eq!(Vesting::vesting(&4).unwrap(), vec![new_vesting_schedule]); // Ensure the transfer happened correctly. let user3_free_balance_updated = Balances::free_balance(&3); assert_eq!(user3_free_balance_updated, 256 * 25); let user4_free_balance_updated = Balances::free_balance(&4); assert_eq!(user4_free_balance_updated, 256 * 45); // Account 4 has 5 * 256 locked. - assert_eq!(mock::Vesting::vesting_balance(&4), Some(256 * 5)); + assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5)); System::set_block_number(20); assert_eq!(System::block_number(), 20); // Account 4 has 5 * 64 units vested by block 20. - assert_eq!(mock::Vesting::vesting_balance(&4), Some(10 * 64)); + assert_eq!(Vesting::vesting_balance(&4), Some(10 * 64)); System::set_block_number(30); assert_eq!(System::block_number(), 30); // Account 4 has fully vested - assert_eq!(mock::Vesting::vesting_balance(&4), Some(0)); + assert_eq!(Vesting::vesting_balance(&4), Some(0)); // and after unlocking its schedules are removed from storage. - assert_ok!(mock::Vesting::vest(Some(4).into())); - assert!(!>::contains_key(&4)); + assert_ok!(Vesting::vest(Some(4).into())); + assert!(!>::contains_key(&4)); }); } @@ -331,27 +331,27 @@ fn vested_transfer_correctly_fails() { 256, // Vesting over 20 blocks 10, ); - assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); // Fails due to too low transfer amount. let new_vesting_schedule_too_low = VestingInfo::new::(::MinVestedTransfer::get() - 1, 64, 10); assert_noop!( - mock::Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule_too_low), + Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule_too_low), Error::::AmountLow, ); // `per_block` is 0, which would result in a schedule with infinite duration. let schedule_per_block_0 = VestingInfo::new::(256, 0, 10); assert_noop!( - mock::Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_per_block_0), + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_per_block_0), Error::::InvalidScheduleParams, ); // `locked` is 0. let schedule_locked_0 = VestingInfo::new::(0, 1, 10); assert_noop!( - mock::Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), Error::::AmountLow, ); @@ -372,18 +372,18 @@ fn vested_transfer_allows_max_schedules() { ); // Add max amount schedules to user 4. for _ in 0 .. max_schedules { - assert_ok!(mock::Vesting::vested_transfer(Some(3).into(), 4, sched)); + assert_ok!(Vesting::vested_transfer(Some(3).into(), 4, sched)); } // The schedules count towards vesting balance let transferred_amount = ED * max_schedules as u64; - assert_eq!(mock::Vesting::vesting_balance(&4), Some(transferred_amount)); + assert_eq!(Vesting::vesting_balance(&4), Some(transferred_amount)); // and free balance. user_4_free_balance += transferred_amount; assert_eq!(Balances::free_balance(&4), user_4_free_balance); // Cannot insert a 4th vesting schedule when `MaxVestingSchedules` === 3 assert_noop!( - mock::Vesting::vested_transfer(Some(3).into(), 4, sched), + Vesting::vested_transfer(Some(3).into(), 4, sched), Error::::AtMaxVestingSchedules, ); // so the free balance does not change. @@ -391,10 +391,10 @@ fn vested_transfer_allows_max_schedules() { // Account 4 has fully vested when all the schedules end System::set_block_number(ED + 10); - assert_eq!(mock::Vesting::vesting_balance(&4), Some(0)); + assert_eq!(Vesting::vesting_balance(&4), Some(0)); // and after unlocking its schedules are removed from storage. - assert_ok!(mock::Vesting::vest(Some(4).into())); - assert!(!>::contains_key(&4)); + assert_ok!(Vesting::vest(Some(4).into())); + assert!(!>::contains_key(&4)); }); } @@ -409,7 +409,7 @@ fn force_vested_transfer_works() { assert_eq!(user3_free_balance, 256 * 30); assert_eq!(user4_free_balance, 256 * 40); // Account 4 should not have any vesting yet. - assert_eq!(mock::Vesting::vesting(&4), None); + assert_eq!(Vesting::vesting(&4), None); // Make the schedule for the new transfer. let new_vesting_schedule = VestingInfo::new::( 256 * 5, @@ -418,40 +418,40 @@ fn force_vested_transfer_works() { ); assert_noop!( - mock::Vesting::force_vested_transfer(Some(4).into(), 3, 4, new_vesting_schedule), + Vesting::force_vested_transfer(Some(4).into(), 3, 4, new_vesting_schedule), BadOrigin ); - assert_ok!(mock::Vesting::force_vested_transfer( + assert_ok!(Vesting::force_vested_transfer( RawOrigin::Root.into(), 3, 4, new_vesting_schedule )); // Now account 4 should have vesting. - assert_eq!(mock::Vesting::vesting(&4).unwrap()[0], new_vesting_schedule); - assert_eq!(mock::Vesting::vesting(&4).unwrap().len(), 1); + assert_eq!(Vesting::vesting(&4).unwrap()[0], new_vesting_schedule); + assert_eq!(Vesting::vesting(&4).unwrap().len(), 1); // Ensure the transfer happened correctly. let user3_free_balance_updated = Balances::free_balance(&3); assert_eq!(user3_free_balance_updated, 256 * 25); let user4_free_balance_updated = Balances::free_balance(&4); assert_eq!(user4_free_balance_updated, 256 * 45); // Account 4 has 5 * 256 locked. - assert_eq!(mock::Vesting::vesting_balance(&4), Some(256 * 5)); + assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5)); System::set_block_number(20); assert_eq!(System::block_number(), 20); // Account 4 has 5 * 64 units vested by block 20. - assert_eq!(mock::Vesting::vesting_balance(&4), Some(10 * 64)); + assert_eq!(Vesting::vesting_balance(&4), Some(10 * 64)); System::set_block_number(30); assert_eq!(System::block_number(), 30); // Account 4 has fully vested. - assert_eq!(mock::Vesting::vesting_balance(&4), Some(0)); + assert_eq!(Vesting::vesting_balance(&4), Some(0)); // and after unlocking its schedules are removed from storage. - assert_ok!(mock::Vesting::vest(Some(4).into())); - assert!(!>::contains_key(&4)); + assert_ok!(Vesting::vest(Some(4).into())); + assert!(!>::contains_key(&4)); }); } @@ -471,13 +471,13 @@ fn force_vested_transfer_correctly_fails() { 256, // Vesting over 20 blocks 10, ); - assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); // Too low transfer amount. let new_vesting_schedule_too_low = VestingInfo::new::(::MinVestedTransfer::get() - 1, 64, 10); assert_noop!( - mock::Vesting::force_vested_transfer( + Vesting::force_vested_transfer( RawOrigin::Root.into(), 3, 4, @@ -489,14 +489,14 @@ fn force_vested_transfer_correctly_fails() { // `per_block` is 0. let schedule_per_block_0 = VestingInfo::new::(256, 0, 10); assert_noop!( - mock::Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_per_block_0), + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_per_block_0), Error::::InvalidScheduleParams, ); // `locked` is 0. let schedule_locked_0 = VestingInfo::new::(0, 1, 10); assert_noop!( - mock::Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), Error::::AmountLow, ); @@ -517,18 +517,18 @@ fn force_vested_transfer_allows_max_schedules() { ); // Add max amount schedules to user 4. for _ in 0 .. max_schedules { - assert_ok!(mock::Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, sched)); + assert_ok!(Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, sched)); } // The schedules count towards vesting balance. let transferred_amount = ED * max_schedules as u64; - assert_eq!(mock::Vesting::vesting_balance(&4), Some(transferred_amount)); + assert_eq!(Vesting::vesting_balance(&4), Some(transferred_amount)); // and free balance. user_4_free_balance += transferred_amount; assert_eq!(Balances::free_balance(&4), user_4_free_balance); // Cannot insert a 4th vesting schedule when `MaxVestingSchedules` === 3 assert_noop!( - mock::Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, sched), + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, sched), Error::::AtMaxVestingSchedules, ); // so the free balance does not change. @@ -536,10 +536,10 @@ fn force_vested_transfer_allows_max_schedules() { // Account 4 has fully vested when all the schedules end System::set_block_number(ED + 10); - assert_eq!(mock::Vesting::vesting_balance(&4), Some(0)); + assert_eq!(Vesting::vesting_balance(&4), Some(0)); // and after unlocking its schedules are removed from storage. - assert_ok!(mock::Vesting::vest(Some(4).into())); - assert!(!>::contains_key(&4)); + assert_ok!(Vesting::vest(Some(4).into())); + assert!(!>::contains_key(&4)); }); } @@ -552,14 +552,14 @@ fn merge_schedules_that_have_not_started() { ED, // Vest over 20 blocks. 10, ); - assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0]); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); assert_eq!(Balances::usable_balance(&2), 0); // Add a schedule that is identical to the one that already exists - mock::Vesting::vested_transfer(Some(3).into(), 2, sched0).unwrap(); - assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0, sched0]); + Vesting::vested_transfer(Some(3).into(), 2, sched0).unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched0]); assert_eq!(Balances::usable_balance(&2), 0); - mock::Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); // Since we merged identical schedules, the new schedule finishes at the same // time as the original, just with double the amount @@ -568,7 +568,7 @@ fn merge_schedules_that_have_not_started() { sched0.per_block() * 2, 10, // starts at the block the schedules are merged ); - assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched1]); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched1]); assert_eq!(Balances::usable_balance(&2), 0); }); @@ -584,15 +584,15 @@ fn merge_ongoing_schedules() { ED, // Vest over 20 blocks 10, ); - assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0]); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); let sched1 = VestingInfo::new::( 256 * 10, 256, // Vest over 10 blocks sched0.starting_block() + 5, // Start at block 15 ); - mock::Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); - assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); + Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); // Got to half way through the second schedule where both schedules are actively vesting let cur_block = 20; @@ -601,7 +601,7 @@ fn merge_ongoing_schedules() { // user2 has no usable balances prior to the merge because they have not unlocked // with `vest` yet. assert_eq!(Balances::usable_balance(&2), 0); - mock::Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); // Merging schedules vests all pre-existing schedules prior to merging, which is reflected // in user2's updated usable balance @@ -614,14 +614,14 @@ fn merge_ongoing_schedules() { .locked_at::(cur_block) .saturating_add(sched0.locked_at::(cur_block)); // End block of the new schedule is the greater of either merged schedule - let sched2_end = sched1.ending_block::().max(sched0.ending_block::()); + let sched2_end = sched1.ending_block::().unwrap().max(sched0.ending_block::().unwrap()); let sched2_duration = sched2_end - cur_block; // Based off the new schedules total locked and its duration, we can calculate the // amount to unlock per block. let sched2_per_block = sched2_locked / sched2_duration; let sched2 = VestingInfo::new::(sched2_locked, sched2_per_block, cur_block); - assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched2]); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2]); }); } @@ -647,7 +647,7 @@ fn merging_shifts_other_schedules_index() { ); // Account 3 start out with no schedules - assert_eq!(mock::Vesting::vesting(&3), None); + assert_eq!(Vesting::vesting(&3), None); // and some usable balance. let usable_balance = Balances::usable_balance(&3); assert_eq!(usable_balance, 30 * ED); @@ -656,16 +656,16 @@ fn merging_shifts_other_schedules_index() { assert_eq!(System::block_number(), cur_block); // Transfer the above 3 schedules to account 3 - assert_ok!(mock::Vesting::vested_transfer(Some(4).into(), 3, sched0)); - assert_ok!(mock::Vesting::vested_transfer(Some(4).into(), 3, sched1)); - assert_ok!(mock::Vesting::vested_transfer(Some(4).into(), 3, sched2)); + assert_ok!(Vesting::vested_transfer(Some(4).into(), 3, sched0)); + assert_ok!(Vesting::vested_transfer(Some(4).into(), 3, sched1)); + assert_ok!(Vesting::vested_transfer(Some(4).into(), 3, sched2)); // With no schedules vested or merged they are in the order they are created - assert_eq!(mock::Vesting::vesting(&3).unwrap(), vec![sched0, sched1, sched2]); + assert_eq!(Vesting::vesting(&3).unwrap(), vec![sched0, sched1, sched2]); // and the usable balance has not changed. assert_eq!(usable_balance, Balances::usable_balance(&3)); - assert_ok!(mock::Vesting::merge_schedules(Some(3).into(), 0, 2)); + assert_ok!(Vesting::merge_schedules(Some(3).into(), 0, 2)); // Create the merged schedule of sched0 & sched2. // The merged schedule will have the max possible starting block, @@ -674,13 +674,13 @@ fn merging_shifts_other_schedules_index() { let sched3_locked = sched2.locked_at::(cur_block) + sched0.locked_at::(cur_block); // and will end at the max possible block. - let sched3_end = sched2.ending_block::().max(sched0.ending_block::()); + let sched3_end = sched2.ending_block::().unwrap().max(sched0.ending_block::().unwrap()); let sched3_duration = sched3_end - sched3_start; let sched3_per_block = sched3_locked / sched3_duration; let sched3 = VestingInfo::new::(sched3_locked, sched3_per_block, sched3_start); // The not touched schedule moves left and the new merged schedule is appended. - assert_eq!(mock::Vesting::vesting(&3).unwrap(), vec![sched1, sched3]); + assert_eq!(Vesting::vesting(&3).unwrap(), vec![sched1, sched3]); // The usable balance hasn't changed since none of the schedules have started. assert_eq!(Balances::usable_balance(&3), usable_balance); }); @@ -697,10 +697,10 @@ fn merge_ongoing_and_yet_to_be_started_schedule() { ED, // Vesting over 20 blocks 10, ); - assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0]); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); // Fast forward to half way through the life of sched_1 - let mut cur_block = (sched0.starting_block() + sched0.ending_block::()) / 2; + let mut cur_block = (sched0.starting_block() + sched0.ending_block::().unwrap()) / 2; assert_eq!(cur_block, 20); System::set_block_number(cur_block); @@ -708,7 +708,7 @@ fn merge_ongoing_and_yet_to_be_started_schedule() { let mut usable_balance = 0; assert_eq!(Balances::usable_balance(&2), usable_balance); // Vest the current schedules (which is just sched0 now). - mock::Vesting::vest(Some(2).into()).unwrap(); + Vesting::vest(Some(2).into()).unwrap(); // After vesting the usable balance increases by the amount the unlocked amount. let sched0_vested_now = sched0.locked() - sched0.locked_at::(cur_block); @@ -725,10 +725,10 @@ fn merge_ongoing_and_yet_to_be_started_schedule() { 1, // Vesting over 256 * 10 (2560) blocks cur_block + 1, ); - mock::Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); + Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); // Merge the schedules before sched1 starts. - mock::Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); // After merging, the usable balance only changes by the amount sched0 vested since we // last called `vest` (which is just 1 block). The usable balance is not affected by // sched1 because it has not started yet. @@ -741,12 +741,12 @@ fn merge_ongoing_and_yet_to_be_started_schedule() { let sched2_locked = sched0.locked_at::(cur_block) + sched1.locked_at::(cur_block); // and will end at the max possible block. - let sched2_end = sched0.ending_block::().max(sched1.ending_block::()); + let sched2_end = sched0.ending_block::().unwrap().max(sched1.ending_block::().unwrap()); let sched2_duration = sched2_end - sched2_start; let sched2_per_block = sched2_locked / sched2_duration; let sched2 = VestingInfo::new::(sched2_locked, sched2_per_block, sched2_start); - assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched2]); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2]); }); } @@ -761,14 +761,14 @@ fn merge_finishing_and_ongoing_schedule() { ED, // Vesting over 20 blocks. 10, ); - assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0]); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); let sched1 = VestingInfo::new::( ED * 40, ED, // Vesting over 40 blocks. 10, ); - assert_ok!(mock::Vesting::vested_transfer(Some(4).into(), 2, sched1)); + assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched1)); // Transfer a 3rd schedule, so we can demonstrate how schedule indices change. // (We are not merging this schedule.) @@ -777,25 +777,25 @@ fn merge_finishing_and_ongoing_schedule() { ED, // Vesting over 30 blocks. 10, ); - assert_ok!(mock::Vesting::vested_transfer(Some(3).into(), 2, sched2)); + assert_ok!(Vesting::vested_transfer(Some(3).into(), 2, sched2)); // The schedules are in expected order prior to merging. - assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0, sched1, sched2]); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1, sched2]); // Fast forward to sched0's end block. - let cur_block = sched0.ending_block::(); + let cur_block = sched0.ending_block::().unwrap(); System::set_block_number(cur_block); assert_eq!(System::block_number(), 30); // Prior to merge_schedules and with no vest/vest_other called the user has no usable // balance. assert_eq!(Balances::usable_balance(&2), 0); - assert_ok!(mock::Vesting::merge_schedules(Some(2).into(), 0, 1)); + assert_ok!(Vesting::merge_schedules(Some(2).into(), 0, 1)); // sched2 is now the first, since sched0 & sched1 get filtered out while "merging". // sched1 gets treated like the new merged schedule by getting pushed onto back // of the vesting schedules vec. Note: sched0 finished at the current block. - assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched2, sched1]); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2, sched1]); // sched0 has finished, so its funds are fully unlocked. let sched0_unlocked_now = sched0.locked(); @@ -823,7 +823,7 @@ fn merge_finishing_schedules_does_not_create_a_new_one() { ED, // 20 block duration. 10, ); - assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0]); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); // Create sched1 and transfer it to account 2. let sched1 = VestingInfo::new::( @@ -831,11 +831,11 @@ fn merge_finishing_schedules_does_not_create_a_new_one() { ED, // 30 block duration. 10, ); - assert_ok!(mock::Vesting::vested_transfer(Some(3).into(), 2, sched1)); - assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); + assert_ok!(Vesting::vested_transfer(Some(3).into(), 2, sched1)); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); let all_scheds_end = - sched0.ending_block::().max(sched1.ending_block::()); + sched0.ending_block::().unwrap().max(sched1.ending_block::().unwrap()); assert_eq!(all_scheds_end, 40); System::set_block_number(all_scheds_end); @@ -844,10 +844,10 @@ fn merge_finishing_schedules_does_not_create_a_new_one() { assert_eq!(Balances::usable_balance(&2), 0); // Merge schedule 0 and 1. - mock::Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); // The user no longer has any more vesting schedules because they both ended at the // block they where merged. - assert!(!>::contains_key(&2)); + assert!(!>::contains_key(&2)); // And their usable balance has increased by the total amount locked in the merged // schedules. assert_eq!(Balances::usable_balance(&2), sched0.locked() + sched1.locked()); @@ -863,28 +863,28 @@ fn merge_schedules_throws_proper_errors() { ED, // 20 block duration. 10, ); - assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0]); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); // There is only 1 vesting schedule. assert_noop!( - mock::Vesting::merge_schedules(Some(2).into(), 0, 1), + Vesting::merge_schedules(Some(2).into(), 0, 1), Error::::ScheduleIndexOutOfBounds ); // There are 0 vesting schedules. - assert_eq!(mock::Vesting::vesting(&4), None); - assert_noop!(mock::Vesting::merge_schedules(Some(4).into(), 0, 1), Error::::NotVesting); + assert_eq!(Vesting::vesting(&4), None); + assert_noop!(Vesting::merge_schedules(Some(4).into(), 0, 1), Error::::NotVesting); // There are enough schedules to merge but an index is non-existent. - mock::Vesting::vested_transfer(Some(3).into(), 2, sched0).unwrap(); - assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![sched0, sched0]); + Vesting::vested_transfer(Some(3).into(), 2, sched0).unwrap(); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched0]); assert_noop!( - mock::Vesting::merge_schedules(Some(2).into(), 0, 2), + Vesting::merge_schedules(Some(2).into(), 0, 2), Error::::ScheduleIndexOutOfBounds ); // It is a storage noop with no errors if the indexes are the same. - assert_storage_noop!(mock::Vesting::merge_schedules(Some(2).into(), 0, 0).unwrap()); + assert_storage_noop!(Vesting::merge_schedules(Some(2).into(), 0, 0).unwrap()); }); } @@ -910,17 +910,17 @@ fn generates_multiple_schedules_from_genesis_config() { .build() .execute_with(|| { let user1_sched1 = VestingInfo::new::(5 * ED, 128, 0u64); - assert_eq!(mock::Vesting::vesting(&1).unwrap(), vec![user1_sched1]); + assert_eq!(Vesting::vesting(&1).unwrap(), vec![user1_sched1]); let user2_sched1 = VestingInfo::new::(1 * ED, 12, 10u64); let user2_sched2 = VestingInfo::new::(2 * ED, 25, 10u64); - assert_eq!(mock::Vesting::vesting(&2).unwrap(), vec![user2_sched1, user2_sched2]); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_sched1, user2_sched2]); let user12_sched1 = VestingInfo::new::(1 * ED, 12, 10u64); let user12_sched2 = VestingInfo::new::(2 * ED, 25, 10u64); let user12_sched3 = VestingInfo::new::(3 * ED, 38, 10u64); assert_eq!( - mock::Vesting::vesting(&12).unwrap(), + Vesting::vesting(&12).unwrap(), vec![user12_sched1, user12_sched2, user12_sched3] ); }); @@ -940,7 +940,7 @@ fn multiple_schedules_from_genesis_config_errors() { } #[test] -fn merge_vesting_info_handles_per_block_0() { +fn merge_vesting_errors_with_per_block_0() { // Faulty schedules with an infinite duration (per_block == 0) can be merged to create // a schedule that vest 1 per_block, (helpful for faulty, legacy schedules). ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { @@ -948,21 +948,21 @@ fn merge_vesting_info_handles_per_block_0() { ED, 0, // Vesting over infinite blocks. 1, ); + assert_eq!( + sched0.ending_block::(), + Err(Error::::InfiniteSchedule.into()) + ); let sched1 = VestingInfo::new::( ED * 2, - 0, // Vesting over infinite blocks. + 1, // Vesting over 512 blocks. 10, ); + assert_eq!(sched1.ending_block::(), Ok(512u32 + 10)); - let cur_block = 5; - let expected_locked = - sched0.locked_at::(cur_block) + sched1.locked_at::(cur_block); - let expected_sched = VestingInfo::new::( - expected_locked, - 1, // per_block of 1, which corrects existing schedules. - 10, + assert_eq!( + Vesting::merge_vesting_info(5, sched0, sched1), + Err(Error::::InfiniteSchedule.into()) ); - assert_eq!(mock::Vesting::merge_vesting_info(cur_block, sched0, sched1), Some(expected_sched),); }); } @@ -1002,35 +1002,36 @@ fn vesting_info_validation_works() { #[test] fn vesting_info_ending_block_works() { - // Treats `per_block` 0 as a `per_block` of 1 to accommodate faulty, legacy schedules. + // Treats `per_block` 0 as an error let per_block_0 = VestingInfo::new::(256u32, 0u32, 10u32); assert_eq!( - per_block_0.ending_block::(), - per_block_0.locked() + per_block_0.starting_block() + per_block_0.ending_block::(), + Err(Error::::InfiniteSchedule.into()) ); - let per_block_1 = VestingInfo::new::(256u32, 1u32, 10u32); - assert_eq!(per_block_0.ending_block::(), per_block_1.ending_block::()); + // let per_block_1 = VestingInfo::new::(256u32, 1u32, 10u32); + // assert_eq!(per_block_0.ending_block::().unwrap(), per_block_1.ending_block::().unwrap()); // `per_block >= locked` always results in a schedule ending the block after it starts let per_block_gt_locked = VestingInfo::new::(256u32, 256 * 2u32, 10u32); assert_eq!( - per_block_gt_locked.ending_block::(), + per_block_gt_locked.ending_block::().unwrap(), 1 + per_block_gt_locked.starting_block() ); let per_block_eq_locked = VestingInfo::new::(256u32, 256u32, 10u32); assert_eq!( - per_block_gt_locked.ending_block::(), - per_block_eq_locked.ending_block::() + per_block_gt_locked.ending_block::().unwrap(), + per_block_eq_locked.ending_block::().unwrap() ); // Correctly calcs end if `locked % per_block != 0`. (We need a block to unlock the remainder). let imperfect_per_block = VestingInfo::new::(256u32, 250u32, 10u32); assert_eq!( - imperfect_per_block.ending_block::(), + imperfect_per_block.ending_block::().unwrap(), imperfect_per_block.starting_block() + 2u32, ); assert_eq!( - imperfect_per_block.locked_at::(imperfect_per_block.ending_block::()), + imperfect_per_block + .locked_at::(imperfect_per_block.ending_block::().unwrap()), 0 ); } From 3a3d227d29d1ec0d3c8b1bad985f04486d7c02cd Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Wed, 30 Jun 2021 13:52:16 -0700 Subject: [PATCH 038/144] Try improve merge vesting info comment --- frame/vesting/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 883c08466bbfa..6b388662476fe 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -508,7 +508,7 @@ pub mod pallet { impl Pallet { // Create a new `VestingInfo`, based off of two other `VestingInfo`s. - // NOTE: We assume both schedules have been vested up through the current block. + // NOTE: We assume both schedules have had funds unlocked up through the current block. fn merge_vesting_info( now: T::BlockNumber, schedule1: VestingInfo, T::BlockNumber>, From 0b276543cf83820f69ed80bcf2e89a79502c9dd7 Mon Sep 17 00:00:00 2001 From: Zeke Mostov <32168567+emostov@users.noreply.github.com> Date: Wed, 30 Jun 2021 13:56:19 -0700 Subject: [PATCH 039/144] Update frame/vesting/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- frame/vesting/src/lib.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 6b388662476fe..4716fbda87aea 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -575,12 +575,7 @@ impl Pallet { let target = T::Lookup::lookup(target)?; let source = T::Lookup::lookup(source)?; - if let Some(len) = Vesting::::decode_len(&target) { - ensure!( - len < T::MaxVestingSchedules::get() as usize, - Error::::AtMaxVestingSchedules - ); - } + ensure!(Vesting::::decode_len(&target).unwrap_or_default() < T::MaxVestingSchedules::get() as usize); T::Currency::transfer( &source, From 899859eadfc8eacecbbb705997bfb7b254ae6997 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Wed, 30 Jun 2021 15:42:45 -0700 Subject: [PATCH 040/144] add validate + correct; handle duration > blocknumber --- frame/vesting/src/lib.rs | 68 +++++++++++++++++++++++++------------- frame/vesting/src/tests.rs | 47 ++++++++++++++++++-------- 2 files changed, 78 insertions(+), 37 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 4716fbda87aea..3625850c3a154 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -65,8 +65,8 @@ use frame_system::{ensure_root, ensure_signed, pallet_prelude::*}; pub use pallet::*; use sp_runtime::{ traits::{ - AtLeast32BitUnsigned, CheckedDiv, Convert, MaybeSerializeDeserialize, One, Saturating, - StaticLookup, Zero, + AtLeast32BitUnsigned, Bounded, CheckedDiv, Convert, MaybeSerializeDeserialize, One, + Saturating, StaticLookup, Zero, }, RuntimeDebug, }; @@ -96,8 +96,10 @@ mod vesting_info { starting_block: BlockNumber, } - impl - VestingInfo + impl VestingInfo + where + Balance: AtLeast32BitUnsigned + Copy, + BlockNumber: AtLeast32BitUnsigned + Copy + Bounded, { /// Instantiate a new `VestingInfo`. pub fn new( @@ -109,16 +111,26 @@ mod vesting_info { } /// Validate parameters for `VestingInfo`. Note that this does not check - /// against `MinVestedTransfer` or the current block. Additionally it - /// will correct `per_block` if it is greater than `locked`. - pub fn validate(mut self) -> Result> { - ensure!( - !self.locked.is_zero() && !self.per_block.is_zero(), - Error::::InvalidScheduleParams - ); + /// against `MinVestedTransfer` or the current block. + pub fn validate, T: Config>( + &self, + ) -> Result<(), Error> { + ensure!(!self.locked.is_zero(), Error::::InvalidScheduleParams); + + let max_block = BlockNumberToBalance::convert(BlockNumber::max_value()); + match self.locked.checked_div(&self.per_block) { + None => return Err(Error::::InfiniteSchedule), // `per_block` is 0 + Some(duration) => ensure!(duration < max_block, Error::::InfiniteSchedule), + }; + + Ok(()) + } + + /// Potentially correct the `per_block` of a schedule. Typically called after `validate`. + pub fn correct(mut self) -> Self { self.per_block = if self.per_block > self.locked { self.locked } else { self.per_block }; - Ok(self) + self } /// Locked amount at schedule creation. @@ -168,9 +180,11 @@ mod vesting_info { } else { self.locked / self.per_block + if (self.locked % self.per_block).is_zero() { - One::one() - } else { Zero::zero() + } else { + // `per_block` does not perfectly divide `locked`, so we need an extra block to + // unlock some amount less than per_block + One::one() } }; @@ -272,8 +286,8 @@ pub mod pallet { let locked = balance.saturating_sub(liquid); let length_as_balance = T::BlockNumberToBalance::convert(length); let per_block = locked / length_as_balance.max(sp_runtime::traits::One::one()); - let vesting_info = VestingInfo::new::(locked, per_block, begin) - .validate::() + let vesting_info = VestingInfo::new::(locked, per_block, begin); + vesting_info.validate::() .expect("Invalid VestingInfo params at genesis"); Vesting::::try_append(who, vesting_info) @@ -313,11 +327,10 @@ pub mod pallet { AmountLow, /// At least one of the indexes is out of bounds of the vesting schedules. ScheduleIndexOutOfBounds, - /// Failed to create a new schedule because the parameters where invalid. i.e. `per_block` or - /// `locked` was 0. + /// Failed to create a new schedule because some parameters where invalid. e.g. `locked` was 0. InvalidScheduleParams, - /// An existing schedule was encountered that contained a `per_block` of 0, thus rendering - /// it unable to ever unlock funds. + /// A schedule contained a `per_block` of 0 or `locked / per_block > BlockNumber::max_value()`, + /// thus rendering it unable to ever fully unlock funds. InfiniteSchedule, } @@ -535,6 +548,10 @@ impl Pallet { .saturating_add(schedule2.locked_at::(now)); // This shouldn't happen because we know at least one ending block is greater than now. if locked.is_zero() { + log::warn!( + target: LOG_TARGET, + "merge_vesting_info validation checks failed to catch a locked of 0" + ); return Ok(None); } @@ -559,7 +576,7 @@ impl Pallet { // At this point inputs have been validated, so this should always be `Some`. let schedule = VestingInfo::new::(locked, per_block, starting_block); - debug_assert!(schedule.validate::().is_ok()); + debug_assert!(schedule.validate::().is_ok()); Ok(Some(schedule)) } @@ -571,11 +588,16 @@ impl Pallet { schedule: VestingInfo, T::BlockNumber>, ) -> DispatchResult { ensure!(schedule.locked() > T::MinVestedTransfer::get(), Error::::AmountLow); - let schedule = schedule.validate::()?; + schedule.validate::()?; + let schedule = schedule.correct(); let target = T::Lookup::lookup(target)?; let source = T::Lookup::lookup(source)?; - ensure!(Vesting::::decode_len(&target).unwrap_or_default() < T::MaxVestingSchedules::get() as usize); + ensure!( + Vesting::::decode_len(&target).unwrap_or_default() < + T::MaxVestingSchedules::get() as usize, + Error::::AtMaxVestingSchedules + ); T::Currency::transfer( &source, diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index 7484118bd1137..a5644f2a57938 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -344,8 +344,15 @@ fn vested_transfer_correctly_fails() { // `per_block` is 0, which would result in a schedule with infinite duration. let schedule_per_block_0 = VestingInfo::new::(256, 0, 10); assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_per_block_0), - Error::::InvalidScheduleParams, + Vesting::vested_transfer(Some(3).into(), 4, schedule_per_block_0), + Error::::InfiniteSchedule, + ); + + // `locked / per_block > BlockNumber::max_value()` + let schedule_duration_gt_max_blocknumber = VestingInfo::new::(u64::MAX, 1, 10); + assert_noop!( + Vesting::vested_transfer(Some(3).into(), 4, schedule_duration_gt_max_blocknumber), + Error::::InfiniteSchedule, ); // `locked` is 0. @@ -490,7 +497,14 @@ fn force_vested_transfer_correctly_fails() { let schedule_per_block_0 = VestingInfo::new::(256, 0, 10); assert_noop!( Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_per_block_0), - Error::::InvalidScheduleParams, + Error::::InfiniteSchedule, + ); + + // `locked / per_block > BlockNumber::max_value()` + let schedule_duration_gt_max_blocknumber = VestingInfo::new::(u64::MAX, 1, 10); + assert_noop!( + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_duration_gt_max_blocknumber), + Error::::InfiniteSchedule, ); // `locked` is 0. @@ -957,7 +971,7 @@ fn merge_vesting_errors_with_per_block_0() { 1, // Vesting over 512 blocks. 10, ); - assert_eq!(sched1.ending_block::(), Ok(512u32 + 10)); + assert_eq!(sched1.ending_block::(), Ok(512u64 + 10)); assert_eq!( Vesting::merge_vesting_info(5, sched0, sched1), @@ -967,35 +981,40 @@ fn merge_vesting_errors_with_per_block_0() { } #[test] -fn vesting_info_validation_works() { +fn vesting_info_validate_and_correct_works() { let min_transfer = ::MinVestedTransfer::get(); // Does not check for min transfer. - match VestingInfo::new::(min_transfer - 1, 1u64, 10u64).validate::() { + match VestingInfo::new::(min_transfer - 1, 1u64, 10u64).validate::() { Ok(_) => (), _ => panic!(), } // `locked` cannot be 0. - match VestingInfo::new::(0, 1u64, 10u64).validate::() { + match VestingInfo::new::(0, 1u64, 10u64).validate::() { Err(Error::::InvalidScheduleParams) => (), _ => panic!(), } // `per_block` cannot be 0. - match VestingInfo::new::(min_transfer + 1, 0u64, 10u64).validate() { - Err(Error::::InvalidScheduleParams) => (), + match VestingInfo::new::(min_transfer + 1, 0u64, 10u64).validate::() { + Err(Error::::InfiniteSchedule) => (), + _ => panic!(), + } + + // `locked / per_block` cannot be bigger than BlockNumber::max_value` + match VestingInfo::new::(u64::MAX, 1u64, 10u64).validate::() { + Err(Error::::InfiniteSchedule) => (), _ => panic!(), } // With valid inputs it does not error and does not modify the inputs. - assert_eq!( - VestingInfo::new::(min_transfer, 1u64, 10u64).validate::().unwrap(), - VestingInfo::new::(min_transfer, 1u64, 10u64) - ); + let valid = VestingInfo::new::(min_transfer, 1u64, 10u64); + assert_ok!(valid.validate::()); + assert_eq!(valid.correct(), VestingInfo::new::(min_transfer, 1u64, 10u64)); // `per_block` is never bigger than `locked`. assert_eq!( - VestingInfo::new::(256u64, 256 * 2u64, 10u64).validate::().unwrap(), + VestingInfo::new::(256u64, 256 * 2u64, 10u64).correct(), VestingInfo::new::(256u64, 256u64, 10u64) ); } From 9924ddd614d79a91254f777da55215c71fd7e450 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Wed, 30 Jun 2021 16:07:53 -0700 Subject: [PATCH 041/144] Move vesting_info module to its own file --- frame/vesting/src/lib.rs | 112 +----------------------------- frame/vesting/src/vesting_info.rs | 108 ++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 111 deletions(-) create mode 100644 frame/vesting/src/vesting_info.rs diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 3625850c3a154..4a32ecff68713 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -49,6 +49,7 @@ mod benchmarking; mod mock; #[cfg(test)] mod tests; +mod vesting_info; pub mod weights; @@ -82,117 +83,6 @@ type MaxLocksOf = const VESTING_ID: LockIdentifier = *b"vesting "; const LOG_TARGET: &'static str = "runtime::vesting"; -// Module to enforce private fields on `VestingInfo` -mod vesting_info { - use super::*; - /// Struct to encode the vesting schedule of an individual account. - #[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug)] - pub struct VestingInfo { - /// Locked amount at genesis. - locked: Balance, - /// Amount that gets unlocked every block after `starting_block`. - per_block: Balance, - /// Starting block for unlocking(vesting). - starting_block: BlockNumber, - } - - impl VestingInfo - where - Balance: AtLeast32BitUnsigned + Copy, - BlockNumber: AtLeast32BitUnsigned + Copy + Bounded, - { - /// Instantiate a new `VestingInfo`. - pub fn new( - locked: Balance, - per_block: Balance, - starting_block: BlockNumber, - ) -> VestingInfo { - VestingInfo { locked, per_block, starting_block } - } - - /// Validate parameters for `VestingInfo`. Note that this does not check - /// against `MinVestedTransfer` or the current block. - pub fn validate, T: Config>( - &self, - ) -> Result<(), Error> { - ensure!(!self.locked.is_zero(), Error::::InvalidScheduleParams); - - let max_block = BlockNumberToBalance::convert(BlockNumber::max_value()); - match self.locked.checked_div(&self.per_block) { - None => return Err(Error::::InfiniteSchedule), // `per_block` is 0 - Some(duration) => ensure!(duration < max_block, Error::::InfiniteSchedule), - }; - - Ok(()) - } - - /// Potentially correct the `per_block` of a schedule. Typically called after `validate`. - pub fn correct(mut self) -> Self { - self.per_block = - if self.per_block > self.locked { self.locked } else { self.per_block }; - self - } - - /// Locked amount at schedule creation. - pub fn locked(&self) -> Balance { - self.locked - } - - /// Amount that gets unlocked every block after `starting_block`. - pub fn per_block(&self) -> Balance { - self.per_block - } - - /// Starting block for unlocking(vesting). - pub fn starting_block(&self) -> BlockNumber { - self.starting_block - } - - /// Amount locked at block `n`. - pub fn locked_at>( - &self, - n: BlockNumber, - ) -> Balance { - // Number of blocks that count toward vesting - // Saturating to 0 when n < starting_block - let vested_block_count = n.saturating_sub(self.starting_block); - let vested_block_count = BlockNumberToBalance::convert(vested_block_count); - // Return amount that is still locked in vesting - vested_block_count - .checked_mul(&self.per_block) - .map(|to_unlock| self.locked.saturating_sub(to_unlock)) - .unwrap_or(Zero::zero()) - } - - /// Block number at which the schedule ends. - pub fn ending_block, T: Config>( - &self, - ) -> Result { - let starting_block = BlockNumberToBalance::convert(self.starting_block); - let duration = if self.per_block >= self.locked { - // If `per_block` is bigger than `locked`, the schedule will end - // the block after starting. - One::one() - } else if self.per_block.is_zero() { - // Check for div by 0 errors, which should only be from legacy - // vesting schedules since new ones are validated for this. - return Err(Error::::InfiniteSchedule.into()); - } else { - self.locked / self.per_block + - if (self.locked % self.per_block).is_zero() { - Zero::zero() - } else { - // `per_block` does not perfectly divide `locked`, so we need an extra block to - // unlock some amount less than per_block - One::one() - } - }; - - Ok(starting_block.saturating_add(duration)) - } - } -} - /// The indexes of vesting schedules to remove from an accounts vesting schedule collection. enum Filter { /// Do not filter out any schedules. diff --git a/frame/vesting/src/vesting_info.rs b/frame/vesting/src/vesting_info.rs new file mode 100644 index 0000000000000..f7fe808d92f60 --- /dev/null +++ b/frame/vesting/src/vesting_info.rs @@ -0,0 +1,108 @@ +//! Module to enforce private fields on `VestingInfo` +use super::*; + +/// Struct to encode the vesting schedule of an individual account. +#[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug)] +pub struct VestingInfo { + /// Locked amount at genesis. + locked: Balance, + /// Amount that gets unlocked every block after `starting_block`. + per_block: Balance, + /// Starting block for unlocking(vesting). + starting_block: BlockNumber, +} + +impl VestingInfo +where + Balance: AtLeast32BitUnsigned + Copy, + BlockNumber: AtLeast32BitUnsigned + Copy + Bounded, +{ + /// Instantiate a new `VestingInfo`. + pub fn new( + locked: Balance, + per_block: Balance, + starting_block: BlockNumber, + ) -> VestingInfo { + VestingInfo { locked, per_block, starting_block } + } + + /// Validate parameters for `VestingInfo`. Note that this does not check + /// against `MinVestedTransfer` or the current block. + pub fn validate, T: Config>( + &self, + ) -> Result<(), Error> { + ensure!(!self.locked.is_zero(), Error::::InvalidScheduleParams); + + let max_block = BlockNumberToBalance::convert(BlockNumber::max_value()); + match self.locked.checked_div(&self.per_block) { + None => return Err(Error::::InfiniteSchedule), // `per_block` is 0 + Some(duration) => ensure!(duration < max_block, Error::::InfiniteSchedule), + }; + + Ok(()) + } + + /// Potentially correct the `per_block` of a schedule. Typically called after `validate`. + pub fn correct(mut self) -> Self { + self.per_block = if self.per_block > self.locked { self.locked } else { self.per_block }; + self + } + + /// Locked amount at schedule creation. + pub fn locked(&self) -> Balance { + self.locked + } + + /// Amount that gets unlocked every block after `starting_block`. + pub fn per_block(&self) -> Balance { + self.per_block + } + + /// Starting block for unlocking(vesting). + pub fn starting_block(&self) -> BlockNumber { + self.starting_block + } + + /// Amount locked at block `n`. + pub fn locked_at>( + &self, + n: BlockNumber, + ) -> Balance { + // Number of blocks that count toward vesting + // Saturating to 0 when n < starting_block + let vested_block_count = n.saturating_sub(self.starting_block); + let vested_block_count = BlockNumberToBalance::convert(vested_block_count); + // Return amount that is still locked in vesting + vested_block_count + .checked_mul(&self.per_block) + .map(|to_unlock| self.locked.saturating_sub(to_unlock)) + .unwrap_or(Zero::zero()) + } + + /// Block number at which the schedule ends. + pub fn ending_block, T: Config>( + &self, + ) -> Result { + let starting_block = BlockNumberToBalance::convert(self.starting_block); + let duration = if self.per_block >= self.locked { + // If `per_block` is bigger than `locked`, the schedule will end + // the block after starting. + One::one() + } else if self.per_block.is_zero() { + // Check for div by 0 errors, which should only be from legacy + // vesting schedules since new ones are validated for this. + return Err(Error::::InfiniteSchedule.into()); + } else { + self.locked / self.per_block + + if (self.locked % self.per_block).is_zero() { + Zero::zero() + } else { + // `per_block` does not perfectly divide `locked`, so we need an extra block to + // unlock some amount less than per_block + One::one() + } + }; + + Ok(starting_block.saturating_add(duration)) + } +} From 4aaa158e32d79a9ef206e594933ca77e09e7d04e Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Wed, 30 Jun 2021 18:41:13 -0700 Subject: [PATCH 042/144] Seperate Vesting/locks updates from writing --- frame/vesting/src/lib.rs | 219 ++++++++++++++++++++++++++------------- 1 file changed, 146 insertions(+), 73 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 4a32ecff68713..e87328a74e19b 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -83,23 +83,23 @@ type MaxLocksOf = const VESTING_ID: LockIdentifier = *b"vesting "; const LOG_TARGET: &'static str = "runtime::vesting"; -/// The indexes of vesting schedules to remove from an accounts vesting schedule collection. -enum Filter { - /// Do not filter out any schedules. - Zero, - /// Filter out 1 schedule. - One(usize), - /// Filter out 2 schedules. - Two(usize, usize), +/// Actions to take on a users `Vesting` storage entry. +enum VestingAction { + /// Do not actively remove any schedules. + Passive, + /// Remove the schedules specified by the index. + Remove(usize), + /// Remove the two schedules, specified by index, so they can be merged. + Merge(usize, usize), } -impl Filter { +impl VestingAction { /// Whether or not the filter says the schedule index should be removed. fn should_remove(&self, index: &usize) -> bool { match self { - Self::Zero => false, - Self::One(index1) => index1 == index, - Self::Two(index1, index2) => index1 == index || index2 == index, + Self::Passive => false, + Self::Remove(index1) => index1 == index, + Self::Merge(index1, index2) => index1 == index || index2 == index, } } } @@ -200,9 +200,9 @@ pub mod pallet { VestingUpdated(T::AccountId, BalanceOf), /// An \[account\] has become fully vested. No further vesting can happen. VestingCompleted(T::AccountId), - /// 2 vesting schedules where successfully merged together. + /// 2 vesting schedules where successfully merged together and the merged schedule was added. /// \[locked, per_block, starting_block\] - VestingMergeSuccess(BalanceOf, BalanceOf, T::BlockNumber), + MergedScheduleAdded(BalanceOf, BalanceOf, T::BlockNumber), } /// Error for the vesting pallet. @@ -371,38 +371,53 @@ pub mod pallet { // The schedule index is based off of the schedule ordering prior to filtering out any // schedules that may be ending at this block. - let schedule1 = *vesting.get(schedule1_index).ok_or(Error::::ScheduleIndexOutOfBounds)?; - let schedule2 = *vesting.get(schedule2_index).ok_or(Error::::ScheduleIndexOutOfBounds)?; - let filter = Filter::Two(schedule1_index, schedule2_index); + let schedule1 = + *vesting.get(schedule1_index).ok_or(Error::::ScheduleIndexOutOfBounds)?; + let schedule2 = + *vesting.get(schedule2_index).ok_or(Error::::ScheduleIndexOutOfBounds)?; + let merge_action = VestingAction::Merge(schedule1_index, schedule2_index); - // The length of vesting decreases by 2 here since we filter out 2 schedules. Thus we know + // The length of vesting decreases by 2 here since wem filter out 2 schedules. Thus we know // below that we can safely insert the new merged schedule. - let maybe_vesting = Self::update_lock_and_schedules(who.clone(), vesting, filter); - - // We can't fail from here on because we have potentially removed two schedules. + let (mut schedules, mut locked_now) = + Self::report_schedule_updates(vesting, merge_action); let now = >::block_number(); - if let Some(s) = Self::merge_vesting_info(now, schedule1, schedule2)? { - let mut vesting = maybe_vesting.unwrap_or_default(); - if let Err(_) = vesting.try_push(s) { - // It shouldn't be possible for this to fail because we removed 2 schedules above. + if let Some(new_schedule) = Self::merge_vesting_info(now, schedule1, schedule2)? { + // Merging created a new schedule; so now we need to add it to the + // accounts vesting schedules, + if let Err(_) = schedules.try_push(new_schedule) { + // (It shouldn't be possible for this to fail because we removed 2 schedules above.) log::warn!( target: LOG_TARGET, "faulty logic led to attempting to add too many vesting schedules", ); return Err(Error::::AtMaxVestingSchedules.into()); } - Self::deposit_event(Event::::VestingMergeSuccess( - s.locked(), - s.per_block(), - s.starting_block(), + // (We use `locked_at` in case this is a schedule that started in the past.) + let new_schedule_locked = new_schedule.locked_at::(now); + // update the locked amount to reflect the schedule we just added, + locked_now = locked_now.saturating_add(new_schedule_locked); + // and deposit an event to show the new, new, merged schedule. + Self::deposit_event(Event::::MergedScheduleAdded( + new_schedule.locked(), + new_schedule.per_block(), + new_schedule.starting_block(), )); - Vesting::::insert(&who, vesting); - } else if let Some(vesting) = maybe_vesting { - Vesting::::insert(&who, vesting); - } else { - Vesting::::remove(&who); - } + } // In the None case there was no new schedule to account for. + + debug_assert!(schedules.len() <= T::MaxVestingSchedules::get() as usize); + debug_assert!( + locked_now > Zero::zero() && schedules.len() > 0 || + locked_now == Zero::zero() && schedules.len() == 0 + ); + if let Err(e) = Self::write_vesting(&who, schedules) { + // The write should not fail because that would mean their where too + // many schedules to start out with. + log::warn!(target: LOG_TARGET, "an account has too many vesting schedules",); + return e.into(); + }; + Self::write_lock(&who, locked_now); Ok(()) } @@ -508,27 +523,33 @@ impl Pallet { Ok(()) } - /// (Re)set or remove the pallet's currency lock on `who`'s account in accordance with their - /// current unvested amount and prune any vesting schedules that have completed. + /// Iterate through the schedules to track the current locked amount and + /// filter out completed and specified schedules. /// - /// NOTE: This will update the users lock, but will not read/write the `Vesting` storage item. - fn update_lock_and_schedules( - who: T::AccountId, - vesting: BoundedVec, T::BlockNumber>, T::MaxVestingSchedules>, - filter: Filter, - ) -> Option, T::BlockNumber>, T::MaxVestingSchedules>> { + /// Returns a tuple that consists of: + /// - bounded vec of vesting schedules, where completed schedules and those + /// specified by filter are remove. + /// - the amount locked at the current block number based on the given schedules. + /// + /// NOTE: the amount locked does not include any schedules that are filtered out. + fn report_schedule_updates( + schedules: BoundedVec, T::BlockNumber>, T::MaxVestingSchedules>, + action: VestingAction, + ) -> (BoundedVec, T::BlockNumber>, T::MaxVestingSchedules>, BalanceOf) + { let now = >::block_number(); let mut total_locked_now: BalanceOf = Zero::zero(); - let still_vesting = vesting + let filtered_schedules = schedules .into_iter() .enumerate() .filter_map(|(index, schedule)| { let locked_now = schedule.locked_at::(now); - total_locked_now = total_locked_now.saturating_add(locked_now); - if locked_now.is_zero() || filter.should_remove(&index) { + if locked_now.is_zero() || action.should_remove(&index) { None } else { + // We track the locked amount only if the schedule is included. + total_locked_now = total_locked_now.saturating_add(locked_now); Some(schedule) } }) @@ -536,28 +557,56 @@ impl Pallet { .try_into() .expect("`BoundedVec` is created from another `BoundedVec` with same bound; q.e.d."); + (filtered_schedules, total_locked_now) + } + + /// Write an accounts updated vesting lock to storage. + fn write_lock(who: &T::AccountId, total_locked_now: BalanceOf) { if total_locked_now.is_zero() { - T::Currency::remove_lock(VESTING_ID, &who); - Vesting::::remove(&who); - Self::deposit_event(Event::::VestingCompleted(who)); - None + T::Currency::remove_lock(VESTING_ID, who); + Self::deposit_event(Event::::VestingCompleted(who.clone())); } else { let reasons = WithdrawReasons::TRANSFER | WithdrawReasons::RESERVE; - T::Currency::set_lock(VESTING_ID, &who, total_locked_now, reasons); - Self::deposit_event(Event::::VestingUpdated(who, total_locked_now)); - Some(still_vesting) + T::Currency::set_lock(VESTING_ID, who, total_locked_now, reasons); + Self::deposit_event(Event::::VestingUpdated(who.clone(), total_locked_now)); + }; + } + + /// Write an accounts updated vesting schedules to storage. + fn write_vesting( + who: &T::AccountId, + schedules: BoundedVec, T::BlockNumber>, T::MaxVestingSchedules>, + ) -> Result<(), DispatchError> { + if schedules.len() > T::MaxVestingSchedules::get() as usize { + return Err(Error::::AtMaxVestingSchedules.into()); + } else if schedules.len() == 0 { + Vesting::::remove(&who); + } else { + Vesting::::insert(who, schedules) } + + Ok(()) } /// Unlock any vested funds of `who`. fn do_vest(who: T::AccountId) -> DispatchResult { - let vesting = Self::vesting(&who).ok_or(Error::::NotVesting)?; - let maybe_vesting = Self::update_lock_and_schedules(who.clone(), vesting, Filter::Zero); - if let Some(vesting) = maybe_vesting { - Vesting::::insert(&who, vesting); - } else { - Vesting::::remove(&who); - } + let schedules = Self::vesting(&who).ok_or(Error::::NotVesting)?; + + let (schedules, locked_now) = + Self::report_schedule_updates(schedules, VestingAction::Passive); + debug_assert!(schedules.len() <= T::MaxVestingSchedules::get() as usize); + debug_assert!( + locked_now > Zero::zero() && schedules.len() > 0 || + locked_now == Zero::zero() && schedules.len() == 0 + ); + + if let Err(e) = Self::write_vesting(&who, schedules) { + // The write should not fail because that would mean their where too + // many schedules to start out with. + log::warn!(target: LOG_TARGET, "an account has too many vesting schedules",); + return e.into(); + }; + Self::write_lock(&who, locked_now); Ok(()) } @@ -605,26 +654,50 @@ where } let vesting_schedule = VestingInfo::new::(locked, per_block, starting_block); - let mut vesting = Self::vesting(who).unwrap_or_default(); - ensure!(vesting.try_push(vesting_schedule).is_ok(), Error::::AtMaxVestingSchedules); + let mut schedules = Self::vesting(who).unwrap_or_default(); + + // NOTE: we must push the new schedule so that `report_schedule_updates` + // will give the correct new locked amount. + ensure!(schedules.try_push(vesting_schedule).is_ok(), Error::::AtMaxVestingSchedules); + + let (schedules, locked_now) = + Self::report_schedule_updates(schedules, VestingAction::Passive); + debug_assert!(schedules.len() <= T::MaxVestingSchedules::get() as usize); + debug_assert!( + locked_now > Zero::zero() && schedules.len() > 0 || + locked_now == Zero::zero() && schedules.len() == 0 + ); + + if let Err(e) = Self::write_vesting(&who, schedules) { + // The write should not fail because that would mean their where too + // many schedules to start out with. + log::warn!(target: LOG_TARGET, "an account has too many vesting schedules",); + return e.into(); + }; + Self::write_lock(who, locked_now); - if let Some(v) = Self::update_lock_and_schedules(who.clone(), vesting, Filter::Zero) { - Vesting::::insert(&who, v); - } else { - Vesting::::remove(&who); - } Ok(()) } /// Remove a vesting schedule for a given account. Will error if `schedule_index` is `None`. fn remove_vesting_schedule(who: &T::AccountId, schedule_index: u32) -> DispatchResult { - let filter = Filter::One(schedule_index as usize); - let vesting = Self::vesting(who).ok_or(Error::::NotVesting)?; - if let Some(v) = Self::update_lock_and_schedules(who.clone(), vesting, filter) { - Vesting::::insert(&who, v); - } else { - Vesting::::remove(&who); + let remove_action = VestingAction::Remove(schedule_index as usize); + let schedules = Self::vesting(who).ok_or(Error::::NotVesting)?; + + let (schedules, locked_now) = Self::report_schedule_updates(schedules, remove_action); + debug_assert!(schedules.len() <= T::MaxVestingSchedules::get() as usize); + debug_assert!( + locked_now > Zero::zero() && schedules.len() > 0 || + locked_now == Zero::zero() && schedules.len() == 0 + ); + + if let Err(e) = Self::write_vesting(&who, schedules) { + // The write should not fail because that would mean their where too + // many schedules to start out with. + log::warn!(target: LOG_TARGET, "an account has too many vesting schedules",); + return e.into(); }; + Self::write_lock(who, locked_now); Ok(()) } } From 14cfd861b1d37ef18b8442214c3165c11a1818f6 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Wed, 30 Jun 2021 19:07:15 -0700 Subject: [PATCH 043/144] Add can_add_vesting schedule --- .../src/traits/tokens/currency/lockable.rs | 9 ++++++ frame/vesting/src/lib.rs | 32 ++++++++++++++++--- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/frame/support/src/traits/tokens/currency/lockable.rs b/frame/support/src/traits/tokens/currency/lockable.rs index d7d50f07edecd..bacfaea257e35 100644 --- a/frame/support/src/traits/tokens/currency/lockable.rs +++ b/frame/support/src/traits/tokens/currency/lockable.rs @@ -97,6 +97,15 @@ pub trait VestingSchedule { starting_block: Self::Moment, ) -> DispatchResult; + /// Checks if `add_vesting_schedule` would work against `who` and the given + /// schedule params. + fn can_add_vesting_schedule( + who: &AccountId, + locked: >::Balance, + per_block: >::Balance, + starting_block: Self::Moment, + ) -> DispatchResult; + /// Remove a vesting schedule for a given account. /// /// Parameter `schedule_index` is only applicable for implementations that diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index e87328a74e19b..d699d0b61c46e 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -492,17 +492,19 @@ impl Pallet { target: ::Source, schedule: VestingInfo, T::BlockNumber>, ) -> DispatchResult { + // Validate user inputs. ensure!(schedule.locked() > T::MinVestedTransfer::get(), Error::::AmountLow); schedule.validate::()?; + + // Potentially correct `per_block` if its greater than locked. let schedule = schedule.correct(); let target = T::Lookup::lookup(target)?; let source = T::Lookup::lookup(source)?; - ensure!( - Vesting::::decode_len(&target).unwrap_or_default() < - T::MaxVestingSchedules::get() as usize, - Error::::AtMaxVestingSchedules - ); + + // Check we can add to this account prior to any storage writes. The schedule + // params are ignored so we just use 0s. + Self::can_add_vesting_schedule(&target, Zero::zero(), Zero::zero(), Zero::zero())?; T::Currency::transfer( &source, @@ -679,6 +681,26 @@ where Ok(()) } + // Ensure we can call `add_vesting_schedule` without error. This should always + // be updated in lockstep with `add_vesting_schedule`. + // + // NOTE: expects schedule param validation has been done due to different + // scenarios having varying requirements. + fn can_add_vesting_schedule( + who: &T::AccountId, + _locked: BalanceOf, + _per_block: BalanceOf, + _starting_block: T::BlockNumber, + ) -> DispatchResult { + ensure!( + Vesting::::decode_len(who).unwrap_or_default() < + T::MaxVestingSchedules::get() as usize, + Error::::AtMaxVestingSchedules + ); + + Ok(()) + } + /// Remove a vesting schedule for a given account. Will error if `schedule_index` is `None`. fn remove_vesting_schedule(who: &T::AccountId, schedule_index: u32) -> DispatchResult { let remove_action = VestingAction::Remove(schedule_index as usize); From 482f1a5e6169111e59c61920cc964f0f43fcc7f5 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Sat, 3 Jul 2021 17:37:24 -0700 Subject: [PATCH 044/144] Adjust min vested transfer to be greater than all ED --- frame/vesting/src/lib.rs | 2 +- frame/vesting/src/mock.rs | 5 ++- frame/vesting/src/tests.rs | 89 ++++++++++++++++++++------------------ 3 files changed, 51 insertions(+), 45 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index d699d0b61c46e..40ef322a77a92 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -493,7 +493,7 @@ impl Pallet { schedule: VestingInfo, T::BlockNumber>, ) -> DispatchResult { // Validate user inputs. - ensure!(schedule.locked() > T::MinVestedTransfer::get(), Error::::AmountLow); + ensure!(schedule.locked() >= T::MinVestedTransfer::get(), Error::::AmountLow); schedule.validate::()?; // Potentially correct `per_block` if its greater than locked. diff --git a/frame/vesting/src/mock.rs b/frame/vesting/src/mock.rs index 62233b2254830..a99f7ed7b59c3 100644 --- a/frame/vesting/src/mock.rs +++ b/frame/vesting/src/mock.rs @@ -85,7 +85,9 @@ impl pallet_balances::Config for Test { type WeightInfo = (); } parameter_types! { - pub const MinVestedTransfer: u64 = 10; + // Ideally all tests should use a value less than `MinVestedTransfer` when configuring the + // existential deposit. + pub const MinVestedTransfer: u64 = 256 * 2; pub static ExistentialDeposit: u64 = 0; pub const MaxVestingSchedules: u32 = 3; } @@ -129,6 +131,7 @@ impl ExtBuilder { (3, 30 * self.existential_deposit), (4, 40 * self.existential_deposit), (12, 10 * self.existential_deposit), + (13, 9999 * self.existential_deposit), ], } .assimilate_storage(&mut t) diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index a5644f2a57938..1eeec488d8ca5 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -28,27 +28,27 @@ const ED: u64 = 256; #[test] fn check_vesting_status() { ExtBuilder::default() - .existential_deposit(256) + .existential_deposit(ED) .build() .execute_with(|| { let user1_free_balance = Balances::free_balance(&1); let user2_free_balance = Balances::free_balance(&2); let user12_free_balance = Balances::free_balance(&12); - assert_eq!(user1_free_balance, 256 * 10); // Account 1 has free balance - assert_eq!(user2_free_balance, 256 * 20); // Account 2 has free balance - assert_eq!(user12_free_balance, 256 * 10); // Account 12 has free balance + assert_eq!(user1_free_balance, ED * 10); // Account 1 has free balance + assert_eq!(user2_free_balance, ED * 20); // Account 2 has free balance + assert_eq!(user12_free_balance, ED * 10); // Account 12 has free balance let user1_vesting_schedule = VestingInfo::new::( - 256 * 5, + ED * 5, 128, // Vesting over 10 blocks 0, ); let user2_vesting_schedule = VestingInfo::new::( - 256 * 20, - 256, // Vesting over 20 blocks + ED * 20, + ED, // Vesting over 20 blocks 10, ); let user12_vesting_schedule = VestingInfo::new::( - 256 * 5, + ED * 5, 64, // Vesting over 20 blocks 10, ); @@ -56,12 +56,12 @@ fn check_vesting_status() { assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); // Account 2 has a vesting schedule assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); // Account 12 has a vesting schedule - // Account 1 has only 128 units vested from their illiquid 256 * 5 units at block 1 + // Account 1 has only 128 units vested from their illiquid ED * 5 units at block 1 assert_eq!(Vesting::vesting_balance(&1), Some(128 * 9)); // Account 2 has their full balance locked assert_eq!(Vesting::vesting_balance(&2), Some(user2_free_balance)); // Account 12 has only their illiquid funds locked - assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); + assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - ED * 5)); System::set_block_number(10); assert_eq!(System::block_number(), 10); @@ -71,7 +71,7 @@ fn check_vesting_status() { // Account 2 has started vesting by block 10 assert_eq!(Vesting::vesting_balance(&2), Some(user2_free_balance)); // Account 12 has started vesting by block 10 - assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); + assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - ED * 5)); System::set_block_number(30); assert_eq!(System::block_number(), 30); @@ -317,18 +317,18 @@ fn vested_transfer_works() { #[test] fn vested_transfer_correctly_fails() { ExtBuilder::default() - .existential_deposit(256) + .existential_deposit(ED) .build() .execute_with(|| { let user2_free_balance = Balances::free_balance(&2); let user4_free_balance = Balances::free_balance(&4); - assert_eq!(user2_free_balance, 256 * 20); - assert_eq!(user4_free_balance, 256 * 40); + assert_eq!(user2_free_balance, ED * 20); + assert_eq!(user4_free_balance, ED * 40); // Account 2 should already have a vesting schedule. let user2_vesting_schedule = VestingInfo::new::( - 256 * 20, - 256, // Vesting over 20 blocks + ED * 20, + ED, // Vesting over 20 blocks 10, ); assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); @@ -342,9 +342,9 @@ fn vested_transfer_correctly_fails() { ); // `per_block` is 0, which would result in a schedule with infinite duration. - let schedule_per_block_0 = VestingInfo::new::(256, 0, 10); + let schedule_per_block_0 = VestingInfo::new::(::MinVestedTransfer::get() + 1, 0, 10); assert_noop!( - Vesting::vested_transfer(Some(3).into(), 4, schedule_per_block_0), + Vesting::vested_transfer(Some(13).into(), 4, schedule_per_block_0), Error::::InfiniteSchedule, ); @@ -374,15 +374,16 @@ fn vested_transfer_allows_max_schedules() { let mut user_4_free_balance = Balances::free_balance(&4); let max_schedules = ::MaxVestingSchedules::get(); let sched = VestingInfo::new::( - ED, 1, // Vest over 256 blocks. + ::MinVestedTransfer::get(), + 1, // Vest over 2 * 256 blocks. 10, ); // Add max amount schedules to user 4. for _ in 0 .. max_schedules { - assert_ok!(Vesting::vested_transfer(Some(3).into(), 4, sched)); + assert_ok!(Vesting::vested_transfer(Some(13).into(), 4, sched)); } // The schedules count towards vesting balance - let transferred_amount = ED * max_schedules as u64; + let transferred_amount = ::MinVestedTransfer::get() * max_schedules as u64; assert_eq!(Vesting::vesting_balance(&4), Some(transferred_amount)); // and free balance. user_4_free_balance += transferred_amount; @@ -397,7 +398,7 @@ fn vested_transfer_allows_max_schedules() { assert_eq!(Balances::free_balance(&4), user_4_free_balance); // Account 4 has fully vested when all the schedules end - System::set_block_number(ED + 10); + System::set_block_number(::MinVestedTransfer::get() + 10); assert_eq!(Vesting::vesting_balance(&4), Some(0)); // and after unlocking its schedules are removed from storage. assert_ok!(Vesting::vest(Some(4).into())); @@ -408,18 +409,18 @@ fn vested_transfer_allows_max_schedules() { #[test] fn force_vested_transfer_works() { ExtBuilder::default() - .existential_deposit(256) + .existential_deposit(ED) .build() .execute_with(|| { let user3_free_balance = Balances::free_balance(&3); let user4_free_balance = Balances::free_balance(&4); - assert_eq!(user3_free_balance, 256 * 30); - assert_eq!(user4_free_balance, 256 * 40); + assert_eq!(user3_free_balance, ED * 30); + assert_eq!(user4_free_balance, ED * 40); // Account 4 should not have any vesting yet. assert_eq!(Vesting::vesting(&4), None); // Make the schedule for the new transfer. let new_vesting_schedule = VestingInfo::new::( - 256 * 5, + ED * 5, 64, // Vesting over 20 blocks 10, ); @@ -439,11 +440,11 @@ fn force_vested_transfer_works() { assert_eq!(Vesting::vesting(&4).unwrap().len(), 1); // Ensure the transfer happened correctly. let user3_free_balance_updated = Balances::free_balance(&3); - assert_eq!(user3_free_balance_updated, 256 * 25); + assert_eq!(user3_free_balance_updated, ED * 25); let user4_free_balance_updated = Balances::free_balance(&4); - assert_eq!(user4_free_balance_updated, 256 * 45); - // Account 4 has 5 * 256 locked. - assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5)); + assert_eq!(user4_free_balance_updated, ED * 45); + // Account 4 has 5 * ED locked. + assert_eq!(Vesting::vesting_balance(&4), Some(ED * 5)); System::set_block_number(20); assert_eq!(System::block_number(), 20); @@ -465,17 +466,17 @@ fn force_vested_transfer_works() { #[test] fn force_vested_transfer_correctly_fails() { ExtBuilder::default() - .existential_deposit(256) + .existential_deposit(ED) .build() .execute_with(|| { let user2_free_balance = Balances::free_balance(&2); let user4_free_balance = Balances::free_balance(&4); - assert_eq!(user2_free_balance, 256 * 20); - assert_eq!(user4_free_balance, 256 * 40); + assert_eq!(user2_free_balance, ED * 20); + assert_eq!(user4_free_balance, ED * 40); // Account 2 should already have a vesting schedule. let user2_vesting_schedule = VestingInfo::new::( - 256 * 20, - 256, // Vesting over 20 blocks + ED * 20, + ED, // Vesting over 20 blocks 10, ); assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); @@ -494,9 +495,10 @@ fn force_vested_transfer_correctly_fails() { ); // `per_block` is 0. - let schedule_per_block_0 = VestingInfo::new::(256, 0, 10); + let schedule_per_block_0 = + VestingInfo::new::(::MinVestedTransfer::get(), 0, 10); assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_per_block_0), + Vesting::force_vested_transfer(RawOrigin::Root.into(), 13, 4, schedule_per_block_0), Error::::InfiniteSchedule, ); @@ -526,15 +528,16 @@ fn force_vested_transfer_allows_max_schedules() { let mut user_4_free_balance = Balances::free_balance(&4); let max_schedules = ::MaxVestingSchedules::get(); let sched = VestingInfo::new::( - ED, 1, // Vest over 256 blocks. + ::MinVestedTransfer::get(), + 1, // Vest over 256 blocks. 10, ); // Add max amount schedules to user 4. for _ in 0 .. max_schedules { - assert_ok!(Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, sched)); + assert_ok!(Vesting::force_vested_transfer(RawOrigin::Root.into(), 13, 4, sched)); } // The schedules count towards vesting balance. - let transferred_amount = ED * max_schedules as u64; + let transferred_amount = ::MinVestedTransfer::get() * max_schedules as u64; assert_eq!(Vesting::vesting_balance(&4), Some(transferred_amount)); // and free balance. user_4_free_balance += transferred_amount; @@ -549,7 +552,7 @@ fn force_vested_transfer_allows_max_schedules() { assert_eq!(Balances::free_balance(&4), user_4_free_balance); // Account 4 has fully vested when all the schedules end - System::set_block_number(ED + 10); + System::set_block_number(::MinVestedTransfer::get() + 10); assert_eq!(Vesting::vesting_balance(&4), Some(0)); // and after unlocking its schedules are removed from storage. assert_ok!(Vesting::vest(Some(4).into())); @@ -601,8 +604,8 @@ fn merge_ongoing_schedules() { assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); let sched1 = VestingInfo::new::( - 256 * 10, - 256, // Vest over 10 blocks + ED * 10, + ED, // Vest over 10 blocks sched0.starting_block() + 5, // Start at block 15 ); Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); From f1a760e08c05a6ae9be43ae345b91f778dbeb664 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Sat, 3 Jul 2021 18:10:22 -0700 Subject: [PATCH 045/144] Initial integrity test impl --- frame/vesting/src/lib.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 40ef322a77a92..2841683714add 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -131,6 +131,14 @@ pub mod pallet { type MaxVestingSchedules: Get; } + #[pallet::hooks] + impl Hooks> for Pallet { + fn integrity_test() { + assert!(T::MinVestedTransfer::get() >= ::Currency::minimum_balance()); + assert!(T::MaxVestingSchedules::get() > 0); + } + } + /// Information regarding the vesting of a given account. #[pallet::storage] #[pallet::getter(fn vesting)] @@ -138,7 +146,7 @@ pub mod pallet { _, Blake2_128Concat, T::AccountId, - BoundedVec, T::BlockNumber>, T::MaxVestingSchedules> + BoundedVec, T::BlockNumber>, T::MaxVestingSchedules>, >; #[pallet::pallet] From 79292788dbbfc72b75805c6282d0abd8bd6fe090 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Sat, 3 Jul 2021 18:57:07 -0700 Subject: [PATCH 046/144] merge_finished_and_yet_to_be_started_schedules --- frame/vesting/src/tests.rs | 59 ++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index 1eeec488d8ca5..6db58c251199e 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -704,7 +704,7 @@ fn merging_shifts_other_schedules_index() { } #[test] -fn merge_ongoing_and_yet_to_be_started_schedule() { +fn merge_ongoing_and_yet_to_be_started_schedules() { // Merge an ongoing schedule that has had `vest` called and a schedule that has not already // started. ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { @@ -768,7 +768,7 @@ fn merge_ongoing_and_yet_to_be_started_schedule() { } #[test] -fn merge_finishing_and_ongoing_schedule() { +fn merge_finished_and_ongoing_schedules() { // If a schedule finishes by the current block we treat the ongoing schedule, // without any alterations, as the merged one. ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { @@ -861,16 +861,65 @@ fn merge_finishing_schedules_does_not_create_a_new_one() { assert_eq!(Balances::usable_balance(&2), 0); // Merge schedule 0 and 1. - Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + assert_ok!(Vesting::merge_schedules(Some(2).into(), 0, 1)); // The user no longer has any more vesting schedules because they both ended at the - // block they where merged. + // block they where merged, assert!(!>::contains_key(&2)); - // And their usable balance has increased by the total amount locked in the merged + // and their usable balance has increased by the total amount locked in the merged // schedules. assert_eq!(Balances::usable_balance(&2), sched0.locked() + sched1.locked()); }); } +#[test] +fn merge_finished_and_yet_to_be_started_schedules() { + // Merging a schedule that has finished and on that has not yet started results in just the not + // yet started schedule being moved to the back. + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + // Account 2 should already have a vesting schedule. + let sched0 = VestingInfo::new::( + ED * 20, + ED, // 20 block duration. + 10, // Ends at block 30 + ); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); + + let sched1 = VestingInfo::new::( + ED * 30, + ED * 2, // 30 block duration. + 35, + ); + assert_ok!(Vesting::vested_transfer(Some(13).into(), 2, sched1)); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); + + let sched2 = VestingInfo::new::( + ED * 40, + ED, // 40 block duration. + 30, + ); + // Add a 3rd schedule to demonstrate how sched1 moves + assert_ok!(Vesting::vested_transfer(Some(13).into(), 2, sched2)); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1, sched2]); + + System::set_block_number(30); + + // At block 30, sched0 has finished unlocking while sched1 and sched2 are still fully locked, + assert_eq!(Vesting::vesting_balance(&2), Some(sched1.locked() + sched2.locked())); + // but since we have not vested usable balance is still 0. + assert_eq!(Balances::usable_balance(&2), 0); + + // Merge schedule 0 and 1. + assert_ok!(Vesting::merge_schedules(Some(2).into(), 0, 1)); + + // sched0 is removed since it finished, and sched1 is removed and then pushed on the back + // because it is treated as the merged schedule + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2, sched1]); + + // The usable balance is updated because merging fully unlocked sched0. + assert_eq!(Balances::usable_balance(&2), sched0.locked()); + }); +} + #[test] fn merge_schedules_throws_proper_errors() { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { From 6b564d880f41184bbedb6e3c7e33560579b73b7f Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Sat, 3 Jul 2021 19:41:56 -0700 Subject: [PATCH 047/144] Make sure to assert storage items are cleaned up --- frame/vesting/src/lib.rs | 4 +-- frame/vesting/src/tests.rs | 56 +++++++++++++++++++++++--------------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 2841683714add..dd14841a9da1a 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -481,7 +481,7 @@ impl Pallet { match locked.checked_div(&duration) { // The logic of `ending_block` guarantees that each schedule ends at least a block // after it starts and since we take the max starting and ending_block we should never - // get here + // get here. None => locked, Some(per_block) => per_block, } @@ -521,7 +521,7 @@ impl Pallet { ExistenceRequirement::AllowDeath, )?; - // We can't let this fail because the currency transfer has already happened + // We can't let this fail because the currency transfer has already happened. Self::add_vesting_schedule( &target, schedule.locked(), diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index 6db58c251199e..d37f220a5b48f 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -16,6 +16,7 @@ // limitations under the License. use frame_support::{assert_noop, assert_ok, assert_storage_noop}; +use frame_support::dispatch::EncodeLike; use frame_system::RawOrigin; use sp_runtime::traits::{BadOrigin, Identity}; @@ -25,6 +26,18 @@ use crate::mock::{Balances, ExtBuilder, System, Test, Vesting}; /// A default existential deposit. const ED: u64 = 256; +/// Calls vest, and asserts that there is no entry for `account` +/// in the `Vesting` storage item. +fn vest_and_assert_no_vesting(account: u64) +where + u64: EncodeLike<::AccountId>, + T: pallet::Config, +{ + // Its ok for this to fail because the user may already have no schedules. + let _result = Vesting::vest(Some(account).into()); + assert!(!>::contains_key(account)); +} + #[test] fn check_vesting_status() { ExtBuilder::default() @@ -81,12 +94,9 @@ fn check_vesting_status() { assert_eq!(Vesting::vesting_balance(&12), Some(0)); // Account 2 has fully vested by block 30 // Once we unlock the funds, they are removed from storage. - assert_ok!(Vesting::vest(Some(1).into())); - assert!(!>::contains_key(&1)); - assert_ok!(Vesting::vest(Some(2).into())); - assert!(!>::contains_key(&2)); - assert_ok!(Vesting::vest(Some(12).into())); - assert!(!>::contains_key(&12)); + vest_and_assert_no_vesting::(1); + vest_and_assert_no_vesting::(2); + vest_and_assert_no_vesting::(12); }); } @@ -161,11 +171,10 @@ fn check_vesting_status_for_multi_schedule_account() { assert_eq!(System::block_number(), 35); assert_eq!(Vesting::vesting_balance(&2), Some(0)); // Since we have not called any extrinsics that would unlock funds the schedules - // are still in storage. + // are still in storage, assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1, sched2]); - // But once we unlock the funds, they are removed from storage. - assert_ok!(Vesting::vest(Some(2).into())); - assert!(!>::contains_key(&2)); + // but once we unlock the funds, they are removed from storage. + vest_and_assert_no_vesting::(2); }); } @@ -309,8 +318,7 @@ fn vested_transfer_works() { // Account 4 has fully vested assert_eq!(Vesting::vesting_balance(&4), Some(0)); // and after unlocking its schedules are removed from storage. - assert_ok!(Vesting::vest(Some(4).into())); - assert!(!>::contains_key(&4)); + vest_and_assert_no_vesting::(4); }); } @@ -365,6 +373,8 @@ fn vested_transfer_correctly_fails() { // Free balance has not changed. assert_eq!(user2_free_balance, Balances::free_balance(&2)); assert_eq!(user4_free_balance, Balances::free_balance(&4)); + // Account 4 has no schedules. + vest_and_assert_no_vesting::(4); }); } @@ -401,8 +411,7 @@ fn vested_transfer_allows_max_schedules() { System::set_block_number(::MinVestedTransfer::get() + 10); assert_eq!(Vesting::vesting_balance(&4), Some(0)); // and after unlocking its schedules are removed from storage. - assert_ok!(Vesting::vest(Some(4).into())); - assert!(!>::contains_key(&4)); + vest_and_assert_no_vesting::(4); }); } @@ -455,11 +464,10 @@ fn force_vested_transfer_works() { System::set_block_number(30); assert_eq!(System::block_number(), 30); - // Account 4 has fully vested. + // Account 4 has fully vested, assert_eq!(Vesting::vesting_balance(&4), Some(0)); // and after unlocking its schedules are removed from storage. - assert_ok!(Vesting::vest(Some(4).into())); - assert!(!>::contains_key(&4)); + vest_and_assert_no_vesting::(4); }); } @@ -519,6 +527,8 @@ fn force_vested_transfer_correctly_fails() { // Verify no currency transfer happened. assert_eq!(user2_free_balance, Balances::free_balance(&2)); assert_eq!(user4_free_balance, Balances::free_balance(&4)); + // Account 4 has no schedules. + vest_and_assert_no_vesting::(4); }); } @@ -555,8 +565,7 @@ fn force_vested_transfer_allows_max_schedules() { System::set_block_number(::MinVestedTransfer::get() + 10); assert_eq!(Vesting::vesting_balance(&4), Some(0)); // and after unlocking its schedules are removed from storage. - assert_ok!(Vesting::vest(Some(4).into())); - assert!(!>::contains_key(&4)); + vest_and_assert_no_vesting::(4); }); } @@ -639,6 +648,10 @@ fn merge_ongoing_schedules() { let sched2 = VestingInfo::new::(sched2_locked, sched2_per_block, cur_block); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2]); + + // And just to double check, we assert the new merged schedule we be cleaned up as expected. + System::set_block_number(30); + vest_and_assert_no_vesting::(2); }); } @@ -758,7 +771,8 @@ fn merge_ongoing_and_yet_to_be_started_schedules() { let sched2_locked = sched0.locked_at::(cur_block) + sched1.locked_at::(cur_block); // and will end at the max possible block. - let sched2_end = sched0.ending_block::().unwrap().max(sched1.ending_block::().unwrap()); + let sched2_end = sched0.ending_block::().unwrap() + .max(sched1.ending_block::().unwrap()); let sched2_duration = sched2_end - sched2_start; let sched2_per_block = sched2_locked / sched2_duration; @@ -873,8 +887,6 @@ fn merge_finishing_schedules_does_not_create_a_new_one() { #[test] fn merge_finished_and_yet_to_be_started_schedules() { - // Merging a schedule that has finished and on that has not yet started results in just the not - // yet started schedule being moved to the back. ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { // Account 2 should already have a vesting schedule. let sched0 = VestingInfo::new::( From 279386a5064fba019b950db1b93c18b3fd64e206 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Sat, 3 Jul 2021 23:14:55 -0700 Subject: [PATCH 048/144] Migration initial impl (not tested) --- frame/vesting/src/lib.rs | 39 ++++++++++++++++++++++++ frame/vesting/src/migration.rs | 55 ++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 frame/vesting/src/migration.rs diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index dd14841a9da1a..2b0b2a406347f 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -45,6 +45,7 @@ #![cfg_attr(not(feature = "std"), no_std)] mod benchmarking; +mod migration; #[cfg(test)] mod mock; #[cfg(test)] @@ -83,6 +84,22 @@ type MaxLocksOf = const VESTING_ID: LockIdentifier = *b"vesting "; const LOG_TARGET: &'static str = "runtime::vesting"; +// A value placed in storage that represents the current version of the Staking storage. +// This value is used by `on_runtime_upgrade` to determine whether we run storage migration logic. +#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug)] +enum Releases { + V0, + V1, +} + +impl Default for Releases { + fn default() -> Self { + // TODO: verify assumption that this will be value in storages + // when we try to do runtime upgrade + Releases::V0 + } +} + /// Actions to take on a users `Vesting` storage entry. enum VestingAction { /// Do not actively remove any schedules. @@ -133,6 +150,15 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { + fn on_runtime_upgrade() -> Weight { + if StorageVersion::::get() == Releases::V0 { + StorageVersion::::put(Releases::V1); + migration::v1::migrate::() + } else { + T::DbWeight::get().reads(1) + } + } + fn integrity_test() { assert!(T::MinVestedTransfer::get() >= ::Currency::minimum_balance()); assert!(T::MaxVestingSchedules::get() > 0); @@ -149,6 +175,12 @@ pub mod pallet { BoundedVec, T::BlockNumber>, T::MaxVestingSchedules>, >; + /// Storage version of the pallet. + /// + /// New networks start with latest version, as determined by the genesis build. + #[pallet::storage] + pub(crate) type StorageVersion = StorageValue<_, Releases, ValueQuery>; + #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); @@ -172,6 +204,8 @@ pub mod pallet { fn build(&self) { use sp_runtime::traits::Saturating; + StorageVersion::::put(Releases::V1); + // Generate initial vesting configuration // * who - Account which we are generating vesting configuration for // * begin - Block when the account will start to vest @@ -730,4 +764,9 @@ where Self::write_lock(who, locked_now); Ok(()) } + + // TODO: migration tests + // - migration version changes as expected + // - same checks as pre and post migrate + // TODO: test that genesis build has the correct migrations version } diff --git a/frame/vesting/src/migration.rs b/frame/vesting/src/migration.rs new file mode 100644 index 0000000000000..e18bbe0d90cca --- /dev/null +++ b/frame/vesting/src/migration.rs @@ -0,0 +1,55 @@ +//! Storage migrations for the vesting pallet. + +use super::*; + +// Migration from single schedule to multiple schedules. +pub(crate) mod v1 { + use super::*; + + #[cfg(feature = "try-runtime")] + pub(crate) fn pre_migrate() { + assert!(!StorageVersion::get()); + for (key, schedule) in pallet::Vesting::::iter_values() { + // Log the key so we can track down a failure below. + log::debug!(target: LOG_Target, "[pre_migrate] Vesting key {}", key); + // Check for infinite schedules. + assert!(schedule.per_block() > 0); + } + log::info!(target: LOG_TARGET, "pre migration checks succesful") + } + /// Migrate from single schedule to multi schedule storage + pub(crate) fn migrate() -> Weight { + let mut reads_writes = 0; + + Vesting::::translate::, T::BlockNumber>, _>( + |_key, vesting_info| { + reads_writes += 1; + let v: Option< + BoundedVec, T::BlockNumber>, T::MaxVestingSchedules>, + > = vec![vesting_info].try_into().ok(); + + v + }, + ); + + T::DbWeight::get().reads_writes(reads_writes, reads_writes) + } + + #[cfg(feature = "try-runtime")] + pub(crate) fn post_migrate() { + assert_eq!(StorageVersion::get(), Release::V1); + + for (key, schedules) in pallet::Vesting::::iter_values() { + log::debug!(target: LOG_TARGET, "[post_migrate] Vesting key {}", key); + // Assert the new bound vec respects size. + assert!(schedules.len() > 0); + assert!(schedules.len() <= T::MaxVestingSchedules::get() as usize); + + for s in schedules { + // Check for infinite schedules + assert!(s.per_block() > 0); + } + } + log::info!(target: LOG_TARGET, "post migration checks succesful") + } +} From 08da674ad0da90a086046980c7ef2226e0ba5ae1 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Sat, 3 Jul 2021 23:44:07 -0700 Subject: [PATCH 049/144] Correct try-runtime hooks --- frame/vesting/src/lib.rs | 14 ++++++-- .../src/{migration.rs => migrations.rs} | 35 ++++++++++--------- 2 files changed, 31 insertions(+), 18 deletions(-) rename frame/vesting/src/{migration.rs => migrations.rs} (52%) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 2b0b2a406347f..05f4df7dd0834 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -45,7 +45,7 @@ #![cfg_attr(not(feature = "std"), no_std)] mod benchmarking; -mod migration; +mod migrations; #[cfg(test)] mod mock; #[cfg(test)] @@ -150,15 +150,25 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result<(), &'static str> { + migrations::v1::pre_migrate::() + } + fn on_runtime_upgrade() -> Weight { if StorageVersion::::get() == Releases::V0 { StorageVersion::::put(Releases::V1); - migration::v1::migrate::() + migrations::v1::migrate::() } else { T::DbWeight::get().reads(1) } } + #[cfg(feature = "try-runtime")] + fn post_upgrade() -> Result<(), &'static str> { + migrations::v1::post_migrate::() + } + fn integrity_test() { assert!(T::MinVestedTransfer::get() >= ::Currency::minimum_balance()); assert!(T::MaxVestingSchedules::get() > 0); diff --git a/frame/vesting/src/migration.rs b/frame/vesting/src/migrations.rs similarity index 52% rename from frame/vesting/src/migration.rs rename to frame/vesting/src/migrations.rs index e18bbe0d90cca..c3419e61b7c82 100644 --- a/frame/vesting/src/migration.rs +++ b/frame/vesting/src/migrations.rs @@ -7,15 +7,17 @@ pub(crate) mod v1 { use super::*; #[cfg(feature = "try-runtime")] - pub(crate) fn pre_migrate() { - assert!(!StorageVersion::get()); - for (key, schedule) in pallet::Vesting::::iter_values() { - // Log the key so we can track down a failure below. - log::debug!(target: LOG_Target, "[pre_migrate] Vesting key {}", key); - // Check for infinite schedules. - assert!(schedule.per_block() > 0); - } - log::info!(target: LOG_TARGET, "pre migration checks succesful") + pub(crate) fn pre_migrate() -> Result<(), &'static str> { + assert!(StorageVersion::::get() == Releases::V0, "Storage version too high."); + + // TODO: figure out how to iterate over old type of values + // Vesting::::translate::, T::BlockNumber>, _>( + // |_key, vesting_info| { + // assert!(vesting_info.per_block() > Zero::zero(), "A schedule with per_block of 0 exists"); + // Some(vesting_info) + // }); + + Ok(()) } /// Migrate from single schedule to multi schedule storage pub(crate) fn migrate() -> Weight { @@ -36,20 +38,21 @@ pub(crate) mod v1 { } #[cfg(feature = "try-runtime")] - pub(crate) fn post_migrate() { - assert_eq!(StorageVersion::get(), Release::V1); + pub(crate) fn post_migrate() -> Result<(), &'static str> { + assert_eq!(StorageVersion::::get(), Releases::V1); - for (key, schedules) in pallet::Vesting::::iter_values() { + for (key, schedules) in Vesting::::iter() { log::debug!(target: LOG_TARGET, "[post_migrate] Vesting key {}", key); // Assert the new bound vec respects size. - assert!(schedules.len() > 0); - assert!(schedules.len() <= T::MaxVestingSchedules::get() as usize); + assert!(schedules.len() > 0, "A bounded vec with no items was created."); + assert!(schedules.len() <= T::MaxVestingSchedules::get() as usize, "A bounded vec with too many items was created."); for s in schedules { // Check for infinite schedules - assert!(s.per_block() > 0); + assert!(s.per_block() > Zero::zero(), "A schedule with per_block of 0 exists"); } } - log::info!(target: LOG_TARGET, "post migration checks succesful") + + Ok(()) } } From 66c8fd243516c23d4275d0c7a5a1068b50f46d8f Mon Sep 17 00:00:00 2001 From: Zeke Mostov <32168567+emostov@users.noreply.github.com> Date: Sat, 3 Jul 2021 23:47:08 -0700 Subject: [PATCH 050/144] Apply suggestions from code review Co-authored-by: Shawn Tabrizi --- frame/vesting/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 05f4df7dd0834..3b800b3d9f301 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -464,7 +464,7 @@ pub mod pallet { locked_now == Zero::zero() && schedules.len() == 0 ); if let Err(e) = Self::write_vesting(&who, schedules) { - // The write should not fail because that would mean their where too + // The write should not fail because that would mean their were too // many schedules to start out with. log::warn!(target: LOG_TARGET, "an account has too many vesting schedules",); return e.into(); @@ -655,7 +655,7 @@ impl Pallet { ); if let Err(e) = Self::write_vesting(&who, schedules) { - // The write should not fail because that would mean their where too + // The write should not fail because that would mean their were too // many schedules to start out with. log::warn!(target: LOG_TARGET, "an account has too many vesting schedules",); return e.into(); From aa88b651f64944806bee567da3125cfdca324b72 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Sun, 4 Jul 2021 12:41:28 -0700 Subject: [PATCH 051/144] header --- frame/vesting/src/migrations.rs | 17 +++++++++++++++++ frame/vesting/src/vesting_info.rs | 20 +++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/frame/vesting/src/migrations.rs b/frame/vesting/src/migrations.rs index c3419e61b7c82..5e59d112e0a7f 100644 --- a/frame/vesting/src/migrations.rs +++ b/frame/vesting/src/migrations.rs @@ -1,3 +1,20 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Storage migrations for the vesting pallet. use super::*; diff --git a/frame/vesting/src/vesting_info.rs b/frame/vesting/src/vesting_info.rs index f7fe808d92f60..73ce320ae46ce 100644 --- a/frame/vesting/src/vesting_info.rs +++ b/frame/vesting/src/vesting_info.rs @@ -1,4 +1,22 @@ -//! Module to enforce private fields on `VestingInfo` +// This file is part of Substrate. + +// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Module to enforce private fields on `VestingInfo`. + use super::*; /// Struct to encode the vesting schedule of an individual account. From 62c364940647fabcd42b9c9a5385f06b50a9b114 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Sun, 4 Jul 2021 15:16:57 -0700 Subject: [PATCH 052/144] WIP: improve benchmarks --- frame/vesting/src/benchmarking.rs | 100 +++++++++++++++++------------- frame/vesting/src/lib.rs | 4 +- frame/vesting/src/migrations.rs | 22 ++++--- 3 files changed, 70 insertions(+), 56 deletions(-) diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index db786b87b1966..b936492723b17 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -47,8 +47,11 @@ fn add_vesting_schedules( target: ::Source, n: u32, ) -> Result, &'static str> { - let locked = ED * 20u32; - let per_block = ED; // Vest over 20 blocks + let min_transfer = T::MinVestedTransfer::get(); + let per_block_duration_20 = min_transfer.checked_div(&20u32.into()).unwrap(); + + let locked = min_transfer; + let per_block = per_block_duration_20; let starting_block = 1u32; let source: T::AccountId = account("source", 0, SEED); @@ -57,13 +60,16 @@ fn add_vesting_schedules( System::::set_block_number(T::BlockNumber::zero()); - let mut total_locked = 0; + let mut total_locked: BalanceOf = Zero::zero(); for _ in 0 .. n { total_locked += locked; - let schedule = - VestingInfo::try_new::(locked.into(), per_block.into(), starting_block.into())?; - assert_ok!(Vesting::::do_vested_transfer(source_lookup.clone(), target.clone(), schedule)); + let schedule = VestingInfo::new::(locked, per_block, starting_block.into()); + assert_ok!(Vesting::::do_vested_transfer( + source_lookup.clone(), + target.clone(), + schedule + )); } Ok(total_locked.into()) @@ -71,7 +77,7 @@ fn add_vesting_schedules( benchmarks! { vest_locked { - let l in 0 .. MaxLocksOf::::get(); + let l in 0 .. MaxLocksOf::::get() - 1; let caller: T::AccountId = whitelisted_caller(); let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); @@ -97,7 +103,7 @@ benchmarks! { } vest_unlocked { - let l in 0 .. MaxLocksOf::::get(); + let l in 0 .. MaxLocksOf::::get() - 1; let caller: T::AccountId = whitelisted_caller(); let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); @@ -123,7 +129,7 @@ benchmarks! { } vest_other_locked { - let l in 0 .. MaxLocksOf::::get(); + let l in 0 .. MaxLocksOf::::get() - 1; let other: T::AccountId = account("other", 0, SEED); let other_lookup: ::Source = T::Lookup::unlookup(other.clone()); @@ -151,7 +157,7 @@ benchmarks! { } vest_other_unlocked { - let l in 0 .. MaxLocksOf::::get(); + let l in 0 .. MaxLocksOf::::get() - 1; let other: T::AccountId = account("other", 0, SEED); let other_lookup: ::Source = T::Lookup::unlookup(other.clone()); @@ -179,7 +185,7 @@ benchmarks! { } last_vested_transfer { - let l in 0 .. MaxLocksOf::::get(); + let l in 0 .. MaxLocksOf::::get() - 1; let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); @@ -192,15 +198,15 @@ benchmarks! { let one_less_than_max_vesting = T::MaxVestingSchedules::get() - 1; let mut expected_balance = add_vesting_schedules::(target_lookup.clone(), one_less_than_max_vesting)?; - let duration = 10u32; - let transfer_amount = (ED * duration).into(); + let transfer_amount = T::MinVestedTransfer::get(); + let per_block_duration_20 = transfer_amount.checked_div(&20u32.into()).unwrap(); expected_balance += transfer_amount; - let vesting_schedule = VestingInfo::try_new::( + let vesting_schedule = VestingInfo::new::( transfer_amount, - duration.into(), + per_block_duration_20, 1u32.into(), - )?; + ); }: vested_transfer(RawOrigin::Signed(caller), target_lookup, vesting_schedule) verify { assert_eq!( @@ -216,7 +222,7 @@ benchmarks! { } first_vested_transfer { - let l in 0 .. MaxLocksOf::::get(); + let l in 0 .. MaxLocksOf::::get() - 1; let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); @@ -226,14 +232,14 @@ benchmarks! { // Give target existing locks add_locks::(&target, l as u8); - let duration = 10u32; - let transfer_amount = (ED * duration).into(); + let transfer_amount = T::MinVestedTransfer::get(); + let per_block_duration_20 = transfer_amount.checked_div(&20u32.into()).unwrap(); - let vesting_schedule = VestingInfo::try_new::( + let vesting_schedule = VestingInfo::new::( transfer_amount, - duration.into(), + per_block_duration_20, 1u32.into(), - )?; + ); }: vested_transfer(RawOrigin::Signed(caller), target_lookup, vesting_schedule) verify { @@ -250,7 +256,7 @@ benchmarks! { } first_force_vested_transfer { - let l in 0 .. MaxLocksOf::::get(); + let l in 0 .. MaxLocksOf::::get() - 1; let source: T::AccountId = account("source", 0, SEED); let source_lookup: ::Source = T::Lookup::unlookup(source.clone()); @@ -261,14 +267,14 @@ benchmarks! { // Give target existing locks add_locks::(&target, l as u8); - let duration = 10u32; - let transfer_amount = (ED * duration).into(); + let transfer_amount = T::MinVestedTransfer::get(); + let per_block_duration_20 = transfer_amount.checked_div(&20u32.into()).unwrap(); - let vesting_schedule = VestingInfo::try_new::( + let vesting_schedule = VestingInfo::new::( transfer_amount, - duration.into(), + per_block_duration_20, 1u32.into(), - )?; + ); }: force_vested_transfer(RawOrigin::Root, source_lookup, target_lookup, vesting_schedule) verify { assert_eq!( @@ -284,7 +290,7 @@ benchmarks! { } last_force_vested_transfer { - let l in 0 .. MaxLocksOf::::get(); + let l in 0 .. MaxLocksOf::::get() - 1; let source: T::AccountId = account("source", 0, SEED); let source_lookup: ::Source = T::Lookup::unlookup(source.clone()); @@ -298,15 +304,15 @@ benchmarks! { let one_less_than_max_vesting = T::MaxVestingSchedules::get() - 1; let mut expected_balance = add_vesting_schedules::(target_lookup.clone(), one_less_than_max_vesting)?; - let duration = 10u32; - let transfer_amount = (ED * duration).into(); + let transfer_amount = T::MinVestedTransfer::get(); + let per_block_duration_20 = transfer_amount.checked_div(&20u32.into()).unwrap(); expected_balance += transfer_amount; - let vesting_schedule = VestingInfo::try_new::( + let vesting_schedule = VestingInfo::new::( transfer_amount, - duration.into(), + per_block_duration_20, 1u32.into(), - )?; + ); }: force_vested_transfer(RawOrigin::Root, source_lookup, target_lookup, vesting_schedule) verify { assert_eq!( @@ -322,7 +328,7 @@ benchmarks! { } not_unlocking_merge_schedules { - let l in 0 .. MaxLocksOf::::get(); + let l in 0 .. MaxLocksOf::::get() - 1; let caller: T::AccountId = account("caller", 0, SEED); let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); @@ -330,6 +336,9 @@ benchmarks! { add_locks::(&caller, l as u8); // Add max vesting schedules let expected_balance = add_vesting_schedules::(caller_lookup.clone(), T::MaxVestingSchedules::get())?; + // Values of the schedules added by `add_vesting_schedules`. + let transfer_amount = T::MinVestedTransfer::get(); + let per_block_duration_20 = transfer_amount.checked_div(&20u32.into()).unwrap(); // Schedules are not vesting at block 0 assert_eq!(System::::block_number(), T::BlockNumber::zero()); @@ -345,11 +354,11 @@ benchmarks! { ); }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, T::MaxVestingSchedules::get() - 1) verify { - let expected_schedule = VestingInfo::try_new::( - (ED * 40u32).into(), - (ED * 2u32).into(), + let expected_schedule = VestingInfo::new::( + T::MinVestedTransfer::get() * 2u32.into(), + per_block_duration_20 * 2u32.into(), 1u32.into(), - )?; + ); let expected_index = (T::MaxVestingSchedules::get() - 2) as usize; assert_eq!( Vesting::::vesting(&caller).unwrap()[expected_index], @@ -368,7 +377,7 @@ benchmarks! { } unlocking_merge_schedules { - let l in 0 .. MaxLocksOf::::get(); + let l in 0 .. MaxLocksOf::::get() - 1; let caller: T::AccountId = account("caller", 0, SEED); let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); @@ -376,6 +385,9 @@ benchmarks! { add_locks::(&caller, l as u8); // Add max vesting schedules let mut expected_balance = add_vesting_schedules::(caller_lookup.clone(), T::MaxVestingSchedules::get())?; + // Values of the schedules added by `add_vesting_schedules`. + let transfer_amount = T::MinVestedTransfer::get(); + let per_block_duration_20 = transfer_amount.checked_div(&20u32.into()).unwrap(); // Go to half way through all the schedules duration. (They all start at 1, and have a duration of 20). System::::set_block_number(11u32.into()); @@ -393,11 +405,11 @@ benchmarks! { ); }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, T::MaxVestingSchedules::get() - 1) verify { - let expected_schedule = VestingInfo::try_new::( - (ED * 20u32).into(), - (ED * 2u32).into(), + let expected_schedule = VestingInfo::new::( + transfer_amount, + per_block_duration_20 * 2u32.into(), 11u32.into(), - )?; + ); let expected_index = (T::MaxVestingSchedules::get() - 2) as usize; assert_eq!( Vesting::::vesting(&caller).unwrap()[expected_index], diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 05f4df7dd0834..04221262a667d 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -84,7 +84,7 @@ type MaxLocksOf = const VESTING_ID: LockIdentifier = *b"vesting "; const LOG_TARGET: &'static str = "runtime::vesting"; -// A value placed in storage that represents the current version of the Staking storage. +// A value placed in storage that represents the current version of the Vesting storage. // This value is used by `on_runtime_upgrade` to determine whether we run storage migration logic. #[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug)] enum Releases { @@ -94,8 +94,6 @@ enum Releases { impl Default for Releases { fn default() -> Self { - // TODO: verify assumption that this will be value in storages - // when we try to do runtime upgrade Releases::V0 } } diff --git a/frame/vesting/src/migrations.rs b/frame/vesting/src/migrations.rs index 5e59d112e0a7f..714ff5ee76602 100644 --- a/frame/vesting/src/migrations.rs +++ b/frame/vesting/src/migrations.rs @@ -27,12 +27,10 @@ pub(crate) mod v1 { pub(crate) fn pre_migrate() -> Result<(), &'static str> { assert!(StorageVersion::::get() == Releases::V0, "Storage version too high."); - // TODO: figure out how to iterate over old type of values - // Vesting::::translate::, T::BlockNumber>, _>( - // |_key, vesting_info| { - // assert!(vesting_info.per_block() > Zero::zero(), "A schedule with per_block of 0 exists"); - // Some(vesting_info) - // }); + log::debug!( + target: LOG_TARGET, + "Vesting storage version v1 **PRE** migration checks succesful!" + ); Ok(()) } @@ -59,17 +57,23 @@ pub(crate) mod v1 { assert_eq!(StorageVersion::::get(), Releases::V1); for (key, schedules) in Vesting::::iter() { - log::debug!(target: LOG_TARGET, "[post_migrate] Vesting key {}", key); // Assert the new bound vec respects size. assert!(schedules.len() > 0, "A bounded vec with no items was created."); - assert!(schedules.len() <= T::MaxVestingSchedules::get() as usize, "A bounded vec with too many items was created."); + assert!( + schedules.len() <= T::MaxVestingSchedules::get() as usize, + "A bounded vec with too many items was created." + ); for s in schedules { - // Check for infinite schedules + // Check for infinite schedules. assert!(s.per_block() > Zero::zero(), "A schedule with per_block of 0 exists"); } } + log::debug!( + target: LOG_TARGET, + "Vesting storage version v1 **POST** migration checks succesful!" + ); Ok(()) } } From 652d3eed931a38c53a84e489750d6c3a8f8df773 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Sun, 4 Jul 2021 15:33:51 -0700 Subject: [PATCH 053/144] Benchmarking working --- frame/vesting/src/weights.rs | 206 +++++++++++++++-------------------- 1 file changed, 88 insertions(+), 118 deletions(-) diff --git a/frame/vesting/src/weights.rs b/frame/vesting/src/weights.rs index 31a14c3a45317..1b5a884bbb63e 100644 --- a/frame/vesting/src/weights.rs +++ b/frame/vesting/src/weights.rs @@ -18,22 +18,18 @@ //! Autogenerated weights for pallet_vesting //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-06-19, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 +//! DATE: 2021-07-04, STEPS: `[]`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 128 // Executed Command: // target/release/substrate // benchmark -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=pallet_vesting +// --pallet=pallet-vesting // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 // --output=./frame/vesting/src/weights.rs -// --template=./.maintain/frame-weight-template.hbs +// --template=.maintain/frame-weight-template.hbs + #![allow(unused_parens)] #![allow(unused_imports)] @@ -47,176 +43,150 @@ pub trait WeightInfo { fn vest_unlocked(l: u32, ) -> Weight; fn vest_other_locked(l: u32, ) -> Weight; fn vest_other_unlocked(l: u32, ) -> Weight; - fn first_vested_transfer(l: u32, ) -> Weight; fn last_vested_transfer(l: u32, ) -> Weight; - fn first_force_vested_transfer(l: u32) -> Weight; - fn last_force_vested_transfer(l: u32) -> Weight; - fn unlocking_merge_schedules(l: u32) -> Weight; - fn not_unlocking_merge_schedules(l: u32) -> Weight; + fn first_vested_transfer(l: u32, ) -> Weight; + fn first_force_vested_transfer(l: u32, ) -> Weight; + fn last_force_vested_transfer(l: u32, ) -> Weight; + fn not_unlocking_merge_schedules(l: u32, ) -> Weight; + fn unlocking_merge_schedules(l: u32, ) -> Weight; } /// Weights for pallet_vesting using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { fn vest_locked(l: u32, ) -> Weight { - (42_905_000 as Weight) + (44_879_000 as Weight) // Standard Error: 13_000 - .saturating_add((232_000 as Weight).saturating_mul(l as Weight)) + .saturating_add((76_000 as Weight).saturating_mul(l as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } - fn vest_unlocked(l: u32, ) -> Weight { - (45_650_000 as Weight) - // Standard Error: 12_000 - .saturating_add((215_000 as Weight).saturating_mul(l as Weight)) + (41_286_000 as Weight) + // Standard Error: 417_000 + .saturating_add((331_000 as Weight).saturating_mul(l as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } - fn vest_other_locked(l: u32, ) -> Weight { - (42_273_000 as Weight) - // Standard Error: 15_000 - .saturating_add((246_000 as Weight).saturating_mul(l as Weight)) + (40_099_000 as Weight) + // Standard Error: 25_000 + .saturating_add((140_000 as Weight).saturating_mul(l as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn vest_other_unlocked(l: u32, ) -> Weight { - (45_324_000 as Weight) - // Standard Error: 12_000 - .saturating_add((214_000 as Weight).saturating_mul(l as Weight)) + (39_132_000 as Weight) + // Standard Error: 93_000 + .saturating_add((71_000 as Weight).saturating_mul(l as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - - fn first_vested_transfer(l: u32, ) -> Weight { - (98_812_000 as Weight) - // Standard Error: 13_000 - .saturating_add((139_000 as Weight).saturating_mul(l as Weight)) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) - .saturating_add(T::DbWeight::get().writes(4 as Weight)) - } - fn last_vested_transfer(l: u32, ) -> Weight { - (98_812_000 as Weight) - // Standard Error: 13_000 - .saturating_add((139_000 as Weight).saturating_mul(l as Weight)) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) - .saturating_add(T::DbWeight::get().writes(4 as Weight)) + (61_648_000 as Weight) + // Standard Error: 565_000 + .saturating_add((576_000 as Weight).saturating_mul(l as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } + fn first_vested_transfer(_l: u32, ) -> Weight { + (113_429_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn first_force_vested_transfer(l: u32, ) -> Weight { - (98_812_000 as Weight) - // Standard Error: 13_000 - .saturating_add((139_000 as Weight).saturating_mul(l as Weight)) + (75_462_000 as Weight) + // Standard Error: 389_000 + .saturating_add((538_000 as Weight).saturating_mul(l as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } - - fn last_force_vested_transfer(l: u32) -> Weight { - (98_812_000 as Weight) - // Standard Error: 13_000 - .saturating_add((139_000 as Weight).saturating_mul(l as Weight)) + fn last_force_vested_transfer(l: u32, ) -> Weight { + (63_044_000 as Weight) + // Standard Error: 207_000 + .saturating_add((386_000 as Weight).saturating_mul(l as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } - - fn unlocking_merge_schedules(l: u32) -> Weight { - (98_812_000 as Weight) - // Standard Error: 13_000 - .saturating_add((139_000 as Weight).saturating_mul(l as Weight)) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) - .saturating_add(T::DbWeight::get().writes(4 as Weight)) + fn not_unlocking_merge_schedules(_l: u32, ) -> Weight { + (71_495_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - - fn not_unlocking_merge_schedules(l: u32) -> Weight { - (98_812_000 as Weight) - // Standard Error: 13_000 - .saturating_add((139_000 as Weight).saturating_mul(l as Weight)) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) - .saturating_add(T::DbWeight::get().writes(4 as Weight)) + fn unlocking_merge_schedules(l: u32, ) -> Weight { + (64_495_000 as Weight) + // Standard Error: 247_000 + .saturating_add((124_000 as Weight).saturating_mul(l as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } } // For backwards compatibility and tests impl WeightInfo for () { fn vest_locked(l: u32, ) -> Weight { - (42_905_000 as Weight) + (44_879_000 as Weight) // Standard Error: 13_000 - .saturating_add((232_000 as Weight).saturating_mul(l as Weight)) + .saturating_add((76_000 as Weight).saturating_mul(l as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } - fn vest_unlocked(l: u32, ) -> Weight { - (45_650_000 as Weight) - // Standard Error: 12_000 - .saturating_add((215_000 as Weight).saturating_mul(l as Weight)) + (41_286_000 as Weight) + // Standard Error: 417_000 + .saturating_add((331_000 as Weight).saturating_mul(l as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } - fn vest_other_locked(l: u32, ) -> Weight { - (42_273_000 as Weight) - // Standard Error: 15_000 - .saturating_add((246_000 as Weight).saturating_mul(l as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(2 as Weight)) - } - - fn vest_other_unlocked(l: u32, ) -> Weight { - (45_324_000 as Weight) - // Standard Error: 12_000 - .saturating_add((214_000 as Weight).saturating_mul(l as Weight)) + (40_099_000 as Weight) + // Standard Error: 25_000 + .saturating_add((140_000 as Weight).saturating_mul(l as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - - fn first_vested_transfer(l: u32, ) -> Weight { - (45_324_000 as Weight) - // Standard Error: 12_000 - .saturating_add((214_000 as Weight).saturating_mul(l as Weight)) + fn vest_other_unlocked(l: u32, ) -> Weight { + (39_132_000 as Weight) + // Standard Error: 93_000 + .saturating_add((71_000 as Weight).saturating_mul(l as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn last_vested_transfer(l: u32, ) -> Weight { - (45_324_000 as Weight) - // Standard Error: 12_000 - .saturating_add((214_000 as Weight).saturating_mul(l as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) - } - - fn first_force_vested_transfer(l: u32, ) -> Weight { - (45_324_000 as Weight) - // Standard Error: 12_000 - .saturating_add((214_000 as Weight).saturating_mul(l as Weight)) + (61_648_000 as Weight) + // Standard Error: 565_000 + .saturating_add((576_000 as Weight).saturating_mul(l as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - - fn last_force_vested_transfer(l: u32) -> Weight { - (45_324_000 as Weight) - // Standard Error: 12_000 - .saturating_add((214_000 as Weight).saturating_mul(l as Weight)) + fn first_vested_transfer(_l: u32, ) -> Weight { + (113_429_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - - fn unlocking_merge_schedules(l: u32) -> Weight { - (45_324_000 as Weight) - // Standard Error: 12_000 - .saturating_add((214_000 as Weight).saturating_mul(l as Weight)) + fn first_force_vested_transfer(l: u32, ) -> Weight { + (75_462_000 as Weight) + // Standard Error: 389_000 + .saturating_add((538_000 as Weight).saturating_mul(l as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(4 as Weight)) + } + fn last_force_vested_transfer(l: u32, ) -> Weight { + (63_044_000 as Weight) + // Standard Error: 207_000 + .saturating_add((386_000 as Weight).saturating_mul(l as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(4 as Weight)) + } + fn not_unlocking_merge_schedules(_l: u32, ) -> Weight { + (71_495_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - - fn not_unlocking_merge_schedules(l: u32) -> Weight { - (45_324_000 as Weight) - // Standard Error: 12_000 - .saturating_add((214_000 as Weight).saturating_mul(l as Weight)) + fn unlocking_merge_schedules(l: u32, ) -> Weight { + (64_495_000 as Weight) + // Standard Error: 247_000 + .saturating_add((124_000 as Weight).saturating_mul(l as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } From ec87a9ac0707096525b1a2e51f697f4d636beda0 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Sun, 4 Jul 2021 16:04:16 -0700 Subject: [PATCH 054/144] benchmarking: step over max schedules --- frame/vesting/src/benchmarking.rs | 42 ++++--- frame/vesting/src/lib.rs | 16 +-- frame/vesting/src/weights.rs | 180 +++++++++++++++++------------- 3 files changed, 136 insertions(+), 102 deletions(-) diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index b936492723b17..fa7629da58b8f 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -78,12 +78,13 @@ fn add_vesting_schedules( benchmarks! { vest_locked { let l in 0 .. MaxLocksOf::::get() - 1; + let s in 1 .. T::MaxVestingSchedules::get(); let caller: T::AccountId = whitelisted_caller(); let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value() / 2u32.into()); add_locks::(&caller, l as u8); - let expected_balance = add_vesting_schedules::(caller_lookup, T::MaxVestingSchedules::get())?; + let expected_balance = add_vesting_schedules::(caller_lookup, s)?; // At block zero, everything is vested. assert_eq!(System::::block_number(), T::BlockNumber::zero()); @@ -104,12 +105,13 @@ benchmarks! { vest_unlocked { let l in 0 .. MaxLocksOf::::get() - 1; + let s in 1 .. T::MaxVestingSchedules::get(); let caller: T::AccountId = whitelisted_caller(); let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value() / 2u32.into()); add_locks::(&caller, l as u8); - add_vesting_schedules::(caller_lookup, T::MaxVestingSchedules::get())?; + add_vesting_schedules::(caller_lookup, s)?; // At block 21, everything is unvested. System::::set_block_number(21u32.into()); @@ -130,12 +132,13 @@ benchmarks! { vest_other_locked { let l in 0 .. MaxLocksOf::::get() - 1; + let s in 1 .. T::MaxVestingSchedules::get(); let other: T::AccountId = account("other", 0, SEED); let other_lookup: ::Source = T::Lookup::unlookup(other.clone()); T::Currency::make_free_balance_be(&other, BalanceOf::::max_value() / 2u32.into()); add_locks::(&other, l as u8); - let expected_balance = add_vesting_schedules::(other_lookup.clone(), T::MaxVestingSchedules::get())?; + let expected_balance = add_vesting_schedules::(other_lookup.clone(), s)?; // At block zero, everything is vested. assert_eq!(System::::block_number(), T::BlockNumber::zero()); @@ -158,13 +161,14 @@ benchmarks! { vest_other_unlocked { let l in 0 .. MaxLocksOf::::get() - 1; + let s in 1 .. T::MaxVestingSchedules::get(); let other: T::AccountId = account("other", 0, SEED); let other_lookup: ::Source = T::Lookup::unlookup(other.clone()); T::Currency::make_free_balance_be(&other, BalanceOf::::max_value() / 2u32.into()); add_locks::(&other, l as u8); - add_vesting_schedules::(other_lookup.clone(), T::MaxVestingSchedules::get())?; + add_vesting_schedules::(other_lookup.clone(), s)?; // At block 21, everything is unvested. System::::set_block_number(21u32.into()); assert_eq!( @@ -186,6 +190,7 @@ benchmarks! { last_vested_transfer { let l in 0 .. MaxLocksOf::::get() - 1; + let s in 0 .. T::MaxVestingSchedules::get() - 1; let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); @@ -195,8 +200,7 @@ benchmarks! { // Give target existing locks add_locks::(&target, l as u8); // Add one less than max vesting schedules - let one_less_than_max_vesting = T::MaxVestingSchedules::get() - 1; - let mut expected_balance = add_vesting_schedules::(target_lookup.clone(), one_less_than_max_vesting)?; + let mut expected_balance = add_vesting_schedules::(target_lookup.clone(), s)?; let transfer_amount = T::MinVestedTransfer::get(); let per_block_duration_20 = transfer_amount.checked_div(&20u32.into()).unwrap(); @@ -291,6 +295,7 @@ benchmarks! { last_force_vested_transfer { let l in 0 .. MaxLocksOf::::get() - 1; + let s in 0 .. T::MaxVestingSchedules::get() - 1; let source: T::AccountId = account("source", 0, SEED); let source_lookup: ::Source = T::Lookup::unlookup(source.clone()); @@ -301,8 +306,7 @@ benchmarks! { // Give target existing locks add_locks::(&target, l as u8); // Add one less than max vesting schedules - let one_less_than_max_vesting = T::MaxVestingSchedules::get() - 1; - let mut expected_balance = add_vesting_schedules::(target_lookup.clone(), one_less_than_max_vesting)?; + let mut expected_balance = add_vesting_schedules::(target_lookup.clone(), s)?; let transfer_amount = T::MinVestedTransfer::get(); let per_block_duration_20 = transfer_amount.checked_div(&20u32.into()).unwrap(); @@ -329,13 +333,14 @@ benchmarks! { not_unlocking_merge_schedules { let l in 0 .. MaxLocksOf::::get() - 1; + let s in 2 .. T::MaxVestingSchedules::get(); let caller: T::AccountId = account("caller", 0, SEED); let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); // Give target existing locks add_locks::(&caller, l as u8); // Add max vesting schedules - let expected_balance = add_vesting_schedules::(caller_lookup.clone(), T::MaxVestingSchedules::get())?; + let expected_balance = add_vesting_schedules::(caller_lookup.clone(), s)?; // Values of the schedules added by `add_vesting_schedules`. let transfer_amount = T::MinVestedTransfer::get(); let per_block_duration_20 = transfer_amount.checked_div(&20u32.into()).unwrap(); @@ -349,17 +354,17 @@ benchmarks! { ); assert_eq!( Vesting::::vesting(&caller).unwrap().len(), - (T::MaxVestingSchedules::get()) as usize, + s as usize, "There should be exactly max vesting schedules" ); - }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, T::MaxVestingSchedules::get() - 1) + }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, s - 1) verify { let expected_schedule = VestingInfo::new::( T::MinVestedTransfer::get() * 2u32.into(), per_block_duration_20 * 2u32.into(), 1u32.into(), ); - let expected_index = (T::MaxVestingSchedules::get() - 2) as usize; + let expected_index = (s - 2) as usize; assert_eq!( Vesting::::vesting(&caller).unwrap()[expected_index], expected_schedule @@ -371,20 +376,21 @@ benchmarks! { ); assert_eq!( Vesting::::vesting(&caller).unwrap().len(), - (T::MaxVestingSchedules::get() - 1) as usize, + (s - 1) as usize, "Schedule count should reduce by 1" ); } unlocking_merge_schedules { let l in 0 .. MaxLocksOf::::get() - 1; + let s in 2 .. T::MaxVestingSchedules::get(); let caller: T::AccountId = account("caller", 0, SEED); let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); // Give target existing locks add_locks::(&caller, l as u8); // Add max vesting schedules - let mut expected_balance = add_vesting_schedules::(caller_lookup.clone(), T::MaxVestingSchedules::get())?; + let mut expected_balance = add_vesting_schedules::(caller_lookup.clone(), s)?; // Values of the schedules added by `add_vesting_schedules`. let transfer_amount = T::MinVestedTransfer::get(); let per_block_duration_20 = transfer_amount.checked_div(&20u32.into()).unwrap(); @@ -400,17 +406,17 @@ benchmarks! { ); assert_eq!( Vesting::::vesting(&caller).unwrap().len(), - (T::MaxVestingSchedules::get()) as usize, + s as usize, "There should be exactly max vesting schedules" ); - }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, T::MaxVestingSchedules::get() - 1) + }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, s - 1) verify { let expected_schedule = VestingInfo::new::( transfer_amount, per_block_duration_20 * 2u32.into(), 11u32.into(), ); - let expected_index = (T::MaxVestingSchedules::get() - 2) as usize; + let expected_index = (s - 2) as usize; assert_eq!( Vesting::::vesting(&caller).unwrap()[expected_index], expected_schedule, @@ -427,7 +433,7 @@ benchmarks! { ); assert_eq!( Vesting::::vesting(&caller).unwrap().len(), - (T::MaxVestingSchedules::get() - 1) as usize, + (s - 1) as usize, "Schedule count should reduce by 1" ); } diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 935ce16269e2d..d9e2b09832bd1 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -289,8 +289,8 @@ pub mod pallet { /// - Reads: Vesting Storage, Balances Locks, [Sender Account] /// - Writes: Vesting Storage, Balances Locks, [Sender Account] /// # - #[pallet::weight(T::WeightInfo::vest_locked(MaxLocksOf::::get()) - .max(T::WeightInfo::vest_unlocked(MaxLocksOf::::get())) + #[pallet::weight(T::WeightInfo::vest_locked(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) + .max(T::WeightInfo::vest_unlocked(MaxLocksOf::::get(), T::MaxVestingSchedules::get())) )] pub fn vest(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; @@ -312,8 +312,8 @@ pub mod pallet { /// - Reads: Vesting Storage, Balances Locks, Target Account /// - Writes: Vesting Storage, Balances Locks, Target Account /// # - #[pallet::weight(T::WeightInfo::vest_other_locked(MaxLocksOf::::get()) - .max(T::WeightInfo::vest_other_unlocked(MaxLocksOf::::get())) + #[pallet::weight(T::WeightInfo::vest_other_locked(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) + .max(T::WeightInfo::vest_other_unlocked(MaxLocksOf::::get(), T::MaxVestingSchedules::get())) )] pub fn vest_other( origin: OriginFor, @@ -340,7 +340,7 @@ pub mod pallet { /// - Writes: Vesting Storage, Balances Locks, Target Account, [Sender Account] /// # #[pallet::weight( - T::WeightInfo::last_vested_transfer(MaxLocksOf::::get()) + T::WeightInfo::last_vested_transfer(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) .max(T::WeightInfo::first_vested_transfer(MaxLocksOf::::get())) )] pub fn vested_transfer( @@ -371,7 +371,7 @@ pub mod pallet { /// # #[pallet::weight( T::WeightInfo::first_force_vested_transfer(MaxLocksOf::::get()) - .max(T::WeightInfo::last_force_vested_transfer(MaxLocksOf::::get())) + .max(T::WeightInfo::last_force_vested_transfer(MaxLocksOf::::get(), T::MaxVestingSchedules::get())) )] pub fn force_vested_transfer( origin: OriginFor, @@ -403,8 +403,8 @@ pub mod pallet { /// - `schedule1_index`: index of the first schedule to merge. /// - `schedule2_index`: index of the second schedule to merge. #[pallet::weight( - T::WeightInfo::not_unlocking_merge_schedules(MaxLocksOf::::get()) - .max(T::WeightInfo::unlocking_merge_schedules(MaxLocksOf::::get())) + T::WeightInfo::not_unlocking_merge_schedules(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) + .max(T::WeightInfo::unlocking_merge_schedules(MaxLocksOf::::get(), T::MaxVestingSchedules::get())) )] pub fn merge_schedules( origin: OriginFor, diff --git a/frame/vesting/src/weights.rs b/frame/vesting/src/weights.rs index 1b5a884bbb63e..04b12737b497f 100644 --- a/frame/vesting/src/weights.rs +++ b/frame/vesting/src/weights.rs @@ -39,84 +39,98 @@ use sp_std::marker::PhantomData; /// Weight functions needed for pallet_vesting. pub trait WeightInfo { - fn vest_locked(l: u32, ) -> Weight; - fn vest_unlocked(l: u32, ) -> Weight; - fn vest_other_locked(l: u32, ) -> Weight; - fn vest_other_unlocked(l: u32, ) -> Weight; - fn last_vested_transfer(l: u32, ) -> Weight; + fn vest_locked(l: u32, s: u32, ) -> Weight; + fn vest_unlocked(l: u32, s: u32, ) -> Weight; + fn vest_other_locked(l: u32, s: u32, ) -> Weight; + fn vest_other_unlocked(l: u32, s: u32, ) -> Weight; + fn last_vested_transfer(l: u32, s: u32, ) -> Weight; fn first_vested_transfer(l: u32, ) -> Weight; fn first_force_vested_transfer(l: u32, ) -> Weight; - fn last_force_vested_transfer(l: u32, ) -> Weight; - fn not_unlocking_merge_schedules(l: u32, ) -> Weight; - fn unlocking_merge_schedules(l: u32, ) -> Weight; + fn last_force_vested_transfer(l: u32, s: u32, ) -> Weight; + fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight; + fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight; } /// Weights for pallet_vesting using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - fn vest_locked(l: u32, ) -> Weight { - (44_879_000 as Weight) - // Standard Error: 13_000 - .saturating_add((76_000 as Weight).saturating_mul(l as Weight)) + fn vest_locked(l: u32, s: u32, ) -> Weight { + (50_388_000 as Weight) + // Standard Error: 57_000 + .saturating_add((123_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 137_000 + .saturating_add((183_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } - fn vest_unlocked(l: u32, ) -> Weight { - (41_286_000 as Weight) - // Standard Error: 417_000 - .saturating_add((331_000 as Weight).saturating_mul(l as Weight)) + fn vest_unlocked(_l: u32, s: u32, ) -> Weight { + (62_248_000 as Weight) + // Standard Error: 429_000 + .saturating_add((127_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } - fn vest_other_locked(l: u32, ) -> Weight { - (40_099_000 as Weight) - // Standard Error: 25_000 - .saturating_add((140_000 as Weight).saturating_mul(l as Weight)) + fn vest_other_locked(l: u32, s: u32, ) -> Weight { + (40_181_000 as Weight) + // Standard Error: 100_000 + .saturating_add((295_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 239_000 + .saturating_add((509_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn vest_other_unlocked(l: u32, ) -> Weight { - (39_132_000 as Weight) - // Standard Error: 93_000 - .saturating_add((71_000 as Weight).saturating_mul(l as Weight)) + fn vest_other_unlocked(l: u32, _s: u32, ) -> Weight { + (79_110_000 as Weight) + // Standard Error: 265_000 + .saturating_add((17_000 as Weight).saturating_mul(l as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn last_vested_transfer(l: u32, ) -> Weight { - (61_648_000 as Weight) - // Standard Error: 565_000 - .saturating_add((576_000 as Weight).saturating_mul(l as Weight)) + fn last_vested_transfer(l: u32, s: u32, ) -> Weight { + (69_185_000 as Weight) + // Standard Error: 35_000 + .saturating_add((183_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 85_000 + .saturating_add((265_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn first_vested_transfer(_l: u32, ) -> Weight { - (113_429_000 as Weight) + (94_473_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn first_force_vested_transfer(l: u32, ) -> Weight { - (75_462_000 as Weight) - // Standard Error: 389_000 - .saturating_add((538_000 as Weight).saturating_mul(l as Weight)) + (75_989_000 as Weight) + // Standard Error: 22_000 + .saturating_add((141_000 as Weight).saturating_mul(l as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } - fn last_force_vested_transfer(l: u32, ) -> Weight { - (63_044_000 as Weight) - // Standard Error: 207_000 - .saturating_add((386_000 as Weight).saturating_mul(l as Weight)) + fn last_force_vested_transfer(l: u32, s: u32, ) -> Weight { + (72_107_000 as Weight) + // Standard Error: 121_000 + .saturating_add((92_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 289_000 + .saturating_add((346_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } - fn not_unlocking_merge_schedules(_l: u32, ) -> Weight { - (71_495_000 as Weight) + fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { + (50_015_000 as Weight) + // Standard Error: 54_000 + .saturating_add((215_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 139_000 + .saturating_add((375_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn unlocking_merge_schedules(l: u32, ) -> Weight { - (64_495_000 as Weight) - // Standard Error: 247_000 - .saturating_add((124_000 as Weight).saturating_mul(l as Weight)) + fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { + (55_278_000 as Weight) + // Standard Error: 39_000 + .saturating_add((154_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 101_000 + .saturating_add((71_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } @@ -124,69 +138,83 @@ impl WeightInfo for SubstrateWeight { // For backwards compatibility and tests impl WeightInfo for () { - fn vest_locked(l: u32, ) -> Weight { - (44_879_000 as Weight) - // Standard Error: 13_000 - .saturating_add((76_000 as Weight).saturating_mul(l as Weight)) + fn vest_locked(l: u32, s: u32, ) -> Weight { + (50_388_000 as Weight) + // Standard Error: 57_000 + .saturating_add((123_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 137_000 + .saturating_add((183_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } - fn vest_unlocked(l: u32, ) -> Weight { - (41_286_000 as Weight) - // Standard Error: 417_000 - .saturating_add((331_000 as Weight).saturating_mul(l as Weight)) + fn vest_unlocked(_l: u32, s: u32, ) -> Weight { + (62_248_000 as Weight) + // Standard Error: 429_000 + .saturating_add((127_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } - fn vest_other_locked(l: u32, ) -> Weight { - (40_099_000 as Weight) - // Standard Error: 25_000 - .saturating_add((140_000 as Weight).saturating_mul(l as Weight)) + fn vest_other_locked(l: u32, s: u32, ) -> Weight { + (40_181_000 as Weight) + // Standard Error: 100_000 + .saturating_add((295_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 239_000 + .saturating_add((509_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn vest_other_unlocked(l: u32, ) -> Weight { - (39_132_000 as Weight) - // Standard Error: 93_000 - .saturating_add((71_000 as Weight).saturating_mul(l as Weight)) + fn vest_other_unlocked(l: u32, _s: u32, ) -> Weight { + (79_110_000 as Weight) + // Standard Error: 265_000 + .saturating_add((17_000 as Weight).saturating_mul(l as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn last_vested_transfer(l: u32, ) -> Weight { - (61_648_000 as Weight) - // Standard Error: 565_000 - .saturating_add((576_000 as Weight).saturating_mul(l as Weight)) + fn last_vested_transfer(l: u32, s: u32, ) -> Weight { + (69_185_000 as Weight) + // Standard Error: 35_000 + .saturating_add((183_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 85_000 + .saturating_add((265_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn first_vested_transfer(_l: u32, ) -> Weight { - (113_429_000 as Weight) + (94_473_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn first_force_vested_transfer(l: u32, ) -> Weight { - (75_462_000 as Weight) - // Standard Error: 389_000 - .saturating_add((538_000 as Weight).saturating_mul(l as Weight)) + (75_989_000 as Weight) + // Standard Error: 22_000 + .saturating_add((141_000 as Weight).saturating_mul(l as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } - fn last_force_vested_transfer(l: u32, ) -> Weight { - (63_044_000 as Weight) - // Standard Error: 207_000 - .saturating_add((386_000 as Weight).saturating_mul(l as Weight)) + fn last_force_vested_transfer(l: u32, s: u32, ) -> Weight { + (72_107_000 as Weight) + // Standard Error: 121_000 + .saturating_add((92_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 289_000 + .saturating_add((346_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } - fn not_unlocking_merge_schedules(_l: u32, ) -> Weight { - (71_495_000 as Weight) + fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { + (50_015_000 as Weight) + // Standard Error: 54_000 + .saturating_add((215_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 139_000 + .saturating_add((375_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn unlocking_merge_schedules(l: u32, ) -> Weight { - (64_495_000 as Weight) - // Standard Error: 247_000 - .saturating_add((124_000 as Weight).saturating_mul(l as Weight)) + fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { + (55_278_000 as Weight) + // Standard Error: 39_000 + .saturating_add((154_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 101_000 + .saturating_add((71_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } From 1b0eacb06772d45df2b50bc63cb0d5ef13810a82 Mon Sep 17 00:00:00 2001 From: Parity Bot Date: Sun, 4 Jul 2021 23:11:15 +0000 Subject: [PATCH 055/144] cargo run --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_vesting --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/vesting/src/weights.rs --template=./.maintain/frame-weight-template.hbs --- frame/vesting/src/weights.rs | 197 ++++++++++++++++++----------------- 1 file changed, 103 insertions(+), 94 deletions(-) diff --git a/frame/vesting/src/weights.rs b/frame/vesting/src/weights.rs index 04b12737b497f..8e279c86e6da2 100644 --- a/frame/vesting/src/weights.rs +++ b/frame/vesting/src/weights.rs @@ -18,17 +18,22 @@ //! Autogenerated weights for pallet_vesting //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-07-04, STEPS: `[]`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 128 +//! DATE: 2021-07-04, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: // target/release/substrate // benchmark -// --pallet=pallet-vesting +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_vesting // --extrinsic=* +// --execution=wasm // --wasm-execution=compiled +// --heap-pages=4096 // --output=./frame/vesting/src/weights.rs -// --template=.maintain/frame-weight-template.hbs +// --template=./.maintain/frame-weight-template.hbs #![allow(unused_parens)] @@ -55,82 +60,84 @@ pub trait WeightInfo { pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { fn vest_locked(l: u32, s: u32, ) -> Weight { - (50_388_000 as Weight) - // Standard Error: 57_000 - .saturating_add((123_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 137_000 - .saturating_add((183_000 as Weight).saturating_mul(s as Weight)) + (58_117_000 as Weight) + // Standard Error: 3_000 + .saturating_add((136_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 12_000 + .saturating_add((177_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } - fn vest_unlocked(_l: u32, s: u32, ) -> Weight { - (62_248_000 as Weight) - // Standard Error: 429_000 - .saturating_add((127_000 as Weight).saturating_mul(s as Weight)) + fn vest_unlocked(l: u32, s: u32, ) -> Weight { + (57_903_000 as Weight) + // Standard Error: 2_000 + .saturating_add((126_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 6_000 + .saturating_add((51_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn vest_other_locked(l: u32, s: u32, ) -> Weight { - (40_181_000 as Weight) - // Standard Error: 100_000 - .saturating_add((295_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 239_000 - .saturating_add((509_000 as Weight).saturating_mul(s as Weight)) + (57_736_000 as Weight) + // Standard Error: 1_000 + .saturating_add((140_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 4_000 + .saturating_add((159_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn vest_other_unlocked(l: u32, _s: u32, ) -> Weight { - (79_110_000 as Weight) - // Standard Error: 265_000 - .saturating_add((17_000 as Weight).saturating_mul(l as Weight)) + fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { + (57_278_000 as Weight) + // Standard Error: 1_000 + .saturating_add((131_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 5_000 + .saturating_add((68_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn last_vested_transfer(l: u32, s: u32, ) -> Weight { - (69_185_000 as Weight) - // Standard Error: 35_000 - .saturating_add((183_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 85_000 - .saturating_add((265_000 as Weight).saturating_mul(s as Weight)) + fn last_vested_transfer(l: u32, _s: u32, ) -> Weight { + (101_019_000 as Weight) + // Standard Error: 5_000 + .saturating_add((135_000 as Weight).saturating_mul(l as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn first_vested_transfer(_l: u32, ) -> Weight { - (94_473_000 as Weight) + fn first_vested_transfer(l: u32, ) -> Weight { + (118_733_000 as Weight) + // Standard Error: 5_000 + .saturating_add((138_000 as Weight).saturating_mul(l as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn first_force_vested_transfer(l: u32, ) -> Weight { - (75_989_000 as Weight) - // Standard Error: 22_000 - .saturating_add((141_000 as Weight).saturating_mul(l as Weight)) + (117_482_000 as Weight) + // Standard Error: 3_000 + .saturating_add((146_000 as Weight).saturating_mul(l as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } - fn last_force_vested_transfer(l: u32, s: u32, ) -> Weight { - (72_107_000 as Weight) - // Standard Error: 121_000 - .saturating_add((92_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 289_000 - .saturating_add((346_000 as Weight).saturating_mul(s as Weight)) + fn last_force_vested_transfer(l: u32, _s: u32, ) -> Weight { + (100_191_000 as Weight) + // Standard Error: 6_000 + .saturating_add((144_000 as Weight).saturating_mul(l as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - (50_015_000 as Weight) - // Standard Error: 54_000 - .saturating_add((215_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 139_000 - .saturating_add((375_000 as Weight).saturating_mul(s as Weight)) + (71_299_000 as Weight) + // Standard Error: 1_000 + .saturating_add((115_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 6_000 + .saturating_add((101_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - (55_278_000 as Weight) - // Standard Error: 39_000 - .saturating_add((154_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 101_000 - .saturating_add((71_000 as Weight).saturating_mul(s as Weight)) + (70_606_000 as Weight) + // Standard Error: 1_000 + .saturating_add((151_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 6_000 + .saturating_add((91_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } @@ -139,82 +146,84 @@ impl WeightInfo for SubstrateWeight { // For backwards compatibility and tests impl WeightInfo for () { fn vest_locked(l: u32, s: u32, ) -> Weight { - (50_388_000 as Weight) - // Standard Error: 57_000 - .saturating_add((123_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 137_000 - .saturating_add((183_000 as Weight).saturating_mul(s as Weight)) + (58_117_000 as Weight) + // Standard Error: 3_000 + .saturating_add((136_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 12_000 + .saturating_add((177_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } - fn vest_unlocked(_l: u32, s: u32, ) -> Weight { - (62_248_000 as Weight) - // Standard Error: 429_000 - .saturating_add((127_000 as Weight).saturating_mul(s as Weight)) + fn vest_unlocked(l: u32, s: u32, ) -> Weight { + (57_903_000 as Weight) + // Standard Error: 2_000 + .saturating_add((126_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 6_000 + .saturating_add((51_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn vest_other_locked(l: u32, s: u32, ) -> Weight { - (40_181_000 as Weight) - // Standard Error: 100_000 - .saturating_add((295_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 239_000 - .saturating_add((509_000 as Weight).saturating_mul(s as Weight)) + (57_736_000 as Weight) + // Standard Error: 1_000 + .saturating_add((140_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 4_000 + .saturating_add((159_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn vest_other_unlocked(l: u32, _s: u32, ) -> Weight { - (79_110_000 as Weight) - // Standard Error: 265_000 - .saturating_add((17_000 as Weight).saturating_mul(l as Weight)) + fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { + (57_278_000 as Weight) + // Standard Error: 1_000 + .saturating_add((131_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 5_000 + .saturating_add((68_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn last_vested_transfer(l: u32, s: u32, ) -> Weight { - (69_185_000 as Weight) - // Standard Error: 35_000 - .saturating_add((183_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 85_000 - .saturating_add((265_000 as Weight).saturating_mul(s as Weight)) + fn last_vested_transfer(l: u32, _s: u32, ) -> Weight { + (101_019_000 as Weight) + // Standard Error: 5_000 + .saturating_add((135_000 as Weight).saturating_mul(l as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn first_vested_transfer(_l: u32, ) -> Weight { - (94_473_000 as Weight) + fn first_vested_transfer(l: u32, ) -> Weight { + (118_733_000 as Weight) + // Standard Error: 5_000 + .saturating_add((138_000 as Weight).saturating_mul(l as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn first_force_vested_transfer(l: u32, ) -> Weight { - (75_989_000 as Weight) - // Standard Error: 22_000 - .saturating_add((141_000 as Weight).saturating_mul(l as Weight)) + (117_482_000 as Weight) + // Standard Error: 3_000 + .saturating_add((146_000 as Weight).saturating_mul(l as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } - fn last_force_vested_transfer(l: u32, s: u32, ) -> Weight { - (72_107_000 as Weight) - // Standard Error: 121_000 - .saturating_add((92_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 289_000 - .saturating_add((346_000 as Weight).saturating_mul(s as Weight)) + fn last_force_vested_transfer(l: u32, _s: u32, ) -> Weight { + (100_191_000 as Weight) + // Standard Error: 6_000 + .saturating_add((144_000 as Weight).saturating_mul(l as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - (50_015_000 as Weight) - // Standard Error: 54_000 - .saturating_add((215_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 139_000 - .saturating_add((375_000 as Weight).saturating_mul(s as Weight)) + (71_299_000 as Weight) + // Standard Error: 1_000 + .saturating_add((115_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 6_000 + .saturating_add((101_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - (55_278_000 as Weight) - // Standard Error: 39_000 - .saturating_add((154_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 101_000 - .saturating_add((71_000 as Weight).saturating_mul(s as Weight)) + (70_606_000 as Weight) + // Standard Error: 1_000 + .saturating_add((151_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 6_000 + .saturating_add((91_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } From 40fb4497cbbd1218f5e58a9828fd6861855683bc Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Sun, 4 Jul 2021 16:57:32 -0700 Subject: [PATCH 056/144] Simplify APIs by accepting vec; convert to bounded on write --- frame/vesting/src/lib.rs | 96 +++++++++++++++------------------------- 1 file changed, 36 insertions(+), 60 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index d9e2b09832bd1..0ca9cc8ec1608 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -417,33 +417,27 @@ pub mod pallet { }; let schedule1_index = schedule1_index as usize; let schedule2_index = schedule2_index as usize; - let vesting = Self::vesting(&who).ok_or(Error::::NotVesting)?; + let schedules = Self::vesting(&who).ok_or(Error::::NotVesting)?; // The schedule index is based off of the schedule ordering prior to filtering out any // schedules that may be ending at this block. let schedule1 = - *vesting.get(schedule1_index).ok_or(Error::::ScheduleIndexOutOfBounds)?; + *schedules.get(schedule1_index).ok_or(Error::::ScheduleIndexOutOfBounds)?; let schedule2 = - *vesting.get(schedule2_index).ok_or(Error::::ScheduleIndexOutOfBounds)?; + *schedules.get(schedule2_index).ok_or(Error::::ScheduleIndexOutOfBounds)?; let merge_action = VestingAction::Merge(schedule1_index, schedule2_index); - // The length of vesting decreases by 2 here since wem filter out 2 schedules. Thus we know - // below that we can safely insert the new merged schedule. + // The length of `schedules` decreases by 2 here since we filter out 2 schedules. + // Thus we know below that we can insert the new merged schedule without error (assuming + // the its initial state was valid). let (mut schedules, mut locked_now) = - Self::report_schedule_updates(vesting, merge_action); + Self::report_schedule_updates(schedules.to_vec(), merge_action); let now = >::block_number(); if let Some(new_schedule) = Self::merge_vesting_info(now, schedule1, schedule2)? { // Merging created a new schedule; so now we need to add it to the - // accounts vesting schedules, - if let Err(_) = schedules.try_push(new_schedule) { - // (It shouldn't be possible for this to fail because we removed 2 schedules above.) - log::warn!( - target: LOG_TARGET, - "faulty logic led to attempting to add too many vesting schedules", - ); - return Err(Error::::AtMaxVestingSchedules.into()); - } + // accounts vesting schedule collection. + schedules.push(new_schedule); // (We use `locked_at` in case this is a schedule that started in the past.) let new_schedule_locked = new_schedule.locked_at::(now); // update the locked amount to reflect the schedule we just added, @@ -461,12 +455,7 @@ pub mod pallet { locked_now > Zero::zero() && schedules.len() > 0 || locked_now == Zero::zero() && schedules.len() == 0 ); - if let Err(e) = Self::write_vesting(&who, schedules) { - // The write should not fail because that would mean their were too - // many schedules to start out with. - log::warn!(target: LOG_TARGET, "an account has too many vesting schedules",); - return e.into(); - }; + Self::write_vesting(&who, schedules)?; Self::write_lock(&who, locked_now); Ok(()) @@ -564,13 +553,13 @@ impl Pallet { )?; // We can't let this fail because the currency transfer has already happened. - Self::add_vesting_schedule( + let res = Self::add_vesting_schedule( &target, schedule.locked(), schedule.per_block(), schedule.starting_block(), - ) - .expect("schedule inputs and vec bounds have been validated. q.e.d."); + ); + debug_assert!(res.is_ok(), "Failed to add a schedule when we had to succeed."); Ok(()) } @@ -579,16 +568,16 @@ impl Pallet { /// filter out completed and specified schedules. /// /// Returns a tuple that consists of: - /// - bounded vec of vesting schedules, where completed schedules and those - /// specified by filter are remove. + /// - vec of vesting schedules, where completed schedules and those + /// specified by filter are remove. (Note the vec is not checked for respecting + /// bounded length.) /// - the amount locked at the current block number based on the given schedules. /// /// NOTE: the amount locked does not include any schedules that are filtered out. fn report_schedule_updates( - schedules: BoundedVec, T::BlockNumber>, T::MaxVestingSchedules>, + schedules: Vec, T::BlockNumber>>, action: VestingAction, - ) -> (BoundedVec, T::BlockNumber>, T::MaxVestingSchedules>, BalanceOf) - { + ) -> (Vec, T::BlockNumber>>, BalanceOf) { let now = >::block_number(); let mut total_locked_now: BalanceOf = Zero::zero(); @@ -605,9 +594,7 @@ impl Pallet { Some(schedule) } }) - .collect::>() - .try_into() - .expect("`BoundedVec` is created from another `BoundedVec` with same bound; q.e.d."); + .collect::>(); (filtered_schedules, total_locked_now) } @@ -627,11 +614,14 @@ impl Pallet { /// Write an accounts updated vesting schedules to storage. fn write_vesting( who: &T::AccountId, - schedules: BoundedVec, T::BlockNumber>, T::MaxVestingSchedules>, + schedules: Vec, T::BlockNumber>>, ) -> Result<(), DispatchError> { - if schedules.len() > T::MaxVestingSchedules::get() as usize { - return Err(Error::::AtMaxVestingSchedules.into()); - } else if schedules.len() == 0 { + let schedules: BoundedVec< + VestingInfo, T::BlockNumber>, + T::MaxVestingSchedules, + > = schedules.try_into().map_err(|_| Error::::AtMaxVestingSchedules)?; + + if schedules.len() == 0 { Vesting::::remove(&who); } else { Vesting::::insert(who, schedules) @@ -645,19 +635,14 @@ impl Pallet { let schedules = Self::vesting(&who).ok_or(Error::::NotVesting)?; let (schedules, locked_now) = - Self::report_schedule_updates(schedules, VestingAction::Passive); + Self::report_schedule_updates(schedules.to_vec(), VestingAction::Passive); + debug_assert!(schedules.len() <= T::MaxVestingSchedules::get() as usize); debug_assert!( locked_now > Zero::zero() && schedules.len() > 0 || locked_now == Zero::zero() && schedules.len() == 0 ); - - if let Err(e) = Self::write_vesting(&who, schedules) { - // The write should not fail because that would mean their were too - // many schedules to start out with. - log::warn!(target: LOG_TARGET, "an account has too many vesting schedules",); - return e.into(); - }; + Self::write_vesting(&who, schedules)?; Self::write_lock(&who, locked_now); Ok(()) @@ -713,19 +698,14 @@ where ensure!(schedules.try_push(vesting_schedule).is_ok(), Error::::AtMaxVestingSchedules); let (schedules, locked_now) = - Self::report_schedule_updates(schedules, VestingAction::Passive); + Self::report_schedule_updates(schedules.to_vec(), VestingAction::Passive); + debug_assert!(schedules.len() <= T::MaxVestingSchedules::get() as usize); debug_assert!( locked_now > Zero::zero() && schedules.len() > 0 || locked_now == Zero::zero() && schedules.len() == 0 ); - - if let Err(e) = Self::write_vesting(&who, schedules) { - // The write should not fail because that would mean their where too - // many schedules to start out with. - log::warn!(target: LOG_TARGET, "an account has too many vesting schedules",); - return e.into(); - }; + Self::write_vesting(&who, schedules)?; Self::write_lock(who, locked_now); Ok(()) @@ -756,19 +736,15 @@ where let remove_action = VestingAction::Remove(schedule_index as usize); let schedules = Self::vesting(who).ok_or(Error::::NotVesting)?; - let (schedules, locked_now) = Self::report_schedule_updates(schedules, remove_action); + let (schedules, locked_now) = + Self::report_schedule_updates(schedules.to_vec(), remove_action); + debug_assert!(schedules.len() <= T::MaxVestingSchedules::get() as usize); debug_assert!( locked_now > Zero::zero() && schedules.len() > 0 || locked_now == Zero::zero() && schedules.len() == 0 ); - - if let Err(e) = Self::write_vesting(&who, schedules) { - // The write should not fail because that would mean their where too - // many schedules to start out with. - log::warn!(target: LOG_TARGET, "an account has too many vesting schedules",); - return e.into(); - }; + Self::write_vesting(&who, schedules)?; Self::write_lock(who, locked_now); Ok(()) } From 879a65d5906596f59c3239788fd296488b76c62e Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Sun, 4 Jul 2021 17:08:11 -0700 Subject: [PATCH 057/144] Test: build_genesis_has_storage_version_v1 --- frame/vesting/src/tests.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index d37f220a5b48f..a3acb8c4d573a 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -1017,6 +1017,13 @@ fn multiple_schedules_from_genesis_config_errors() { .build(); } +#[test] +fn build_genesis_has_storage_version_v1() { + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + assert_eq!(StorageVersion::::get(), Releases::V1); + }); +} + #[test] fn merge_vesting_errors_with_per_block_0() { // Faulty schedules with an infinite duration (per_block == 0) can be merged to create From 0b243e0cf5221f9c89b44603ecc774f75235aec6 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Sun, 4 Jul 2021 18:13:55 -0700 Subject: [PATCH 058/144] Test more error cases --- frame/vesting/src/lib.rs | 3 --- frame/vesting/src/tests.rs | 32 ++++++++++++++++++++++++++------ 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 0ca9cc8ec1608..c15f4b26673ca 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -637,7 +637,6 @@ impl Pallet { let (schedules, locked_now) = Self::report_schedule_updates(schedules.to_vec(), VestingAction::Passive); - debug_assert!(schedules.len() <= T::MaxVestingSchedules::get() as usize); debug_assert!( locked_now > Zero::zero() && schedules.len() > 0 || locked_now == Zero::zero() && schedules.len() == 0 @@ -700,7 +699,6 @@ where let (schedules, locked_now) = Self::report_schedule_updates(schedules.to_vec(), VestingAction::Passive); - debug_assert!(schedules.len() <= T::MaxVestingSchedules::get() as usize); debug_assert!( locked_now > Zero::zero() && schedules.len() > 0 || locked_now == Zero::zero() && schedules.len() == 0 @@ -739,7 +737,6 @@ where let (schedules, locked_now) = Self::report_schedule_updates(schedules.to_vec(), remove_action); - debug_assert!(schedules.len() <= T::MaxVestingSchedules::get() as usize); debug_assert!( locked_now > Zero::zero() && schedules.len() > 0 || locked_now == Zero::zero() && schedules.len() == 0 diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index a3acb8c4d573a..0a05a9daff79b 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -363,10 +363,10 @@ fn vested_transfer_correctly_fails() { Error::::InfiniteSchedule, ); - // `locked` is 0. + // `locked` is less than `MinVestedTransfer`. let schedule_locked_0 = VestingInfo::new::(0, 1, 10); assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), + Vesting::vested_transfer(Some(3).into(), 4, schedule_locked_0), Error::::AmountLow, ); @@ -755,10 +755,10 @@ fn merge_ongoing_and_yet_to_be_started_schedules() { 1, // Vesting over 256 * 10 (2560) blocks cur_block + 1, ); - Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); + assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched1)); // Merge the schedules before sched1 starts. - Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + assert_ok!(Vesting::merge_schedules(Some(2).into(), 0, 1)); // After merging, the usable balance only changes by the amount sched0 vested since we // last called `vest` (which is just 1 block). The usable balance is not affected by // sched1 because it has not started yet. @@ -963,6 +963,12 @@ fn merge_schedules_throws_proper_errors() { // It is a storage noop with no errors if the indexes are the same. assert_storage_noop!(Vesting::merge_schedules(Some(2).into(), 0, 0).unwrap()); + + // There are no vesting schedules, so they are not vesting + assert_noop!( + Vesting::merge_schedules(Some(4).into(), 0, 1), + Error::::NotVesting + ); }); } @@ -1026,8 +1032,6 @@ fn build_genesis_has_storage_version_v1() { #[test] fn merge_vesting_errors_with_per_block_0() { - // Faulty schedules with an infinite duration (per_block == 0) can be merged to create - // a schedule that vest 1 per_block, (helpful for faulty, legacy schedules). ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { let sched0 = VestingInfo::new::( ED, 0, // Vesting over infinite blocks. @@ -1125,3 +1129,19 @@ fn vesting_info_ending_block_works() { 0 ); } + +#[test] +fn vest_correctly_fails() { + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + assert!(!>::contains_key(4)); + assert_noop!(Vesting::vest(Some(4).into()), Error::::NotVesting); + }); +} + +#[test] +fn vest_other_correctly_fails() { + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + assert!(!>::contains_key(4)); + assert_noop!(Vesting::vest_other(Some(3).into(), 4), Error::::NotVesting); + }); +} From 1b6d3ec0c94405309c31a52d72a306158a828418 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Sun, 4 Jul 2021 20:46:59 -0700 Subject: [PATCH 059/144] Hack to get polkadot weights to work; should revert later --- frame/vesting/src/benchmarking.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index fa7629da58b8f..d551ef343989b 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -227,6 +227,8 @@ benchmarks! { first_vested_transfer { let l in 0 .. MaxLocksOf::::get() - 1; + // TODO: this is just here to get the polkadot runtimes too build .. need to figure that out + let s in 0 .. T::MaxVestingSchedules::get() - 1; let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); @@ -261,6 +263,8 @@ benchmarks! { first_force_vested_transfer { let l in 0 .. MaxLocksOf::::get() - 1; + // TODO: this is just here to get the polkadot runtimes too build .. need to figure that out + let s in 0 .. T::MaxVestingSchedules::get() - 1; let source: T::AccountId = account("source", 0, SEED); let source_lookup: ::Source = T::Lookup::unlookup(source.clone()); From a5299c774f2d296636377372bfc62bc1b8f14cd6 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 5 Jul 2021 11:47:07 -0700 Subject: [PATCH 060/144] Improve benchmarking; works on polkadot --- frame/vesting/src/benchmarking.rs | 80 +----------- frame/vesting/src/lib.rs | 7 +- frame/vesting/src/weights.rs | 194 ++++++++++++------------------ 3 files changed, 83 insertions(+), 198 deletions(-) diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index d551ef343989b..8f280acb99bf3 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -188,7 +188,7 @@ benchmarks! { ); } - last_vested_transfer { + vested_transfer { let l in 0 .. MaxLocksOf::::get() - 1; let s in 0 .. T::MaxVestingSchedules::get() - 1; @@ -211,7 +211,7 @@ benchmarks! { per_block_duration_20, 1u32.into(), ); - }: vested_transfer(RawOrigin::Signed(caller), target_lookup, vesting_schedule) + }: _(RawOrigin::Signed(caller), target_lookup, vesting_schedule) verify { assert_eq!( expected_balance, @@ -225,79 +225,7 @@ benchmarks! { ); } - first_vested_transfer { - let l in 0 .. MaxLocksOf::::get() - 1; - // TODO: this is just here to get the polkadot runtimes too build .. need to figure that out - let s in 0 .. T::MaxVestingSchedules::get() - 1; - - let caller: T::AccountId = whitelisted_caller(); - T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); - - let target: T::AccountId = account("target", 0, SEED); - let target_lookup: ::Source = T::Lookup::unlookup(target.clone()); - // Give target existing locks - add_locks::(&target, l as u8); - - let transfer_amount = T::MinVestedTransfer::get(); - let per_block_duration_20 = transfer_amount.checked_div(&20u32.into()).unwrap(); - - let vesting_schedule = VestingInfo::new::( - transfer_amount, - per_block_duration_20, - 1u32.into(), - ); - - }: vested_transfer(RawOrigin::Signed(caller), target_lookup, vesting_schedule) - verify { - assert_eq!( - transfer_amount, - T::Currency::free_balance(&target), - "Transfer didn't happen", - ); - assert_eq!( - Vesting::::vesting_balance(&target), - Some(transfer_amount), - "Lock not created", - ); - } - - first_force_vested_transfer { - let l in 0 .. MaxLocksOf::::get() - 1; - // TODO: this is just here to get the polkadot runtimes too build .. need to figure that out - let s in 0 .. T::MaxVestingSchedules::get() - 1; - - let source: T::AccountId = account("source", 0, SEED); - let source_lookup: ::Source = T::Lookup::unlookup(source.clone()); - T::Currency::make_free_balance_be(&source, BalanceOf::::max_value()); - - let target: T::AccountId = account("target", 0, SEED); - let target_lookup: ::Source = T::Lookup::unlookup(target.clone()); - // Give target existing locks - add_locks::(&target, l as u8); - - let transfer_amount = T::MinVestedTransfer::get(); - let per_block_duration_20 = transfer_amount.checked_div(&20u32.into()).unwrap(); - - let vesting_schedule = VestingInfo::new::( - transfer_amount, - per_block_duration_20, - 1u32.into(), - ); - }: force_vested_transfer(RawOrigin::Root, source_lookup, target_lookup, vesting_schedule) - verify { - assert_eq!( - transfer_amount, - T::Currency::free_balance(&target), - "Transfer didn't happen", - ); - assert_eq!( - Vesting::::vesting_balance(&target), - Some(transfer_amount), - "Lock not created", - ); - } - - last_force_vested_transfer { + force_vested_transfer { let l in 0 .. MaxLocksOf::::get() - 1; let s in 0 .. T::MaxVestingSchedules::get() - 1; @@ -321,7 +249,7 @@ benchmarks! { per_block_duration_20, 1u32.into(), ); - }: force_vested_transfer(RawOrigin::Root, source_lookup, target_lookup, vesting_schedule) + }: _(RawOrigin::Root, source_lookup, target_lookup, vesting_schedule) verify { assert_eq!( expected_balance, diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index c15f4b26673ca..75fb422f21657 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -340,8 +340,8 @@ pub mod pallet { /// - Writes: Vesting Storage, Balances Locks, Target Account, [Sender Account] /// # #[pallet::weight( - T::WeightInfo::last_vested_transfer(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) - .max(T::WeightInfo::first_vested_transfer(MaxLocksOf::::get())) + T::WeightInfo::vested_transfer(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) + )] pub fn vested_transfer( origin: OriginFor, @@ -370,8 +370,7 @@ pub mod pallet { /// - Writes: Vesting Storage, Balances Locks, Target Account, Source Account /// # #[pallet::weight( - T::WeightInfo::first_force_vested_transfer(MaxLocksOf::::get()) - .max(T::WeightInfo::last_force_vested_transfer(MaxLocksOf::::get(), T::MaxVestingSchedules::get())) + T::WeightInfo::force_vested_transfer(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) )] pub fn force_vested_transfer( origin: OriginFor, diff --git a/frame/vesting/src/weights.rs b/frame/vesting/src/weights.rs index 8e279c86e6da2..df36f8b642260 100644 --- a/frame/vesting/src/weights.rs +++ b/frame/vesting/src/weights.rs @@ -18,22 +18,22 @@ //! Autogenerated weights for pallet_vesting //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-07-04, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2021-07-05, STEPS: `[110, ]`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: // target/release/substrate // benchmark // --chain=dev -// --steps=50 -// --repeat=20 +// --steps=110 +// --repeat=1 // --pallet=pallet_vesting // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 // --output=./frame/vesting/src/weights.rs -// --template=./.maintain/frame-weight-template.hbs +// --template=.maintain/frame-weight-template.hbs #![allow(unused_parens)] @@ -48,10 +48,8 @@ pub trait WeightInfo { fn vest_unlocked(l: u32, s: u32, ) -> Weight; fn vest_other_locked(l: u32, s: u32, ) -> Weight; fn vest_other_unlocked(l: u32, s: u32, ) -> Weight; - fn last_vested_transfer(l: u32, s: u32, ) -> Weight; - fn first_vested_transfer(l: u32, ) -> Weight; - fn first_force_vested_transfer(l: u32, ) -> Weight; - fn last_force_vested_transfer(l: u32, s: u32, ) -> Weight; + fn vested_transfer(l: u32, s: u32, ) -> Weight; + fn force_vested_transfer(l: u32, s: u32, ) -> Weight; fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight; fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight; } @@ -60,84 +58,64 @@ pub trait WeightInfo { pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { fn vest_locked(l: u32, s: u32, ) -> Weight { - (58_117_000 as Weight) - // Standard Error: 3_000 - .saturating_add((136_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 12_000 - .saturating_add((177_000 as Weight).saturating_mul(s as Weight)) + (68_892_000 as Weight) + // Standard Error: 70_000 + .saturating_add((223_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 219_000 + .saturating_add((378_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn vest_unlocked(l: u32, s: u32, ) -> Weight { - (57_903_000 as Weight) - // Standard Error: 2_000 - .saturating_add((126_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 6_000 - .saturating_add((51_000 as Weight).saturating_mul(s as Weight)) + (72_557_000 as Weight) + // Standard Error: 33_000 + .saturating_add((140_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 105_000 + .saturating_add((157_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn vest_other_locked(l: u32, s: u32, ) -> Weight { - (57_736_000 as Weight) - // Standard Error: 1_000 - .saturating_add((140_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 4_000 - .saturating_add((159_000 as Weight).saturating_mul(s as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) - } - fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { - (57_278_000 as Weight) - // Standard Error: 1_000 - .saturating_add((131_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 5_000 - .saturating_add((68_000 as Weight).saturating_mul(s as Weight)) + (69_529_000 as Weight) + // Standard Error: 127_000 + .saturating_add((239_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 396_000 + .saturating_add((494_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn last_vested_transfer(l: u32, _s: u32, ) -> Weight { - (101_019_000 as Weight) - // Standard Error: 5_000 - .saturating_add((135_000 as Weight).saturating_mul(l as Weight)) + fn vest_other_unlocked(l: u32, _s: u32, ) -> Weight { + (78_096_000 as Weight) + // Standard Error: 36_000 + .saturating_add((50_000 as Weight).saturating_mul(l as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn first_vested_transfer(l: u32, ) -> Weight { - (118_733_000 as Weight) - // Standard Error: 5_000 - .saturating_add((138_000 as Weight).saturating_mul(l as Weight)) + fn vested_transfer(l: u32, s: u32, ) -> Weight { + (126_319_000 as Weight) + // Standard Error: 339_000 + .saturating_add((753_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 1_057_000 + .saturating_add((721_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn first_force_vested_transfer(l: u32, ) -> Weight { - (117_482_000 as Weight) - // Standard Error: 3_000 - .saturating_add((146_000 as Weight).saturating_mul(l as Weight)) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) - .saturating_add(T::DbWeight::get().writes(4 as Weight)) - } - fn last_force_vested_transfer(l: u32, _s: u32, ) -> Weight { - (100_191_000 as Weight) - // Standard Error: 6_000 - .saturating_add((144_000 as Weight).saturating_mul(l as Weight)) + fn force_vested_transfer(l: u32, s: u32, ) -> Weight { + (106_182_000 as Weight) + // Standard Error: 216_000 + .saturating_add((571_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 674_000 + .saturating_add((1_538_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } - fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - (71_299_000 as Weight) - // Standard Error: 1_000 - .saturating_add((115_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 6_000 - .saturating_add((101_000 as Weight).saturating_mul(s as Weight)) + fn not_unlocking_merge_schedules(_l: u32, _s: u32, ) -> Weight { + (120_434_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - (70_606_000 as Weight) - // Standard Error: 1_000 - .saturating_add((151_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 6_000 - .saturating_add((91_000 as Weight).saturating_mul(s as Weight)) + fn unlocking_merge_schedules(_l: u32, _s: u32, ) -> Weight { + (144_560_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } @@ -146,84 +124,64 @@ impl WeightInfo for SubstrateWeight { // For backwards compatibility and tests impl WeightInfo for () { fn vest_locked(l: u32, s: u32, ) -> Weight { - (58_117_000 as Weight) - // Standard Error: 3_000 - .saturating_add((136_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 12_000 - .saturating_add((177_000 as Weight).saturating_mul(s as Weight)) + (68_892_000 as Weight) + // Standard Error: 70_000 + .saturating_add((223_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 219_000 + .saturating_add((378_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn vest_unlocked(l: u32, s: u32, ) -> Weight { - (57_903_000 as Weight) - // Standard Error: 2_000 - .saturating_add((126_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 6_000 - .saturating_add((51_000 as Weight).saturating_mul(s as Weight)) + (72_557_000 as Weight) + // Standard Error: 33_000 + .saturating_add((140_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 105_000 + .saturating_add((157_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn vest_other_locked(l: u32, s: u32, ) -> Weight { - (57_736_000 as Weight) - // Standard Error: 1_000 - .saturating_add((140_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 4_000 - .saturating_add((159_000 as Weight).saturating_mul(s as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) - } - fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { - (57_278_000 as Weight) - // Standard Error: 1_000 - .saturating_add((131_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 5_000 - .saturating_add((68_000 as Weight).saturating_mul(s as Weight)) + (69_529_000 as Weight) + // Standard Error: 127_000 + .saturating_add((239_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 396_000 + .saturating_add((494_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn last_vested_transfer(l: u32, _s: u32, ) -> Weight { - (101_019_000 as Weight) - // Standard Error: 5_000 - .saturating_add((135_000 as Weight).saturating_mul(l as Weight)) + fn vest_other_unlocked(l: u32, _s: u32, ) -> Weight { + (78_096_000 as Weight) + // Standard Error: 36_000 + .saturating_add((50_000 as Weight).saturating_mul(l as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn first_vested_transfer(l: u32, ) -> Weight { - (118_733_000 as Weight) - // Standard Error: 5_000 - .saturating_add((138_000 as Weight).saturating_mul(l as Weight)) + fn vested_transfer(l: u32, s: u32, ) -> Weight { + (126_319_000 as Weight) + // Standard Error: 339_000 + .saturating_add((753_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 1_057_000 + .saturating_add((721_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn first_force_vested_transfer(l: u32, ) -> Weight { - (117_482_000 as Weight) - // Standard Error: 3_000 - .saturating_add((146_000 as Weight).saturating_mul(l as Weight)) - .saturating_add(RocksDbWeight::get().reads(4 as Weight)) - .saturating_add(RocksDbWeight::get().writes(4 as Weight)) - } - fn last_force_vested_transfer(l: u32, _s: u32, ) -> Weight { - (100_191_000 as Weight) - // Standard Error: 6_000 - .saturating_add((144_000 as Weight).saturating_mul(l as Weight)) + fn force_vested_transfer(l: u32, s: u32, ) -> Weight { + (106_182_000 as Weight) + // Standard Error: 216_000 + .saturating_add((571_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 674_000 + .saturating_add((1_538_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } - fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - (71_299_000 as Weight) - // Standard Error: 1_000 - .saturating_add((115_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 6_000 - .saturating_add((101_000 as Weight).saturating_mul(s as Weight)) + fn not_unlocking_merge_schedules(_l: u32, _s: u32, ) -> Weight { + (120_434_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - (70_606_000 as Weight) - // Standard Error: 1_000 - .saturating_add((151_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 6_000 - .saturating_add((91_000 as Weight).saturating_mul(s as Weight)) + fn unlocking_merge_schedules(_l: u32, _s: u32, ) -> Weight { + (144_560_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } From d9c548874af4a72f8763af5264222242d201afbb Mon Sep 17 00:00:00 2001 From: Parity Bot Date: Mon, 5 Jul 2021 19:00:30 +0000 Subject: [PATCH 061/144] cargo run --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_vesting --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/vesting/src/weights.rs --template=./.maintain/frame-weight-template.hbs --- frame/vesting/src/weights.rs | 160 +++++++++++++++++++---------------- 1 file changed, 86 insertions(+), 74 deletions(-) diff --git a/frame/vesting/src/weights.rs b/frame/vesting/src/weights.rs index df36f8b642260..7f4091b48fd7e 100644 --- a/frame/vesting/src/weights.rs +++ b/frame/vesting/src/weights.rs @@ -18,22 +18,22 @@ //! Autogenerated weights for pallet_vesting //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-07-05, STEPS: `[110, ]`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2021-07-05, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: // target/release/substrate // benchmark // --chain=dev -// --steps=110 -// --repeat=1 +// --steps=50 +// --repeat=20 // --pallet=pallet_vesting // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 // --output=./frame/vesting/src/weights.rs -// --template=.maintain/frame-weight-template.hbs +// --template=./.maintain/frame-weight-template.hbs #![allow(unused_parens)] @@ -58,64 +58,70 @@ pub trait WeightInfo { pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { fn vest_locked(l: u32, s: u32, ) -> Weight { - (68_892_000 as Weight) - // Standard Error: 70_000 - .saturating_add((223_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 219_000 - .saturating_add((378_000 as Weight).saturating_mul(s as Weight)) + (60_524_000 as Weight) + // Standard Error: 1_000 + .saturating_add((158_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 5_000 + .saturating_add((181_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn vest_unlocked(l: u32, s: u32, ) -> Weight { - (72_557_000 as Weight) - // Standard Error: 33_000 - .saturating_add((140_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 105_000 - .saturating_add((157_000 as Weight).saturating_mul(s as Weight)) + (60_205_000 as Weight) + // Standard Error: 1_000 + .saturating_add((136_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 5_000 + .saturating_add((102_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn vest_other_locked(l: u32, s: u32, ) -> Weight { - (69_529_000 as Weight) - // Standard Error: 127_000 - .saturating_add((239_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 396_000 - .saturating_add((494_000 as Weight).saturating_mul(s as Weight)) + (60_649_000 as Weight) + // Standard Error: 2_000 + .saturating_add((149_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 7_000 + .saturating_add((173_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn vest_other_unlocked(l: u32, _s: u32, ) -> Weight { - (78_096_000 as Weight) - // Standard Error: 36_000 - .saturating_add((50_000 as Weight).saturating_mul(l as Weight)) + fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { + (59_567_000 as Weight) + // Standard Error: 2_000 + .saturating_add((148_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 6_000 + .saturating_add((84_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn vested_transfer(l: u32, s: u32, ) -> Weight { - (126_319_000 as Weight) - // Standard Error: 339_000 - .saturating_add((753_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 1_057_000 - .saturating_add((721_000 as Weight).saturating_mul(s as Weight)) + fn vested_transfer(l: u32, _s: u32, ) -> Weight { + (105_528_000 as Weight) + // Standard Error: 6_000 + .saturating_add((149_000 as Weight).saturating_mul(l as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn force_vested_transfer(l: u32, s: u32, ) -> Weight { - (106_182_000 as Weight) - // Standard Error: 216_000 - .saturating_add((571_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 674_000 - .saturating_add((1_538_000 as Weight).saturating_mul(s as Weight)) + fn force_vested_transfer(l: u32, _s: u32, ) -> Weight { + (106_006_000 as Weight) + // Standard Error: 9_000 + .saturating_add((111_000 as Weight).saturating_mul(l as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } - fn not_unlocking_merge_schedules(_l: u32, _s: u32, ) -> Weight { - (120_434_000 as Weight) + fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { + (73_233_000 as Weight) + // Standard Error: 1_000 + .saturating_add((147_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 5_000 + .saturating_add((143_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn unlocking_merge_schedules(_l: u32, _s: u32, ) -> Weight { - (144_560_000 as Weight) + fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { + (73_772_000 as Weight) + // Standard Error: 2_000 + .saturating_add((145_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 6_000 + .saturating_add((119_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } @@ -124,64 +130,70 @@ impl WeightInfo for SubstrateWeight { // For backwards compatibility and tests impl WeightInfo for () { fn vest_locked(l: u32, s: u32, ) -> Weight { - (68_892_000 as Weight) - // Standard Error: 70_000 - .saturating_add((223_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 219_000 - .saturating_add((378_000 as Weight).saturating_mul(s as Weight)) + (60_524_000 as Weight) + // Standard Error: 1_000 + .saturating_add((158_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 5_000 + .saturating_add((181_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn vest_unlocked(l: u32, s: u32, ) -> Weight { - (72_557_000 as Weight) - // Standard Error: 33_000 - .saturating_add((140_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 105_000 - .saturating_add((157_000 as Weight).saturating_mul(s as Weight)) + (60_205_000 as Weight) + // Standard Error: 1_000 + .saturating_add((136_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 5_000 + .saturating_add((102_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn vest_other_locked(l: u32, s: u32, ) -> Weight { - (69_529_000 as Weight) - // Standard Error: 127_000 - .saturating_add((239_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 396_000 - .saturating_add((494_000 as Weight).saturating_mul(s as Weight)) + (60_649_000 as Weight) + // Standard Error: 2_000 + .saturating_add((149_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 7_000 + .saturating_add((173_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn vest_other_unlocked(l: u32, _s: u32, ) -> Weight { - (78_096_000 as Weight) - // Standard Error: 36_000 - .saturating_add((50_000 as Weight).saturating_mul(l as Weight)) + fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { + (59_567_000 as Weight) + // Standard Error: 2_000 + .saturating_add((148_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 6_000 + .saturating_add((84_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn vested_transfer(l: u32, s: u32, ) -> Weight { - (126_319_000 as Weight) - // Standard Error: 339_000 - .saturating_add((753_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 1_057_000 - .saturating_add((721_000 as Weight).saturating_mul(s as Weight)) + fn vested_transfer(l: u32, _s: u32, ) -> Weight { + (105_528_000 as Weight) + // Standard Error: 6_000 + .saturating_add((149_000 as Weight).saturating_mul(l as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn force_vested_transfer(l: u32, s: u32, ) -> Weight { - (106_182_000 as Weight) - // Standard Error: 216_000 - .saturating_add((571_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 674_000 - .saturating_add((1_538_000 as Weight).saturating_mul(s as Weight)) + fn force_vested_transfer(l: u32, _s: u32, ) -> Weight { + (106_006_000 as Weight) + // Standard Error: 9_000 + .saturating_add((111_000 as Weight).saturating_mul(l as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } - fn not_unlocking_merge_schedules(_l: u32, _s: u32, ) -> Weight { - (120_434_000 as Weight) + fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { + (73_233_000 as Weight) + // Standard Error: 1_000 + .saturating_add((147_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 5_000 + .saturating_add((143_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn unlocking_merge_schedules(_l: u32, _s: u32, ) -> Weight { - (144_560_000 as Weight) + fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { + (73_772_000 as Weight) + // Standard Error: 2_000 + .saturating_add((145_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 6_000 + .saturating_add((119_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } From bd00ccc2dc68d253980421ae9c3a341d3b180d71 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 5 Jul 2021 14:21:56 -0700 Subject: [PATCH 062/144] WIP override storage --- frame/vesting/src/mock.rs | 1 - frame/vesting/src/tests.rs | 72 +++++++++++++++++++++++++++++++++++--- 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/frame/vesting/src/mock.rs b/frame/vesting/src/mock.rs index a99f7ed7b59c3..0f45858b5b33b 100644 --- a/frame/vesting/src/mock.rs +++ b/frame/vesting/src/mock.rs @@ -24,7 +24,6 @@ use sp_runtime::{ use super::*; use crate as pallet_vesting; - type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index 0a05a9daff79b..a13ff6d0e0938 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -15,9 +15,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use frame_support::{assert_noop, assert_ok, assert_storage_noop}; -use frame_support::dispatch::EncodeLike; +use codec::Encode; +use frame_support::{assert_noop, assert_ok, assert_storage_noop, dispatch::EncodeLike, Blake2_128Concat, StorageHasher}; use frame_system::RawOrigin; +use sp_core::hashing::{blake2_128, twox_128}; use sp_runtime::traits::{BadOrigin, Identity}; use super::{Vesting as VestingStorage, *}; @@ -33,9 +34,69 @@ where u64: EncodeLike<::AccountId>, T: pallet::Config, { - // Its ok for this to fail because the user may already have no schedules. - let _result = Vesting::vest(Some(account).into()); - assert!(!>::contains_key(account)); + // Its ok for this to fail because the user may already have no schedules. + let _result = Vesting::vest(Some(account).into()); + assert!(!>::contains_key(account)); +} + +/// Place the given encoded `schedules` into storage, overriding any checks. +// fn override_vesting(account: u64, schedules: &[u8]) { +// let vesting_key = account.using_encoded(blake2_128concat); +// let storage_key = twox_128(b"Vesting") +// .iter() +// .chain(twox_128(b"Vesting").iter()) +// .chain(vesting_key[..].iter()) +// .cloned() +// .collect::>(); +// sp_io::storage::set(&storage_key[..], schedules); +// } + +fn blake2_128concat(x: &[u8]) -> Vec { + blake2_128(x).iter().chain(x.into_iter()).cloned().collect::>() +} + +fn generate_vesting_key(account: u64) -> Vec { + let module_prefix_hashed = twox_128(b"Vesting"); + let storage_prefix_hashed = twox_128(b"Vesting"); + // let key_hashed = account.using_encoded(blake2_128concat); + let key_hashed = account.using_encoded(Blake2_128Concat::hash); + + let mut final_key = Vec::with_capacity( + module_prefix_hashed.len() + storage_prefix_hashed.len() + key_hashed.len(), + ); + + final_key.extend_from_slice(&module_prefix_hashed[..]); + final_key.extend_from_slice(&storage_prefix_hashed[..]); + final_key.extend_from_slice(key_hashed.as_ref()); + + final_key +} + +#[test] +fn merge_schedules_fails_when_starting_with_too_many_schedules() { + let one_extra = ::MaxVestingSchedules::get() + 1; + + let sched = VestingInfo::new::(ED * 20, ED, 10u64); + + let too_many_schedules = (0 .. one_extra) + .map(|_| sched) + .collect::>(); + let storage_value = too_many_schedules.clone().encode(); + // let storage_value = Some(too_many_schedules.clone()).encode(); + + let storage_key = generate_vesting_key(4); + + let mut ext = ExtBuilder::default().existential_deposit(ED).build(); + ext.insert(storage_key.clone(), storage_value.clone()); // seems to be equivalent to storage::set + + ext.execute_with(|| { + // sp_io::storage::set(&storage_key[..], &storage_value[..]); + // This works + assert_eq!(sp_io::storage::get(&storage_key[..]).unwrap(), &storage_value[..]); + + // This does not work, says there is nothing in storage + assert_eq!(Vesting::vesting(4).unwrap(), too_many_schedules); + }); } #[test] @@ -375,6 +436,7 @@ fn vested_transfer_correctly_fails() { assert_eq!(user4_free_balance, Balances::free_balance(&4)); // Account 4 has no schedules. vest_and_assert_no_vesting::(4); + }); } From bdeda40f1c4882e55333c6d4e993d7f66a8c7483 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 5 Jul 2021 14:24:52 -0700 Subject: [PATCH 063/144] Set storage not working example --- frame/vesting/src/tests.rs | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index a13ff6d0e0938..d6bec04c5eb41 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -16,9 +16,8 @@ // limitations under the License. use codec::Encode; -use frame_support::{assert_noop, assert_ok, assert_storage_noop, dispatch::EncodeLike, Blake2_128Concat, StorageHasher}; +use frame_support::{assert_noop, assert_ok, assert_storage_noop, dispatch::EncodeLike, Blake2_128Concat,Twox128, StorageHasher}; use frame_system::RawOrigin; -use sp_core::hashing::{blake2_128, twox_128}; use sp_runtime::traits::{BadOrigin, Identity}; use super::{Vesting as VestingStorage, *}; @@ -39,26 +38,9 @@ where assert!(!>::contains_key(account)); } -/// Place the given encoded `schedules` into storage, overriding any checks. -// fn override_vesting(account: u64, schedules: &[u8]) { -// let vesting_key = account.using_encoded(blake2_128concat); -// let storage_key = twox_128(b"Vesting") -// .iter() -// .chain(twox_128(b"Vesting").iter()) -// .chain(vesting_key[..].iter()) -// .cloned() -// .collect::>(); -// sp_io::storage::set(&storage_key[..], schedules); -// } - -fn blake2_128concat(x: &[u8]) -> Vec { - blake2_128(x).iter().chain(x.into_iter()).cloned().collect::>() -} - fn generate_vesting_key(account: u64) -> Vec { - let module_prefix_hashed = twox_128(b"Vesting"); - let storage_prefix_hashed = twox_128(b"Vesting"); - // let key_hashed = account.using_encoded(blake2_128concat); + let module_prefix_hashed = Twox128::hash(b"Vesting"); + let storage_prefix_hashed = Twox128::hash(b"Vesting"); let key_hashed = account.using_encoded(Blake2_128Concat::hash); let mut final_key = Vec::with_capacity( From 9c699e4bd1c9a7f6179d9b9a2969a4c28c5d0f79 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 5 Jul 2021 16:30:15 -0700 Subject: [PATCH 064/144] Remove unused tests --- frame/vesting/src/tests.rs | 46 +------------------------------------- 1 file changed, 1 insertion(+), 45 deletions(-) diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index d6bec04c5eb41..4f4df98a6002f 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -15,8 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use codec::Encode; -use frame_support::{assert_noop, assert_ok, assert_storage_noop, dispatch::EncodeLike, Blake2_128Concat,Twox128, StorageHasher}; +use frame_support::{assert_noop, assert_ok, assert_storage_noop, dispatch::EncodeLike}; use frame_system::RawOrigin; use sp_runtime::traits::{BadOrigin, Identity}; @@ -38,49 +37,6 @@ where assert!(!>::contains_key(account)); } -fn generate_vesting_key(account: u64) -> Vec { - let module_prefix_hashed = Twox128::hash(b"Vesting"); - let storage_prefix_hashed = Twox128::hash(b"Vesting"); - let key_hashed = account.using_encoded(Blake2_128Concat::hash); - - let mut final_key = Vec::with_capacity( - module_prefix_hashed.len() + storage_prefix_hashed.len() + key_hashed.len(), - ); - - final_key.extend_from_slice(&module_prefix_hashed[..]); - final_key.extend_from_slice(&storage_prefix_hashed[..]); - final_key.extend_from_slice(key_hashed.as_ref()); - - final_key -} - -#[test] -fn merge_schedules_fails_when_starting_with_too_many_schedules() { - let one_extra = ::MaxVestingSchedules::get() + 1; - - let sched = VestingInfo::new::(ED * 20, ED, 10u64); - - let too_many_schedules = (0 .. one_extra) - .map(|_| sched) - .collect::>(); - let storage_value = too_many_schedules.clone().encode(); - // let storage_value = Some(too_many_schedules.clone()).encode(); - - let storage_key = generate_vesting_key(4); - - let mut ext = ExtBuilder::default().existential_deposit(ED).build(); - ext.insert(storage_key.clone(), storage_value.clone()); // seems to be equivalent to storage::set - - ext.execute_with(|| { - // sp_io::storage::set(&storage_key[..], &storage_value[..]); - // This works - assert_eq!(sp_io::storage::get(&storage_key[..]).unwrap(), &storage_value[..]); - - // This does not work, says there is nothing in storage - assert_eq!(Vesting::vesting(4).unwrap(), too_many_schedules); - }); -} - #[test] fn check_vesting_status() { ExtBuilder::default() From f5aee3b63c66361c5b618c5e785129f439327225 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 5 Jul 2021 17:30:22 -0700 Subject: [PATCH 065/144] VestingInfo: make public, derive MaxEndcodedLen --- frame/vesting/src/lib.rs | 3 +-- frame/vesting/src/vesting_info.rs | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 75fb422f21657..79218be4ca3aa 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -50,7 +50,7 @@ mod migrations; mod mock; #[cfg(test)] mod tests; -mod vesting_info; +pub mod vesting_info; pub mod weights; @@ -341,7 +341,6 @@ pub mod pallet { /// # #[pallet::weight( T::WeightInfo::vested_transfer(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) - )] pub fn vested_transfer( origin: OriginFor, diff --git a/frame/vesting/src/vesting_info.rs b/frame/vesting/src/vesting_info.rs index 73ce320ae46ce..f3a9e5751904f 100644 --- a/frame/vesting/src/vesting_info.rs +++ b/frame/vesting/src/vesting_info.rs @@ -18,9 +18,10 @@ //! Module to enforce private fields on `VestingInfo`. use super::*; +use codec::MaxEncodedLen; /// Struct to encode the vesting schedule of an individual account. -#[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug)] +#[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen)] pub struct VestingInfo { /// Locked amount at genesis. locked: Balance, From e67c6c59e530105e4db5603902f5bdbafe656960 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 5 Jul 2021 17:41:21 -0700 Subject: [PATCH 066/144] Rename ending_block to ending_block_as_balance --- frame/vesting/src/lib.rs | 4 ++-- frame/vesting/src/tests.rs | 34 +++++++++++++++---------------- frame/vesting/src/vesting_info.rs | 4 ++-- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 79218be4ca3aa..ddf6f3139c699 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -469,8 +469,8 @@ impl Pallet { schedule1: VestingInfo, T::BlockNumber>, schedule2: VestingInfo, T::BlockNumber>, ) -> Result, T::BlockNumber>>, DispatchError> { - let schedule1_ending_block = schedule1.ending_block::()?; - let schedule2_ending_block = schedule2.ending_block::()?; + let schedule1_ending_block = schedule1.ending_block_as_balance::()?; + let schedule2_ending_block = schedule2.ending_block_as_balance::()?; let now_as_balance = T::BlockNumberToBalance::convert(now); // Check if one or both schedules have ended. diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index 4f4df98a6002f..9d82132095215 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -640,7 +640,7 @@ fn merge_ongoing_schedules() { .locked_at::(cur_block) .saturating_add(sched0.locked_at::(cur_block)); // End block of the new schedule is the greater of either merged schedule - let sched2_end = sched1.ending_block::().unwrap().max(sched0.ending_block::().unwrap()); + let sched2_end = sched1.ending_block_as_balance::().unwrap().max(sched0.ending_block_as_balance::().unwrap()); let sched2_duration = sched2_end - cur_block; // Based off the new schedules total locked and its duration, we can calculate the // amount to unlock per block. @@ -704,7 +704,7 @@ fn merging_shifts_other_schedules_index() { let sched3_locked = sched2.locked_at::(cur_block) + sched0.locked_at::(cur_block); // and will end at the max possible block. - let sched3_end = sched2.ending_block::().unwrap().max(sched0.ending_block::().unwrap()); + let sched3_end = sched2.ending_block_as_balance::().unwrap().max(sched0.ending_block_as_balance::().unwrap()); let sched3_duration = sched3_end - sched3_start; let sched3_per_block = sched3_locked / sched3_duration; let sched3 = VestingInfo::new::(sched3_locked, sched3_per_block, sched3_start); @@ -730,7 +730,7 @@ fn merge_ongoing_and_yet_to_be_started_schedules() { assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); // Fast forward to half way through the life of sched_1 - let mut cur_block = (sched0.starting_block() + sched0.ending_block::().unwrap()) / 2; + let mut cur_block = (sched0.starting_block() + sched0.ending_block_as_balance::().unwrap()) / 2; assert_eq!(cur_block, 20); System::set_block_number(cur_block); @@ -771,8 +771,8 @@ fn merge_ongoing_and_yet_to_be_started_schedules() { let sched2_locked = sched0.locked_at::(cur_block) + sched1.locked_at::(cur_block); // and will end at the max possible block. - let sched2_end = sched0.ending_block::().unwrap() - .max(sched1.ending_block::().unwrap()); + let sched2_end = sched0.ending_block_as_balance::().unwrap() + .max(sched1.ending_block_as_balance::().unwrap()); let sched2_duration = sched2_end - sched2_start; let sched2_per_block = sched2_locked / sched2_duration; @@ -814,7 +814,7 @@ fn merge_finished_and_ongoing_schedules() { assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1, sched2]); // Fast forward to sched0's end block. - let cur_block = sched0.ending_block::().unwrap(); + let cur_block = sched0.ending_block_as_balance::().unwrap(); System::set_block_number(cur_block); assert_eq!(System::block_number(), 30); @@ -866,7 +866,7 @@ fn merge_finishing_schedules_does_not_create_a_new_one() { assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); let all_scheds_end = - sched0.ending_block::().unwrap().max(sched1.ending_block::().unwrap()); + sched0.ending_block_as_balance::().unwrap().max(sched1.ending_block_as_balance::().unwrap()); assert_eq!(all_scheds_end, 40); System::set_block_number(all_scheds_end); @@ -1038,7 +1038,7 @@ fn merge_vesting_errors_with_per_block_0() { 1, ); assert_eq!( - sched0.ending_block::(), + sched0.ending_block_as_balance::(), Err(Error::::InfiniteSchedule.into()) ); let sched1 = VestingInfo::new::( @@ -1046,7 +1046,7 @@ fn merge_vesting_errors_with_per_block_0() { 1, // Vesting over 512 blocks. 10, ); - assert_eq!(sched1.ending_block::(), Ok(512u64 + 10)); + assert_eq!(sched1.ending_block_as_balance::(), Ok(512u64 + 10)); assert_eq!( Vesting::merge_vesting_info(5, sched0, sched1), @@ -1095,37 +1095,37 @@ fn vesting_info_validate_and_correct_works() { } #[test] -fn vesting_info_ending_block_works() { +fn vesting_info_ending_block_as_balance_works() { // Treats `per_block` 0 as an error let per_block_0 = VestingInfo::new::(256u32, 0u32, 10u32); assert_eq!( - per_block_0.ending_block::(), + per_block_0.ending_block_as_balance::(), Err(Error::::InfiniteSchedule.into()) ); // let per_block_1 = VestingInfo::new::(256u32, 1u32, 10u32); - // assert_eq!(per_block_0.ending_block::().unwrap(), per_block_1.ending_block::().unwrap()); + // assert_eq!(per_block_0.ending_block_as_balance::().unwrap(), per_block_1.ending_block_as_balance::().unwrap()); // `per_block >= locked` always results in a schedule ending the block after it starts let per_block_gt_locked = VestingInfo::new::(256u32, 256 * 2u32, 10u32); assert_eq!( - per_block_gt_locked.ending_block::().unwrap(), + per_block_gt_locked.ending_block_as_balance::().unwrap(), 1 + per_block_gt_locked.starting_block() ); let per_block_eq_locked = VestingInfo::new::(256u32, 256u32, 10u32); assert_eq!( - per_block_gt_locked.ending_block::().unwrap(), - per_block_eq_locked.ending_block::().unwrap() + per_block_gt_locked.ending_block_as_balance::().unwrap(), + per_block_eq_locked.ending_block_as_balance::().unwrap() ); // Correctly calcs end if `locked % per_block != 0`. (We need a block to unlock the remainder). let imperfect_per_block = VestingInfo::new::(256u32, 250u32, 10u32); assert_eq!( - imperfect_per_block.ending_block::().unwrap(), + imperfect_per_block.ending_block_as_balance::().unwrap(), imperfect_per_block.starting_block() + 2u32, ); assert_eq!( imperfect_per_block - .locked_at::(imperfect_per_block.ending_block::().unwrap()), + .locked_at::(imperfect_per_block.ending_block_as_balance::().unwrap()), 0 ); } diff --git a/frame/vesting/src/vesting_info.rs b/frame/vesting/src/vesting_info.rs index f3a9e5751904f..0770640b72ed5 100644 --- a/frame/vesting/src/vesting_info.rs +++ b/frame/vesting/src/vesting_info.rs @@ -98,8 +98,8 @@ where .unwrap_or(Zero::zero()) } - /// Block number at which the schedule ends. - pub fn ending_block, T: Config>( + /// Block number at which the schedule ends (as type `Balance`). + pub fn ending_block_as_balance, T: Config>( &self, ) -> Result { let starting_block = BlockNumberToBalance::convert(self.starting_block); From defb875a6cc646d1e061e932e6743cce62238712 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 5 Jul 2021 18:34:43 -0700 Subject: [PATCH 067/144] Superificial improvements --- bin/node/runtime/src/lib.rs | 13 +++---- .../src/traits/tokens/currency/lockable.rs | 3 +- frame/vesting/src/benchmarking.rs | 25 +++++++------ frame/vesting/src/lib.rs | 35 ++++++++++--------- frame/vesting/src/migrations.rs | 3 ++ frame/vesting/src/vesting_info.rs | 4 ++- 6 files changed, 44 insertions(+), 39 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 7f5a0fe2952f2..d0e892159ae99 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1023,25 +1023,26 @@ impl pallet_society::Config for Runtime { parameter_types! { pub const MinVestedTransfer: Balance = 100 * DOLLARS; - pub const MaxVestingSchedules: u32 = 20; + pub const MaxVestingSchedules: u32 = 28; } impl pallet_vesting::Config for Runtime { - type Event = Event; - type Currency = Balances; type BlockNumberToBalance = ConvertInto; + type Currency = Balances; + type Event = Event; + type MaxVestingSchedules = MaxVestingSchedules; type MinVestedTransfer = MinVestedTransfer; type WeightInfo = pallet_vesting::weights::SubstrateWeight; - type MaxVestingSchedules = MaxVestingSchedules; } impl pallet_mmr::Config for Runtime { - const INDEXING_PREFIX: &'static [u8] = b"mmr"; - type Hashing = ::Hashing; type Hash = ::Hash; + type Hashing = ::Hashing; type LeafData = frame_system::Pallet; type OnNewRoot = (); type WeightInfo = (); + + const INDEXING_PREFIX: &'static [u8] = b"mmr"; } parameter_types! { diff --git a/frame/support/src/traits/tokens/currency/lockable.rs b/frame/support/src/traits/tokens/currency/lockable.rs index bacfaea257e35..f8696a19a41ca 100644 --- a/frame/support/src/traits/tokens/currency/lockable.rs +++ b/frame/support/src/traits/tokens/currency/lockable.rs @@ -97,8 +97,7 @@ pub trait VestingSchedule { starting_block: Self::Moment, ) -> DispatchResult; - /// Checks if `add_vesting_schedule` would work against `who` and the given - /// schedule params. + /// Checks if `add_vesting_schedule` would work against `who`. fn can_add_vesting_schedule( who: &AccountId, locked: >::Balance, diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index 8f280acb99bf3..925cad3a6e09c 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -29,15 +29,14 @@ use frame_support::assert_ok; use crate::Pallet as Vesting; const SEED: u32 = 0; -const ED: u32 = 256; -type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; +type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; fn add_locks(who: &T::AccountId, n: u8) { for id in 0 .. n { let lock_id = [id; 8]; - let locked = ED; - + let locked = 256u32; let reasons = WithdrawReasons::TRANSFER | WithdrawReasons::RESERVE; T::Currency::set_lock(lock_id, who, locked.into(), reasons); } @@ -180,7 +179,7 @@ benchmarks! { let caller: T::AccountId = whitelisted_caller(); }: vest_other(RawOrigin::Signed(caller.clone()), other_lookup) verify { - // Vesting schedule is removed! + // Vesting schedule is removed. assert_eq!( Vesting::::vesting_balance(&other), None, @@ -199,7 +198,7 @@ benchmarks! { let target_lookup: ::Source = T::Lookup::unlookup(target.clone()); // Give target existing locks add_locks::(&target, l as u8); - // Add one less than max vesting schedules + // Add one less than max vesting schedules. let mut expected_balance = add_vesting_schedules::(target_lookup.clone(), s)?; let transfer_amount = T::MinVestedTransfer::get(); @@ -269,15 +268,15 @@ benchmarks! { let caller: T::AccountId = account("caller", 0, SEED); let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); - // Give target existing locks + // Give target existing locks. add_locks::(&caller, l as u8); - // Add max vesting schedules + // Add max vesting schedules. let expected_balance = add_vesting_schedules::(caller_lookup.clone(), s)?; - // Values of the schedules added by `add_vesting_schedules`. + // Track the value of the schedules added by `add_vesting_schedules`. let transfer_amount = T::MinVestedTransfer::get(); let per_block_duration_20 = transfer_amount.checked_div(&20u32.into()).unwrap(); - // Schedules are not vesting at block 0 + // Schedules are not vesting at block 0. assert_eq!(System::::block_number(), T::BlockNumber::zero()); assert_eq!( Vesting::::vesting_balance(&caller), @@ -319,9 +318,9 @@ benchmarks! { let caller: T::AccountId = account("caller", 0, SEED); let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); - // Give target existing locks + // Give target other locks. add_locks::(&caller, l as u8); - // Add max vesting schedules + // Add max vesting schedules. let mut expected_balance = add_vesting_schedules::(caller_lookup.clone(), s)?; // Values of the schedules added by `add_vesting_schedules`. let transfer_amount = T::MinVestedTransfer::get(); @@ -329,7 +328,7 @@ benchmarks! { // Go to half way through all the schedules duration. (They all start at 1, and have a duration of 20). System::::set_block_number(11u32.into()); - // We expect half the original locked balance + // We expect half the original locked balance. expected_balance = expected_balance / 2u32.into(); assert_eq!( Vesting::::vesting_balance(&caller), diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index ddf6f3139c699..f7fd01a6c3a25 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -212,6 +212,7 @@ pub mod pallet { fn build(&self) { use sp_runtime::traits::Saturating; + // Genesis uses the latest storage version. StorageVersion::::put(Releases::V1); // Generate initial vesting configuration @@ -260,14 +261,14 @@ pub mod pallet { pub enum Error { /// The account given is not vesting. NotVesting, - /// The account already has `MaxVestingSchedules` number of schedules and thus + /// The account already has `MaxVestingSchedules` count of schedules and thus /// cannot add another one. Consider merging existing schedules in order to add another. AtMaxVestingSchedules, /// Amount being transferred is too low to create a vesting schedule. AmountLow, - /// At least one of the indexes is out of bounds of the vesting schedules. + /// An index was out of bounds of the vesting schedules. ScheduleIndexOutOfBounds, - /// Failed to create a new schedule because some parameters where invalid. e.g. `locked` was 0. + /// Failed to create a new schedule because some parameter was invalid. e.g. `locked` was 0. InvalidScheduleParams, /// A schedule contained a `per_block` of 0 or `locked / per_block > BlockNumber::max_value()`, /// thus rendering it unable to ever fully unlock funds. @@ -369,7 +370,7 @@ pub mod pallet { /// - Writes: Vesting Storage, Balances Locks, Target Account, Source Account /// # #[pallet::weight( - T::WeightInfo::force_vested_transfer(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) + T::WeightInfo::force_vested_transfer(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) )] pub fn force_vested_transfer( origin: OriginFor, @@ -381,7 +382,7 @@ pub mod pallet { Self::do_vested_transfer(source, target, schedule) } - /// Merge two vesting schedules together, creating a new vesting schedule that unlocks over + /// Merge two vesting schedules together, creating a new vesting schedule that unlocks over the /// highest possible start and end blocks. If both schedules have already started the current /// block will be used as the schedule start; with the caveat that if one schedule is finished /// by the current block, the other will be treated as the new merged schedule, unmodified. @@ -427,20 +428,20 @@ pub mod pallet { // The length of `schedules` decreases by 2 here since we filter out 2 schedules. // Thus we know below that we can insert the new merged schedule without error (assuming - // the its initial state was valid). + // initial state was valid). let (mut schedules, mut locked_now) = Self::report_schedule_updates(schedules.to_vec(), merge_action); let now = >::block_number(); if let Some(new_schedule) = Self::merge_vesting_info(now, schedule1, schedule2)? { - // Merging created a new schedule; so now we need to add it to the - // accounts vesting schedule collection. + // Merging created a new schedule so we: + // 1) need to add it to the accounts vesting schedule collection, schedules.push(new_schedule); - // (We use `locked_at` in case this is a schedule that started in the past.) + // (we use `locked_at` in case this is a schedule that started in the past) let new_schedule_locked = new_schedule.locked_at::(now); - // update the locked amount to reflect the schedule we just added, + // 2) update the locked amount to reflect the schedule we just added, locked_now = locked_now.saturating_add(new_schedule_locked); - // and deposit an event to show the new, new, merged schedule. + // and 3) deposit an event. Self::deposit_event(Event::::MergedScheduleAdded( new_schedule.locked(), new_schedule.per_block(), @@ -448,7 +449,6 @@ pub mod pallet { )); } // In the None case there was no new schedule to account for. - debug_assert!(schedules.len() <= T::MaxVestingSchedules::get() as usize); debug_assert!( locked_now > Zero::zero() && schedules.len() > 0 || locked_now == Zero::zero() && schedules.len() == 0 @@ -508,17 +508,18 @@ impl Pallet { One::one() } else { match locked.checked_div(&duration) { - // The logic of `ending_block` guarantees that each schedule ends at least a block - // after it starts and since we take the max starting and ending_block we should never - // get here. + // The logic of `ending_block_as_balance` guarantees that each schedule ends at + // least a block after it starts and since we take the max starting and ending_block + // we should never get here. None => locked, Some(per_block) => per_block, } }; - // At this point inputs have been validated, so this should always be `Some`. + // While `per_block` should never be 0, it is possible that the created schedule would + // end after the highest possible block, which is a case where `validate` fails but we + // are ok with. let schedule = VestingInfo::new::(locked, per_block, starting_block); - debug_assert!(schedule.validate::().is_ok()); Ok(Some(schedule)) } diff --git a/frame/vesting/src/migrations.rs b/frame/vesting/src/migrations.rs index 714ff5ee76602..8c255471ba275 100644 --- a/frame/vesting/src/migrations.rs +++ b/frame/vesting/src/migrations.rs @@ -67,6 +67,9 @@ pub(crate) mod v1 { for s in schedules { // Check for infinite schedules. assert!(s.per_block() > Zero::zero(), "A schedule with per_block of 0 exists"); + // It is ok if this does not pass, but ideally pre-existing schedules would pass + // this validation logic so we can be more confident about edge cases. + debug_assert!(s.validate(), "A schedule does not pass new validation logic"); } } diff --git a/frame/vesting/src/vesting_info.rs b/frame/vesting/src/vesting_info.rs index 0770640b72ed5..5620113e1c814 100644 --- a/frame/vesting/src/vesting_info.rs +++ b/frame/vesting/src/vesting_info.rs @@ -55,7 +55,9 @@ where let max_block = BlockNumberToBalance::convert(BlockNumber::max_value()); match self.locked.checked_div(&self.per_block) { None => return Err(Error::::InfiniteSchedule), // `per_block` is 0 - Some(duration) => ensure!(duration < max_block, Error::::InfiniteSchedule), + Some(duration) => { + ensure!(duration + self.starting_block() < max_block, Error::::InfiniteSchedule) + } }; Ok(()) From 03e43990bccfb7d87c4436152a01f37e0c64a66a Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 5 Jul 2021 18:45:05 -0700 Subject: [PATCH 068/144] Check for end block infinite, not just duration --- frame/vesting/src/lib.rs | 10 +++++++--- frame/vesting/src/tests.rs | 14 ++++++++------ frame/vesting/src/vesting_info.rs | 4 +++- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index f7fd01a6c3a25..510a543505ca0 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -540,9 +540,13 @@ impl Pallet { let target = T::Lookup::lookup(target)?; let source = T::Lookup::lookup(source)?; - // Check we can add to this account prior to any storage writes. The schedule - // params are ignored so we just use 0s. - Self::can_add_vesting_schedule(&target, Zero::zero(), Zero::zero(), Zero::zero())?; + // Check we can add to this account prior to any storage writes. + Self::can_add_vesting_schedule( + &target, + schedule.locked(), + schedule.per_block(), + schedule.starting_block(), + )?; T::Currency::transfer( &source, diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index 9d82132095215..bb2bb7725576f 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -355,10 +355,11 @@ fn vested_transfer_correctly_fails() { Error::::InfiniteSchedule, ); - // `locked / per_block > BlockNumber::max_value()` - let schedule_duration_gt_max_blocknumber = VestingInfo::new::(u64::MAX, 1, 10); + // `locked / per_block + starting_block > BlockNumber::max_value()` + let start = 10u64; + let schedule_end_gt_max_blocknumber = VestingInfo::new::(u64::MAX - start + 1, 1, start); assert_noop!( - Vesting::vested_transfer(Some(3).into(), 4, schedule_duration_gt_max_blocknumber), + Vesting::vested_transfer(Some(3).into(), 4, schedule_end_gt_max_blocknumber), Error::::InfiniteSchedule, ); @@ -510,10 +511,11 @@ fn force_vested_transfer_correctly_fails() { Error::::InfiniteSchedule, ); - // `locked / per_block > BlockNumber::max_value()` - let schedule_duration_gt_max_blocknumber = VestingInfo::new::(u64::MAX, 1, 10); + // `locked / per_block + starting_block > BlockNumber::max_value()` + let start = 10u64; + let schedule_end_gt_max_blocknumber = VestingInfo::new::(u64::MAX - start + 1, 1, start); assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_duration_gt_max_blocknumber), + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_end_gt_max_blocknumber), Error::::InfiniteSchedule, ); diff --git a/frame/vesting/src/vesting_info.rs b/frame/vesting/src/vesting_info.rs index 5620113e1c814..3a4f2df4a659b 100644 --- a/frame/vesting/src/vesting_info.rs +++ b/frame/vesting/src/vesting_info.rs @@ -53,10 +53,12 @@ where ensure!(!self.locked.is_zero(), Error::::InvalidScheduleParams); let max_block = BlockNumberToBalance::convert(BlockNumber::max_value()); + let starting_block = BlockNumberToBalance::convert(self.starting_block()); match self.locked.checked_div(&self.per_block) { None => return Err(Error::::InfiniteSchedule), // `per_block` is 0 Some(duration) => { - ensure!(duration + self.starting_block() < max_block, Error::::InfiniteSchedule) + let end = duration.saturating_add(starting_block); + ensure!(end < max_block, Error::::InfiniteSchedule) } }; From 848a4ad81dcf35aa4c8b74f380961029d490ef51 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 5 Jul 2021 18:55:20 -0700 Subject: [PATCH 069/144] More superficial update --- frame/vesting/src/lib.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 510a543505ca0..40abe6a9145ee 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -655,8 +655,8 @@ impl VestingSchedule for Pallet where BalanceOf: MaybeSerializeDeserialize + Debug, { - type Currency = T::Currency; type Moment = T::BlockNumber; + type Currency = T::Currency; /// Get the amount that is currently being vested and cannot be transferred out of this account. fn vesting_balance(who: &T::AccountId) -> Option> { @@ -681,16 +681,14 @@ where /// `vest_other`. /// /// Is a no-op if the amount to be vested is zero. - /// NOTE: it is assumed the function user has done necessary `VestingInfo` param validation. + /// NOTE: does not validate schedule params. fn add_vesting_schedule( who: &T::AccountId, locked: BalanceOf, per_block: BalanceOf, starting_block: T::BlockNumber, ) -> DispatchResult { - if locked.is_zero() { - return Ok(()); - } + if locked.is_zero() { return Ok(()); } let vesting_schedule = VestingInfo::new::(locked, per_block, starting_block); let mut schedules = Self::vesting(who).unwrap_or_default(); @@ -713,7 +711,7 @@ where } // Ensure we can call `add_vesting_schedule` without error. This should always - // be updated in lockstep with `add_vesting_schedule`. + // be called prior to `add_vesting_schedule`. // // NOTE: expects schedule param validation has been done due to different // scenarios having varying requirements. From b202ff82ab50c4cb524684d239505ce92d80edbe Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 5 Jul 2021 19:38:10 -0700 Subject: [PATCH 070/144] Update tests --- frame/vesting/src/lib.rs | 11 +-- frame/vesting/src/migrations.rs | 12 ++-- frame/vesting/src/mock.rs | 5 +- frame/vesting/src/tests.rs | 122 +++++++++++++++++--------------- 4 files changed, 77 insertions(+), 73 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 40abe6a9145ee..8a7cc1ce0dbf0 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -655,8 +655,8 @@ impl VestingSchedule for Pallet where BalanceOf: MaybeSerializeDeserialize + Debug, { - type Moment = T::BlockNumber; type Currency = T::Currency; + type Moment = T::BlockNumber; /// Get the amount that is currently being vested and cannot be transferred out of this account. fn vesting_balance(who: &T::AccountId) -> Option> { @@ -730,10 +730,10 @@ where Ok(()) } - /// Remove a vesting schedule for a given account. Will error if `schedule_index` is `None`. + /// Remove a vesting schedule for a given account. fn remove_vesting_schedule(who: &T::AccountId, schedule_index: u32) -> DispatchResult { - let remove_action = VestingAction::Remove(schedule_index as usize); let schedules = Self::vesting(who).ok_or(Error::::NotVesting)?; + let remove_action = VestingAction::Remove(schedule_index as usize); let (schedules, locked_now) = Self::report_schedule_updates(schedules.to_vec(), remove_action); @@ -746,9 +746,4 @@ where Self::write_lock(who, locked_now); Ok(()) } - - // TODO: migration tests - // - migration version changes as expected - // - same checks as pre and post migrate - // TODO: test that genesis build has the correct migrations version } diff --git a/frame/vesting/src/migrations.rs b/frame/vesting/src/migrations.rs index 8c255471ba275..097e66e8a2161 100644 --- a/frame/vesting/src/migrations.rs +++ b/frame/vesting/src/migrations.rs @@ -29,11 +29,12 @@ pub(crate) mod v1 { log::debug!( target: LOG_TARGET, - "Vesting storage version v1 **PRE** migration checks succesful!" + "Vesting storage version v1 PRE migration checks succesful!" ); Ok(()) } + /// Migrate from single schedule to multi schedule storage pub(crate) fn migrate() -> Weight { let mut reads_writes = 0; @@ -56,7 +57,7 @@ pub(crate) mod v1 { pub(crate) fn post_migrate() -> Result<(), &'static str> { assert_eq!(StorageVersion::::get(), Releases::V1); - for (key, schedules) in Vesting::::iter() { + for (_key, schedules) in Vesting::::iter() { // Assert the new bound vec respects size. assert!(schedules.len() > 0, "A bounded vec with no items was created."); assert!( @@ -69,13 +70,16 @@ pub(crate) mod v1 { assert!(s.per_block() > Zero::zero(), "A schedule with per_block of 0 exists"); // It is ok if this does not pass, but ideally pre-existing schedules would pass // this validation logic so we can be more confident about edge cases. - debug_assert!(s.validate(), "A schedule does not pass new validation logic"); + debug_assert!( + s.validate::().is_ok(), + "A schedule does not pass new validation logic" + ); } } log::debug!( target: LOG_TARGET, - "Vesting storage version v1 **POST** migration checks succesful!" + "Vesting storage version v1 POST migration checks succesful!" ); Ok(()) } diff --git a/frame/vesting/src/mock.rs b/frame/vesting/src/mock.rs index 0f45858b5b33b..52af34eee8864 100644 --- a/frame/vesting/src/mock.rs +++ b/frame/vesting/src/mock.rs @@ -24,6 +24,7 @@ use sp_runtime::{ use super::*; use crate as pallet_vesting; + type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -84,8 +85,8 @@ impl pallet_balances::Config for Test { type WeightInfo = (); } parameter_types! { - // Ideally all tests should use a value less than `MinVestedTransfer` when configuring the - // existential deposit. + // Ideally all tests should use a value <= `MinVestedTransfer` when configuring the + // existential deposit. This is ensured the by the integrity test. pub const MinVestedTransfer: u64 = 256 * 2; pub static ExistentialDeposit: u64 = 0; pub const MaxVestingSchedules: u32 = 3; diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index bb2bb7725576f..21827dd149034 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -111,12 +111,12 @@ fn check_vesting_status_for_multi_schedule_account() { // Account 2 already has a vesting schedule. assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); - // Account 2's free balance is from sched0 + // Account 2's free balance is from sched0. let free_balance = Balances::free_balance(&2); assert_eq!(free_balance, ED * (20)); assert_eq!(Vesting::vesting_balance(&2), Some(free_balance)); - // Add a 2nd schedule that is already unlocking by block #1 + // Add a 2nd schedule that is already unlocking by block #1. let sched1 = VestingInfo::new::( ED * 10, ED, // Vesting over 10 blocks @@ -131,7 +131,7 @@ fn check_vesting_status_for_multi_schedule_account() { // sched1 has free funds at block #1, but nothing else. assert_eq!(Vesting::vesting_balance(&2), Some(free_balance - sched1.per_block())); - // Add a 3rd schedule + // Add a 3rd schedule. let sched2 = VestingInfo::new::( ED * 30, ED, // Vesting over 30 blocks @@ -167,7 +167,6 @@ fn check_vesting_status_for_multi_schedule_account() { // At block #35 sched2 fully unlocks and thus all schedules funds are unlocked. System::set_block_number(35); - assert_eq!(System::block_number(), 35); assert_eq!(Vesting::vesting_balance(&2), Some(0)); // Since we have not called any extrinsics that would unlock funds the schedules // are still in storage, @@ -314,7 +313,7 @@ fn vested_transfer_works() { System::set_block_number(30); assert_eq!(System::block_number(), 30); - // Account 4 has fully vested + // Account 4 has fully vested, assert_eq!(Vesting::vesting_balance(&4), Some(0)); // and after unlocking its schedules are removed from storage. vest_and_assert_no_vesting::(4); @@ -349,7 +348,7 @@ fn vested_transfer_correctly_fails() { ); // `per_block` is 0, which would result in a schedule with infinite duration. - let schedule_per_block_0 = VestingInfo::new::(::MinVestedTransfer::get() + 1, 0, 10); + let schedule_per_block_0 = VestingInfo::new::(::MinVestedTransfer::get(), 0, 10); assert_noop!( Vesting::vested_transfer(Some(13).into(), 4, schedule_per_block_0), Error::::InfiniteSchedule, @@ -363,7 +362,7 @@ fn vested_transfer_correctly_fails() { Error::::InfiniteSchedule, ); - // `locked` is less than `MinVestedTransfer`. + // `locked` is 0 let schedule_locked_0 = VestingInfo::new::(0, 1, 10); assert_noop!( Vesting::vested_transfer(Some(3).into(), 4, schedule_locked_0), @@ -389,10 +388,12 @@ fn vested_transfer_allows_max_schedules() { 1, // Vest over 2 * 256 blocks. 10, ); + // Add max amount schedules to user 4. for _ in 0 .. max_schedules { assert_ok!(Vesting::vested_transfer(Some(13).into(), 4, sched)); } + // The schedules count towards vesting balance let transferred_amount = ::MinVestedTransfer::get() * max_schedules as u64; assert_eq!(Vesting::vesting_balance(&4), Some(transferred_amount)); @@ -400,7 +401,7 @@ fn vested_transfer_allows_max_schedules() { user_4_free_balance += transferred_amount; assert_eq!(Balances::free_balance(&4), user_4_free_balance); - // Cannot insert a 4th vesting schedule when `MaxVestingSchedules` === 3 + // Cannot insert a 4th vesting schedule when `MaxVestingSchedules` === 3, assert_noop!( Vesting::vested_transfer(Some(3).into(), 4, sched), Error::::AtMaxVestingSchedules, @@ -409,7 +410,7 @@ fn vested_transfer_allows_max_schedules() { assert_eq!(Balances::free_balance(&4), user_4_free_balance); // Account 4 has fully vested when all the schedules end - System::set_block_number(::MinVestedTransfer::get() + 10); + System::set_block_number(::MinVestedTransfer::get() + sched.starting_block()); assert_eq!(Vesting::vesting_balance(&4), Some(0)); // and after unlocking its schedules are removed from storage. vest_and_assert_no_vesting::(4); @@ -492,7 +493,7 @@ fn force_vested_transfer_correctly_fails() { // Too low transfer amount. let new_vesting_schedule_too_low = - VestingInfo::new::(::MinVestedTransfer::get() - 1, 64, 10); + VestingInfo::new::(::MinVestedTransfer::get() - 1, 64, 10); assert_noop!( Vesting::force_vested_transfer( RawOrigin::Root.into(), @@ -544,10 +545,12 @@ fn force_vested_transfer_allows_max_schedules() { 1, // Vest over 256 blocks. 10, ); + // Add max amount schedules to user 4. for _ in 0 .. max_schedules { assert_ok!(Vesting::force_vested_transfer(RawOrigin::Root.into(), 13, 4, sched)); } + // The schedules count towards vesting balance. let transferred_amount = ::MinVestedTransfer::get() * max_schedules as u64; assert_eq!(Vesting::vesting_balance(&4), Some(transferred_amount)); @@ -563,7 +566,7 @@ fn force_vested_transfer_allows_max_schedules() { // so the free balance does not change. assert_eq!(Balances::free_balance(&4), user_4_free_balance); - // Account 4 has fully vested when all the schedules end + // Account 4 has fully vested when all the schedules end, System::set_block_number(::MinVestedTransfer::get() + 10); assert_eq!(Vesting::vesting_balance(&4), Some(0)); // and after unlocking its schedules are removed from storage. @@ -584,10 +587,10 @@ fn merge_schedules_that_have_not_started() { assert_eq!(Balances::usable_balance(&2), 0); // Add a schedule that is identical to the one that already exists - Vesting::vested_transfer(Some(3).into(), 2, sched0).unwrap(); + assert_ok!(Vesting::vested_transfer(Some(3).into(), 2, sched0)); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched0]); assert_eq!(Balances::usable_balance(&2), 0); - Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + assert_ok!(Vesting::merge_schedules(Some(2).into(), 0, 1)); // Since we merged identical schedules, the new schedule finishes at the same // time as the original, just with double the amount @@ -604,7 +607,7 @@ fn merge_schedules_that_have_not_started() { #[test] fn merge_ongoing_schedules() { - // Merging two schedules that have started will vest both before merging + // Merging two schedules that have started will vest both before merging. ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { // Account 2 should already have a vesting schedule. let sched0 = VestingInfo::new::( @@ -616,23 +619,24 @@ fn merge_ongoing_schedules() { let sched1 = VestingInfo::new::( ED * 10, - ED, // Vest over 10 blocks + ED, // Vest over 10 blocks sched0.starting_block() + 5, // Start at block 15 ); - Vesting::vested_transfer(Some(4).into(), 2, sched1).unwrap(); + assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched1)); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); // Got to half way through the second schedule where both schedules are actively vesting let cur_block = 20; System::set_block_number(cur_block); - // user2 has no usable balances prior to the merge because they have not unlocked + // Account 2 has no usable balances prior to the merge because they have not unlocked // with `vest` yet. assert_eq!(Balances::usable_balance(&2), 0); - Vesting::merge_schedules(Some(2).into(), 0, 1).unwrap(); + + assert_ok!(Vesting::merge_schedules(Some(2).into(), 0, 1)); // Merging schedules vests all pre-existing schedules prior to merging, which is reflected - // in user2's updated usable balance + // in account 2's updated usable balance. let sched0_vested_now = sched0.per_block() * (cur_block - sched0.starting_block()); let sched1_vested_now = sched1.per_block() * (cur_block - sched1.starting_block()); assert_eq!(Balances::usable_balance(&2), sched0_vested_now + sched1_vested_now); @@ -641,8 +645,11 @@ fn merge_ongoing_schedules() { let sched2_locked = sched1 .locked_at::(cur_block) .saturating_add(sched0.locked_at::(cur_block)); - // End block of the new schedule is the greater of either merged schedule - let sched2_end = sched1.ending_block_as_balance::().unwrap().max(sched0.ending_block_as_balance::().unwrap()); + // End block of the new schedule is the greater of either merged schedule. + let sched2_end = sched1 + .ending_block_as_balance::() + .unwrap() + .max(sched0.ending_block_as_balance::().unwrap()); let sched2_duration = sched2_end - cur_block; // Based off the new schedules total locked and its duration, we can calculate the // amount to unlock per block. @@ -660,7 +667,7 @@ fn merge_ongoing_schedules() { #[test] fn merging_shifts_other_schedules_index() { // Schedules being merged are filtered out, schedules to the right of any merged - // schedule shift left and the new, merged schedule is always last. + // schedule shift left and the merged schedule is always last. ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { let sched0 = VestingInfo::new::( ED * 10, @@ -678,7 +685,7 @@ fn merging_shifts_other_schedules_index() { 12, ); - // Account 3 start out with no schedules + // Account 3 starts out with no schedules, assert_eq!(Vesting::vesting(&3), None); // and some usable balance. let usable_balance = Balances::usable_balance(&3); @@ -687,7 +694,7 @@ fn merging_shifts_other_schedules_index() { let cur_block = 1; assert_eq!(System::block_number(), cur_block); - // Transfer the above 3 schedules to account 3 + // Transfer the above 3 schedules to account 3. assert_ok!(Vesting::vested_transfer(Some(4).into(), 3, sched0)); assert_ok!(Vesting::vested_transfer(Some(4).into(), 3, sched1)); assert_ok!(Vesting::vested_transfer(Some(4).into(), 3, sched2)); @@ -702,11 +709,14 @@ fn merging_shifts_other_schedules_index() { // Create the merged schedule of sched0 & sched2. // The merged schedule will have the max possible starting block, let sched3_start = sched1.starting_block().max(sched2.starting_block()); - // have locked equal to the sum of the two schedules locked through the current block, + // `locked` equal to the sum of the two schedules locked through the current block, let sched3_locked = sched2.locked_at::(cur_block) + sched0.locked_at::(cur_block); // and will end at the max possible block. - let sched3_end = sched2.ending_block_as_balance::().unwrap().max(sched0.ending_block_as_balance::().unwrap()); + let sched3_end = sched2 + .ending_block_as_balance::() + .unwrap() + .max(sched0.ending_block_as_balance::().unwrap()); let sched3_duration = sched3_end - sched3_start; let sched3_per_block = sched3_locked / sched3_duration; let sched3 = VestingInfo::new::(sched3_locked, sched3_per_block, sched3_start); @@ -731,7 +741,7 @@ fn merge_ongoing_and_yet_to_be_started_schedules() { ); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); - // Fast forward to half way through the life of sched_1 + // Fast forward to half way through the life of sched1. let mut cur_block = (sched0.starting_block() + sched0.ending_block_as_balance::().unwrap()) / 2; assert_eq!(cur_block, 20); System::set_block_number(cur_block); @@ -742,7 +752,7 @@ fn merge_ongoing_and_yet_to_be_started_schedules() { // Vest the current schedules (which is just sched0 now). Vesting::vest(Some(2).into()).unwrap(); - // After vesting the usable balance increases by the amount the unlocked amount. + // After vesting the usable balance increases by the unlocked amount. let sched0_vested_now = sched0.locked() - sched0.locked_at::(cur_block); usable_balance += sched0_vested_now; assert_eq!(Balances::usable_balance(&2), usable_balance); @@ -769,7 +779,7 @@ fn merge_ongoing_and_yet_to_be_started_schedules() { // The resulting schedule will have the later starting block of the two, let sched2_start = sched1.starting_block(); - // have locked equal to the sum of the two schedules locked through the current block, + // `locked` equal to the sum of the two schedules locked through the current block, let sched2_locked = sched0.locked_at::(cur_block) + sched1.locked_at::(cur_block); // and will end at the max possible block. @@ -820,7 +830,7 @@ fn merge_finished_and_ongoing_schedules() { System::set_block_number(cur_block); assert_eq!(System::block_number(), 30); - // Prior to merge_schedules and with no vest/vest_other called the user has no usable + // Prior to `merge_schedules` and with no vest/vest_other called the user has no usable // balance. assert_eq!(Balances::usable_balance(&2), 0); assert_ok!(Vesting::merge_schedules(Some(2).into(), 0, 1)); @@ -864,11 +874,12 @@ fn merge_finishing_schedules_does_not_create_a_new_one() { ED, // 30 block duration. 10, ); - assert_ok!(Vesting::vested_transfer(Some(3).into(), 2, sched1)); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); + assert_ok!(Vesting::vested_transfer(Some(3).into(), 2, sched1)); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); + + let all_scheds_end = sched0.ending_block_as_balance::().unwrap() + .max(sched1.ending_block_as_balance::().unwrap()); - let all_scheds_end = - sched0.ending_block_as_balance::().unwrap().max(sched1.ending_block_as_balance::().unwrap()); assert_eq!(all_scheds_end, 40); System::set_block_number(all_scheds_end); @@ -911,7 +922,7 @@ fn merge_finished_and_yet_to_be_started_schedules() { ED, // 40 block duration. 30, ); - // Add a 3rd schedule to demonstrate how sched1 moves + // Add a 3rd schedule to demonstrate how sched1 shifts. assert_ok!(Vesting::vested_transfer(Some(13).into(), 2, sched2)); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1, sched2]); @@ -945,13 +956,13 @@ fn merge_schedules_throws_proper_errors() { ); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); - // There is only 1 vesting schedule. + // Account 2 only has 1 vesting schedule. assert_noop!( Vesting::merge_schedules(Some(2).into(), 0, 1), Error::::ScheduleIndexOutOfBounds ); - // There are 0 vesting schedules. + // Account 4 has 0 vesting schedules. assert_eq!(Vesting::vesting(&4), None); assert_noop!(Vesting::merge_schedules(Some(4).into(), 0, 1), Error::::NotVesting); @@ -965,29 +976,23 @@ fn merge_schedules_throws_proper_errors() { // It is a storage noop with no errors if the indexes are the same. assert_storage_noop!(Vesting::merge_schedules(Some(2).into(), 0, 0).unwrap()); - - // There are no vesting schedules, so they are not vesting - assert_noop!( - Vesting::merge_schedules(Some(4).into(), 0, 1), - Error::::NotVesting - ); }); } #[test] fn generates_multiple_schedules_from_genesis_config() { let vesting_config = vec![ - // 5 * existential deposit locked + // 5 * existential deposit locked. (1, 0, 10, 5 * ED), - // 1 * existential deposit locked + // 1 * existential deposit locked. (2, 10, 20, 19 * ED), - // 2 * existential deposit locked + // 2 * existential deposit locked. (2, 10, 20, 18 * ED), - // 1 * existential deposit locked + // 1 * existential deposit locked. (12, 10, 20, 9 * ED), - // 2 * existential deposit locked + // 2 * existential deposit locked. (12, 10, 20, 8 * ED), - // 3 * existential deposit locked + // 3 * existential deposit locked. (12, 10, 20, 7 * ED), ]; ExtBuilder::default() @@ -1078,22 +1083,23 @@ fn vesting_info_validate_and_correct_works() { _ => panic!(), } - // `locked / per_block` cannot be bigger than BlockNumber::max_value` - match VestingInfo::new::(u64::MAX, 1u64, 10u64).validate::() { + // `locked / per_block + starting_block` cannot be bigger than BlockNumber::max_value`. + let start = 10; + match VestingInfo::new::(u64::MAX - start + 1, 1u64, start).validate::() { Err(Error::::InfiniteSchedule) => (), _ => panic!(), } - // With valid inputs it does not error and does not modify the inputs. - let valid = VestingInfo::new::(min_transfer, 1u64, 10u64); - assert_ok!(valid.validate::()); - assert_eq!(valid.correct(), VestingInfo::new::(min_transfer, 1u64, 10u64)); - - // `per_block` is never bigger than `locked`. + // `per_block` gets corrected to never bigger than `locked`. assert_eq!( VestingInfo::new::(256u64, 256 * 2u64, 10u64).correct(), VestingInfo::new::(256u64, 256u64, 10u64) ); + + // With valid inputs it does not error and does not modify the inputs. + let valid = VestingInfo::new::(min_transfer, 1u64, 10u64); + assert_ok!(valid.validate::()); + assert_eq!(valid.correct(), VestingInfo::new::(min_transfer, 1u64, 10u64)); } #[test] @@ -1104,8 +1110,6 @@ fn vesting_info_ending_block_as_balance_works() { per_block_0.ending_block_as_balance::(), Err(Error::::InfiniteSchedule.into()) ); - // let per_block_1 = VestingInfo::new::(256u32, 1u32, 10u32); - // assert_eq!(per_block_0.ending_block_as_balance::().unwrap(), per_block_1.ending_block_as_balance::().unwrap()); // `per_block >= locked` always results in a schedule ending the block after it starts let per_block_gt_locked = VestingInfo::new::(256u32, 256 * 2u32, 10u32); From 38dfbcd8bf887249e8df5c4635d3c2eee81955d2 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 5 Jul 2021 20:09:20 -0700 Subject: [PATCH 071/144] Test vest with multi schedule --- frame/vesting/src/tests.rs | 76 +++++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 17 deletions(-) diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index 21827dd149034..c27c1a1c9f803 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -208,6 +208,35 @@ fn vested_balance_should_transfer() { }); } +#[test] +fn vested_balance_should_transferwith_multi_sched() { + ExtBuilder::default() + .existential_deposit(ED) + .build() + .execute_with(|| { + let sched0 = VestingInfo::new::(5*ED, 128, 0); + assert_ok!(Vesting::vested_transfer(Some(13).into(), 1, sched0)); + // Total of 2560 of locked for all the schedules. + assert_eq!(Vesting::vesting(&1).unwrap(), vec![sched0, sched0]); + + let user1_free_balance = Balances::free_balance(&1); + assert_eq!(user1_free_balance, 3840); // Account 1 has free balance + + // Account 1 has only 256 units unlocking at block 1 (plus 1280 unvested) + assert_eq!(Vesting::vesting_balance(&1), Some(2304)); + assert_ok!(Vesting::vest(Some(1).into())); + assert_ok!(Balances::transfer(Some(1).into(), 2, 1536)); + }); +} + +#[test] +fn vest_correctly_fails() { + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + assert!(!>::contains_key(4)); + assert_noop!(Vesting::vest(Some(4).into()), Error::::NotVesting); + }); +} + #[test] fn vested_balance_should_transfer_using_vest_other() { ExtBuilder::default() @@ -223,6 +252,35 @@ fn vested_balance_should_transfer_using_vest_other() { }); } +#[test] +fn vested_balance_should_transfer_using_vest_other_with_multi_sched() { + ExtBuilder::default() + .existential_deposit(ED) + .build() + .execute_with(|| { + let sched0 = VestingInfo::new::(5*ED, 128, 0); + assert_ok!(Vesting::vested_transfer(Some(13).into(), 1, sched0)); + // Total of 2560 of locked for all the schedules. + assert_eq!(Vesting::vesting(&1).unwrap(), vec![sched0, sched0]); + + let user1_free_balance = Balances::free_balance(&1); + assert_eq!(user1_free_balance, 3840); // Account 1 has free balance + + // Account 1 has only 256 units unlocking at block 1 (plus 1280 unvested) + assert_eq!(Vesting::vesting_balance(&1), Some(2304)); + assert_ok!(Vesting::vest_other(Some(2).into(), 1)); + assert_ok!(Balances::transfer(Some(1).into(), 2, 1536)); + }); +} + +#[test] +fn vest_other_correctly_fails() { + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + assert!(!>::contains_key(4)); + assert_noop!(Vesting::vest_other(Some(3).into(), 4), Error::::NotVesting); + }); +} + #[test] fn extra_balance_should_transfer() { ExtBuilder::default() @@ -542,7 +600,7 @@ fn force_vested_transfer_allows_max_schedules() { let max_schedules = ::MaxVestingSchedules::get(); let sched = VestingInfo::new::( ::MinVestedTransfer::get(), - 1, // Vest over 256 blocks. + 1, // Vest over 2 * 256 blocks. 10, ); @@ -1135,19 +1193,3 @@ fn vesting_info_ending_block_as_balance_works() { 0 ); } - -#[test] -fn vest_correctly_fails() { - ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - assert!(!>::contains_key(4)); - assert_noop!(Vesting::vest(Some(4).into()), Error::::NotVesting); - }); -} - -#[test] -fn vest_other_correctly_fails() { - ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - assert!(!>::contains_key(4)); - assert_noop!(Vesting::vest_other(Some(3).into(), 4), Error::::NotVesting); - }); -} From a9fca6d4a5e97524dd9c2b319e5c98d533fa0d96 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 5 Jul 2021 20:38:14 -0700 Subject: [PATCH 072/144] Don't use half max balance in benchmarks --- frame/vesting/src/benchmarking.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index 925cad3a6e09c..a652b25ccc389 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -69,6 +69,9 @@ fn add_vesting_schedules( target.clone(), schedule )); + + // Top up to guarantee we can always transfer another schedule. + T::Currency::make_free_balance_be(&source, BalanceOf::::max_value()); } Ok(total_locked.into()) @@ -81,7 +84,8 @@ benchmarks! { let caller: T::AccountId = whitelisted_caller(); let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); - T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value() / 2u32.into()); + T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance()); + add_locks::(&caller, l as u8); let expected_balance = add_vesting_schedules::(caller_lookup, s)?; @@ -108,7 +112,8 @@ benchmarks! { let caller: T::AccountId = whitelisted_caller(); let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); - T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value() / 2u32.into()); + T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance()); + add_locks::(&caller, l as u8); add_vesting_schedules::(caller_lookup, s)?; @@ -135,7 +140,8 @@ benchmarks! { let other: T::AccountId = account("other", 0, SEED); let other_lookup: ::Source = T::Lookup::unlookup(other.clone()); - T::Currency::make_free_balance_be(&other, BalanceOf::::max_value() / 2u32.into()); + // T::Currency::make_free_balance_be(&other, BalanceOf::::max_value() / 2u32.into()); + add_locks::(&other, l as u8); let expected_balance = add_vesting_schedules::(other_lookup.clone(), s)?; @@ -164,7 +170,6 @@ benchmarks! { let other: T::AccountId = account("other", 0, SEED); let other_lookup: ::Source = T::Lookup::unlookup(other.clone()); - T::Currency::make_free_balance_be(&other, BalanceOf::::max_value() / 2u32.into()); add_locks::(&other, l as u8); add_vesting_schedules::(other_lookup.clone(), s)?; From 96af8dd13c2beb5473dbf2a4d299af1982d1762e Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 5 Jul 2021 21:03:17 -0700 Subject: [PATCH 073/144] Use debug_assert when locked is unexpected 0 --- frame/vesting/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 8a7cc1ce0dbf0..1635a65932ae4 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -50,7 +50,7 @@ mod migrations; mod mock; #[cfg(test)] mod tests; -pub mod vesting_info; +mod vesting_info; pub mod weights; @@ -489,6 +489,7 @@ impl Pallet { .locked_at::(now) .saturating_add(schedule2.locked_at::(now)); // This shouldn't happen because we know at least one ending block is greater than now. + debug_assert!(!locked.is_zero(), "merge_vesting_info validation checks failed to catch a locked of 0"); if locked.is_zero() { log::warn!( target: LOG_TARGET, From 4d410f7e6a8df7f57c5d65f6a5cae4daf8bd9bad Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 5 Jul 2021 22:03:42 -0700 Subject: [PATCH 074/144] Implement exec_action --- frame/vesting/src/lib.rs | 106 +++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 49 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 1635a65932ae4..0b655e86706d2 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -416,43 +416,13 @@ pub mod pallet { }; let schedule1_index = schedule1_index as usize; let schedule2_index = schedule2_index as usize; - let schedules = Self::vesting(&who).ok_or(Error::::NotVesting)?; - // The schedule index is based off of the schedule ordering prior to filtering out any - // schedules that may be ending at this block. - let schedule1 = - *schedules.get(schedule1_index).ok_or(Error::::ScheduleIndexOutOfBounds)?; - let schedule2 = - *schedules.get(schedule2_index).ok_or(Error::::ScheduleIndexOutOfBounds)?; + let schedules = Self::vesting(&who).ok_or(Error::::NotVesting)?; let merge_action = VestingAction::Merge(schedule1_index, schedule2_index); - // The length of `schedules` decreases by 2 here since we filter out 2 schedules. - // Thus we know below that we can insert the new merged schedule without error (assuming - // initial state was valid). - let (mut schedules, mut locked_now) = - Self::report_schedule_updates(schedules.to_vec(), merge_action); + let (schedules, locked_now) = + Self::exec_action(schedules.to_vec(), merge_action)?; - let now = >::block_number(); - if let Some(new_schedule) = Self::merge_vesting_info(now, schedule1, schedule2)? { - // Merging created a new schedule so we: - // 1) need to add it to the accounts vesting schedule collection, - schedules.push(new_schedule); - // (we use `locked_at` in case this is a schedule that started in the past) - let new_schedule_locked = new_schedule.locked_at::(now); - // 2) update the locked amount to reflect the schedule we just added, - locked_now = locked_now.saturating_add(new_schedule_locked); - // and 3) deposit an event. - Self::deposit_event(Event::::MergedScheduleAdded( - new_schedule.locked(), - new_schedule.per_block(), - new_schedule.starting_block(), - )); - } // In the None case there was no new schedule to account for. - - debug_assert!( - locked_now > Zero::zero() && schedules.len() > 0 || - locked_now == Zero::zero() && schedules.len() == 0 - ); Self::write_vesting(&who, schedules)?; Self::write_lock(&who, locked_now); @@ -573,7 +543,7 @@ impl Pallet { /// /// Returns a tuple that consists of: /// - vec of vesting schedules, where completed schedules and those - /// specified by filter are remove. (Note the vec is not checked for respecting + /// specified by filter are remove. (Note the vec is not checked for respecting /// bounded length.) /// - the amount locked at the current block number based on the given schedules. /// @@ -639,16 +609,62 @@ impl Pallet { let schedules = Self::vesting(&who).ok_or(Error::::NotVesting)?; let (schedules, locked_now) = - Self::report_schedule_updates(schedules.to_vec(), VestingAction::Passive); + Self::exec_action(schedules.to_vec(), VestingAction::Passive)?; + + Self::write_vesting(&who, schedules)?; + Self::write_lock(&who, locked_now); + + Ok(()) + } + + /// Execute a `VestingAction` against the given `schedules`. Returns the updated schedules + /// and locked amount. + fn exec_action( + schedules: Vec, T::BlockNumber>>, + action: VestingAction, + ) -> Result<(Vec, T::BlockNumber>>, BalanceOf), DispatchError> { + let (schedules, locked_now) = match action { + VestingAction::Merge(idx1, idx2) => { + // The schedule index is based off of the schedule ordering prior to filtering out any + // schedules that may be ending at this block. + let schedule1 = *schedules.get(idx1).ok_or(Error::::ScheduleIndexOutOfBounds)?; + let schedule2 = *schedules.get(idx2).ok_or(Error::::ScheduleIndexOutOfBounds)?; + + // The length of `schedules` decreases by 2 here since we filter out 2 schedules. + // Thus we know below that we can pushed the new merged schedule without error (assuming + // initial state was valid). + let (mut schedules, mut locked_now) = + Self::report_schedule_updates(schedules.to_vec(), action); + + let now = >::block_number(); + if let Some(new_schedule) = Self::merge_vesting_info(now, schedule1, schedule2)? { + // Merging created a new schedule so we: + // 1) need to add it to the accounts vesting schedule collection, + schedules.push(new_schedule); + // (we use `locked_at` in case this is a schedule that started in the past) + let new_schedule_locked = + new_schedule.locked_at::(now); + // 2) update the locked amount to reflect the schedule we just added, + locked_now = locked_now.saturating_add(new_schedule_locked); + // and 3) deposit an event. + Self::deposit_event(Event::::MergedScheduleAdded( + new_schedule.locked(), + new_schedule.per_block(), + new_schedule.starting_block(), + )); + } // In the None case there was no new schedule to account for. + + (schedules, locked_now) + } + _ => Self::report_schedule_updates(schedules.to_vec(), action), + }; debug_assert!( locked_now > Zero::zero() && schedules.len() > 0 || locked_now == Zero::zero() && schedules.len() == 0 ); - Self::write_vesting(&who, schedules)?; - Self::write_lock(&who, locked_now); - Ok(()) + Ok((schedules, locked_now)) } } @@ -694,17 +710,13 @@ where let vesting_schedule = VestingInfo::new::(locked, per_block, starting_block); let mut schedules = Self::vesting(who).unwrap_or_default(); - // NOTE: we must push the new schedule so that `report_schedule_updates` + // NOTE: we must push the new schedule so that `exec_action` // will give the correct new locked amount. ensure!(schedules.try_push(vesting_schedule).is_ok(), Error::::AtMaxVestingSchedules); let (schedules, locked_now) = - Self::report_schedule_updates(schedules.to_vec(), VestingAction::Passive); + Self::exec_action(schedules.to_vec(), VestingAction::Passive)?; - debug_assert!( - locked_now > Zero::zero() && schedules.len() > 0 || - locked_now == Zero::zero() && schedules.len() == 0 - ); Self::write_vesting(&who, schedules)?; Self::write_lock(who, locked_now); @@ -737,12 +749,8 @@ where let remove_action = VestingAction::Remove(schedule_index as usize); let (schedules, locked_now) = - Self::report_schedule_updates(schedules.to_vec(), remove_action); + Self::exec_action(schedules.to_vec(), remove_action)?; - debug_assert!( - locked_now > Zero::zero() && schedules.len() > 0 || - locked_now == Zero::zero() && schedules.len() == 0 - ); Self::write_vesting(&who, schedules)?; Self::write_lock(who, locked_now); Ok(()) From c750bd8b2f09da871a2f87c0a11cc4f92a31cc59 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 5 Jul 2021 22:19:51 -0700 Subject: [PATCH 075/144] Simplify per_block calc in vesting_info --- frame/vesting/src/lib.rs | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 0b655e86706d2..bf5093e6203f2 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -470,26 +470,16 @@ impl Pallet { let ending_block = schedule1_ending_block.max(schedule2_ending_block); let starting_block = now.max(schedule1.starting_block()).max(schedule2.starting_block()); - let duration = - ending_block.saturating_sub(T::BlockNumberToBalance::convert(starting_block)); - let per_block = if duration > locked { - // This would mean we have a per_block of less than 1, which should not be not possible - // because when we create the new schedule is at most the same duration as the longest, - // but never greater. - One::one() - } else { - match locked.checked_div(&duration) { - // The logic of `ending_block_as_balance` guarantees that each schedule ends at - // least a block after it starts and since we take the max starting and ending_block - // we should never get here. - None => locked, - Some(per_block) => per_block, - } + + let per_block = { + let duration = ending_block + .saturating_sub(T::BlockNumberToBalance::convert(starting_block)) + .max(One::one()); + (locked / duration).max(One::one()) }; - // While `per_block` should never be 0, it is possible that the created schedule would - // end after the highest possible block, which is a case where `validate` fails but we - // are ok with. + // While `per_block` should never be 0, it is possible that the created schedule would end + // after the highest possible block, which is a case we are ok with, but `validate` fails let schedule = VestingInfo::new::(locked, per_block, starting_block); Ok(Some(schedule)) From 24214ac2e0f049504c29057a88856c9d7ca02da5 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 5 Jul 2021 22:29:39 -0700 Subject: [PATCH 076/144] VestingInfo.validate in add_vesting_schedule & can_add_vesting_schedule --- frame/vesting/src/lib.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index bf5093e6203f2..c41691214185f 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -67,8 +67,8 @@ use frame_system::{ensure_root, ensure_signed, pallet_prelude::*}; pub use pallet::*; use sp_runtime::{ traits::{ - AtLeast32BitUnsigned, Bounded, CheckedDiv, Convert, MaybeSerializeDeserialize, One, - Saturating, StaticLookup, Zero, + AtLeast32BitUnsigned, Bounded, Convert, MaybeSerializeDeserialize, One, Saturating, + StaticLookup, Zero, }, RuntimeDebug, }; @@ -688,7 +688,6 @@ where /// `vest_other`. /// /// Is a no-op if the amount to be vested is zero. - /// NOTE: does not validate schedule params. fn add_vesting_schedule( who: &T::AccountId, locked: BalanceOf, @@ -698,6 +697,9 @@ where if locked.is_zero() { return Ok(()); } let vesting_schedule = VestingInfo::new::(locked, per_block, starting_block); + // Check for `per_block` or `locked` of 0 and ending block greater than max block. + vesting_schedule.validate::()?; + let mut schedules = Self::vesting(who).unwrap_or_default(); // NOTE: we must push the new schedule so that `exec_action` @@ -716,14 +718,16 @@ where // Ensure we can call `add_vesting_schedule` without error. This should always // be called prior to `add_vesting_schedule`. // - // NOTE: expects schedule param validation has been done due to different - // scenarios having varying requirements. fn can_add_vesting_schedule( who: &T::AccountId, - _locked: BalanceOf, - _per_block: BalanceOf, - _starting_block: T::BlockNumber, + locked: BalanceOf, + per_block: BalanceOf, + starting_block: T::BlockNumber, ) -> DispatchResult { + // Check for `per_block` or `locked` of 0 and ending block greater than max block. + VestingInfo::new::(locked, per_block, starting_block) + .validate::()?; + ensure!( Vesting::::decode_len(who).unwrap_or_default() < T::MaxVestingSchedules::get() as usize, From 48dd1b5fafeede2384acb96d5f1d83ea1eae8273 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 5 Jul 2021 22:35:09 -0700 Subject: [PATCH 077/144] Simplify post migrate check --- frame/vesting/src/migrations.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/frame/vesting/src/migrations.rs b/frame/vesting/src/migrations.rs index 097e66e8a2161..4faa6158be1ee 100644 --- a/frame/vesting/src/migrations.rs +++ b/frame/vesting/src/migrations.rs @@ -59,11 +59,7 @@ pub(crate) mod v1 { for (_key, schedules) in Vesting::::iter() { // Assert the new bound vec respects size. - assert!(schedules.len() > 0, "A bounded vec with no items was created."); - assert!( - schedules.len() <= T::MaxVestingSchedules::get() as usize, - "A bounded vec with too many items was created." - ); + assert!(schedules.len() == 0, "A bounded vec with incorrect count of items was created."); for s in schedules { // Check for infinite schedules. From c3cae6b05d5059903bf2d3ce04367bf65309bed8 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 5 Jul 2021 22:39:58 -0700 Subject: [PATCH 078/144] Remove merge event --- frame/vesting/src/lib.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index c41691214185f..e7646628c5142 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -251,9 +251,6 @@ pub mod pallet { VestingUpdated(T::AccountId, BalanceOf), /// An \[account\] has become fully vested. No further vesting can happen. VestingCompleted(T::AccountId), - /// 2 vesting schedules where successfully merged together and the merged schedule was added. - /// \[locked, per_block, starting_block\] - MergedScheduleAdded(BalanceOf, BalanceOf, T::BlockNumber), } /// Error for the vesting pallet. @@ -634,14 +631,8 @@ impl Pallet { // (we use `locked_at` in case this is a schedule that started in the past) let new_schedule_locked = new_schedule.locked_at::(now); - // 2) update the locked amount to reflect the schedule we just added, + // and 2) update the locked amount to reflect the schedule we just added. locked_now = locked_now.saturating_add(new_schedule_locked); - // and 3) deposit an event. - Self::deposit_event(Event::::MergedScheduleAdded( - new_schedule.locked(), - new_schedule.per_block(), - new_schedule.starting_block(), - )); } // In the None case there was no new schedule to account for. (schedules, locked_now) From 05bf0c5b0d46b16499d4058c7c2c2f722f399151 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 5 Jul 2021 23:36:44 -0700 Subject: [PATCH 079/144] Minor benchmarking updates --- frame/vesting/src/benchmarking.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index a652b25ccc389..24e5d8ee56d92 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -23,7 +23,7 @@ use super::*; use frame_system::{RawOrigin, Pallet as System}; use frame_benchmarking::{benchmarks, account, whitelisted_caller, impl_benchmark_test_suite}; -use sp_runtime::traits::Bounded; +use sp_runtime::traits::{CheckedDiv, Bounded}; use frame_support::assert_ok; use crate::Pallet as Vesting; @@ -140,7 +140,6 @@ benchmarks! { let other: T::AccountId = account("other", 0, SEED); let other_lookup: ::Source = T::Lookup::unlookup(other.clone()); - // T::Currency::make_free_balance_be(&other, BalanceOf::::max_value() / 2u32.into()); add_locks::(&other, l as u8); let expected_balance = add_vesting_schedules::(other_lookup.clone(), s)?; From e4493d7ecef5202c89458e43fe4a789cefdae4fc Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 6 Jul 2021 00:33:42 -0700 Subject: [PATCH 080/144] Remove VestingInfo.correct --- frame/vesting/src/lib.rs | 3 --- frame/vesting/src/tests.rs | 9 +-------- frame/vesting/src/vesting_info.rs | 9 ++------- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index e7646628c5142..131cf4efea563 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -492,9 +492,6 @@ impl Pallet { ensure!(schedule.locked() >= T::MinVestedTransfer::get(), Error::::AmountLow); schedule.validate::()?; - // Potentially correct `per_block` if its greater than locked. - let schedule = schedule.correct(); - let target = T::Lookup::lookup(target)?; let source = T::Lookup::lookup(source)?; diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index c27c1a1c9f803..75decd5b4980b 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -1121,7 +1121,7 @@ fn merge_vesting_errors_with_per_block_0() { } #[test] -fn vesting_info_validate_and_correct_works() { +fn vesting_info_validate_works() { let min_transfer = ::MinVestedTransfer::get(); // Does not check for min transfer. match VestingInfo::new::(min_transfer - 1, 1u64, 10u64).validate::() { @@ -1148,16 +1148,9 @@ fn vesting_info_validate_and_correct_works() { _ => panic!(), } - // `per_block` gets corrected to never bigger than `locked`. - assert_eq!( - VestingInfo::new::(256u64, 256 * 2u64, 10u64).correct(), - VestingInfo::new::(256u64, 256u64, 10u64) - ); - // With valid inputs it does not error and does not modify the inputs. let valid = VestingInfo::new::(min_transfer, 1u64, 10u64); assert_ok!(valid.validate::()); - assert_eq!(valid.correct(), VestingInfo::new::(min_transfer, 1u64, 10u64)); } #[test] diff --git a/frame/vesting/src/vesting_info.rs b/frame/vesting/src/vesting_info.rs index 3a4f2df4a659b..3de498f9e714c 100644 --- a/frame/vesting/src/vesting_info.rs +++ b/frame/vesting/src/vesting_info.rs @@ -65,22 +65,17 @@ where Ok(()) } - /// Potentially correct the `per_block` of a schedule. Typically called after `validate`. - pub fn correct(mut self) -> Self { - self.per_block = if self.per_block > self.locked { self.locked } else { self.per_block }; - self - } - /// Locked amount at schedule creation. pub fn locked(&self) -> Balance { self.locked } - /// Amount that gets unlocked every block after `starting_block`. + // pub fn raw_per_block(&self) -> Balance { pub fn per_block(&self) -> Balance { self.per_block } + /// Starting block for unlocking(vesting). pub fn starting_block(&self) -> BlockNumber { self.starting_block From ee6a75dcb9deb7262dab66f74270a67dcb8b6db5 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 6 Jul 2021 01:00:32 -0700 Subject: [PATCH 081/144] per_block accesor max with 1 --- frame/vesting/src/lib.rs | 3 ++- frame/vesting/src/tests.rs | 27 ++++++++++++++++++++------- frame/vesting/src/vesting_info.rs | 25 +++++++++++++++---------- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 131cf4efea563..71bf9be5e378e 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -476,8 +476,9 @@ impl Pallet { }; // While `per_block` should never be 0, it is possible that the created schedule would end - // after the highest possible block, which is a case we are ok with, but `validate` fails + // after the highest possible block, which is a case we are ok with, but `validate` fails. let schedule = VestingInfo::new::(locked, per_block, starting_block); + // debug_assert!(schedule.validate::()); Ok(Some(schedule)) } diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index 75decd5b4980b..7bc902fe5e2c4 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -1096,26 +1096,28 @@ fn build_genesis_has_storage_version_v1() { } #[test] -fn merge_vesting_errors_with_per_block_0() { +fn merge_vesting_handles_per_block_0() { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { let sched0 = VestingInfo::new::( - ED, 0, // Vesting over infinite blocks. + ED, + 0, // Vesting over 256 blocks. 1, ); assert_eq!( sched0.ending_block_as_balance::(), - Err(Error::::InfiniteSchedule.into()) + Ok(257) ); let sched1 = VestingInfo::new::( ED * 2, - 1, // Vesting over 512 blocks. + 0, // Vesting over 512 blocks. 10, ); assert_eq!(sched1.ending_block_as_balance::(), Ok(512u64 + 10)); + let merged = VestingInfo::new::(764, 1, 10); assert_eq!( Vesting::merge_vesting_info(5, sched0, sched1), - Err(Error::::InfiniteSchedule.into()) + Ok(Some(merged)) ); }); } @@ -1155,11 +1157,11 @@ fn vesting_info_validate_works() { #[test] fn vesting_info_ending_block_as_balance_works() { - // Treats `per_block` 0 as an error + // Treats `per_block` 0 as 1. let per_block_0 = VestingInfo::new::(256u32, 0u32, 10u32); assert_eq!( per_block_0.ending_block_as_balance::(), - Err(Error::::InfiniteSchedule.into()) + Ok(266) ); // `per_block >= locked` always results in a schedule ending the block after it starts @@ -1186,3 +1188,14 @@ fn vesting_info_ending_block_as_balance_works() { 0 ); } + +#[test] +fn per_block_works() { + let per_block_0 = VestingInfo::new::(256u32, 0u32, 10u32); + assert_eq!(per_block_0.per_block(), 1u32); + assert_eq!(per_block_0.raw_per_block(), 0u32); + + let per_block_1 = VestingInfo::new::(256u32, 1u32, 10u32); + assert_eq!(per_block_1.per_block(), 1u32); + assert_eq!(per_block_1.raw_per_block(), 1u32); +} diff --git a/frame/vesting/src/vesting_info.rs b/frame/vesting/src/vesting_info.rs index 3de498f9e714c..e47cd928020ff 100644 --- a/frame/vesting/src/vesting_info.rs +++ b/frame/vesting/src/vesting_info.rs @@ -54,7 +54,7 @@ where let max_block = BlockNumberToBalance::convert(BlockNumber::max_value()); let starting_block = BlockNumberToBalance::convert(self.starting_block()); - match self.locked.checked_div(&self.per_block) { + match self.locked.checked_div(&self.raw_per_block()) { None => return Err(Error::::InfiniteSchedule), // `per_block` is 0 Some(duration) => { let end = duration.saturating_add(starting_block); @@ -70,8 +70,17 @@ where self.locked } - // pub fn raw_per_block(&self) -> Balance { + + /// Amount that gets unlocked every block after `starting_block`. Corrects for `per_block` of 0. + /// This should be used whenever accessing `per_block` unless explicitly checking for 0 values. pub fn per_block(&self) -> Balance { + self.per_block.max(One::one()) + } + + + /// Get the unmodified `per_block`. Generally should not be used, but is is useful for + /// validating `per_block`. + pub fn raw_per_block(&self) -> Balance { self.per_block } @@ -92,7 +101,7 @@ where let vested_block_count = BlockNumberToBalance::convert(vested_block_count); // Return amount that is still locked in vesting vested_block_count - .checked_mul(&self.per_block) + .checked_mul(&self.per_block()) // per_block accessor guarantees at least 1. .map(|to_unlock| self.locked.saturating_sub(to_unlock)) .unwrap_or(Zero::zero()) } @@ -102,17 +111,13 @@ where &self, ) -> Result { let starting_block = BlockNumberToBalance::convert(self.starting_block); - let duration = if self.per_block >= self.locked { + let duration = if self.per_block() >= self.locked { // If `per_block` is bigger than `locked`, the schedule will end // the block after starting. One::one() - } else if self.per_block.is_zero() { - // Check for div by 0 errors, which should only be from legacy - // vesting schedules since new ones are validated for this. - return Err(Error::::InfiniteSchedule.into()); } else { - self.locked / self.per_block + - if (self.locked % self.per_block).is_zero() { + self.locked / self.per_block() + + if (self.locked % self.per_block()).is_zero() { Zero::zero() } else { // `per_block` does not perfectly divide `locked`, so we need an extra block to From ba7bc831fd9be0e56aba05bdc51d35d2d671a254 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 6 Jul 2021 01:03:11 -0700 Subject: [PATCH 082/144] Improve comment --- frame/vesting/src/vesting_info.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/frame/vesting/src/vesting_info.rs b/frame/vesting/src/vesting_info.rs index e47cd928020ff..ce76ef72f5e71 100644 --- a/frame/vesting/src/vesting_info.rs +++ b/frame/vesting/src/vesting_info.rs @@ -72,6 +72,7 @@ where /// Amount that gets unlocked every block after `starting_block`. Corrects for `per_block` of 0. + /// We dont let `per_block` be less than one, or else the vesting will never end. /// This should be used whenever accessing `per_block` unless explicitly checking for 0 values. pub fn per_block(&self) -> Balance { self.per_block.max(One::one()) From bd8fed298026a23b25e7377f35c9d81e6affdcf2 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 6 Jul 2021 01:09:36 -0700 Subject: [PATCH 083/144] Remoe debug --- frame/vesting/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 71bf9be5e378e..c72fa3ef17bfc 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -478,7 +478,6 @@ impl Pallet { // While `per_block` should never be 0, it is possible that the created schedule would end // after the highest possible block, which is a case we are ok with, but `validate` fails. let schedule = VestingInfo::new::(locked, per_block, starting_block); - // debug_assert!(schedule.validate::()); Ok(Some(schedule)) } From ca727c6495b094d9538d1a864877526e4f4182de Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 6 Jul 2021 09:49:50 -0700 Subject: [PATCH 084/144] Fix add schedule comment --- frame/support/src/traits/tokens/currency/lockable.rs | 4 ++-- frame/vesting/src/lib.rs | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/frame/support/src/traits/tokens/currency/lockable.rs b/frame/support/src/traits/tokens/currency/lockable.rs index f8696a19a41ca..991538f306903 100644 --- a/frame/support/src/traits/tokens/currency/lockable.rs +++ b/frame/support/src/traits/tokens/currency/lockable.rs @@ -84,8 +84,8 @@ pub trait VestingSchedule { /// Adds a vesting schedule to a given account. /// - /// If there already exists a vesting schedule for the given account, an `Err` is returned - /// and nothing is updated. + /// If the account has `MaxVestingSchedules`, an Error is returned and nothing + /// is updated. /// /// Is a no-op if the amount to be vested is zero. /// diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index c72fa3ef17bfc..b77f2684d6808 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -668,7 +668,7 @@ where /// Adds a vesting schedule to a given account. /// - /// If the account has `MaxVestingSchedules`, an Error is returned and nothing + /// If the account has `MaxVestingSchedules`, an Error is returned and nothing ` /// is updated. /// /// On success, a linearly reducing amount of funds will be locked. In order to realise any @@ -676,6 +676,8 @@ where /// `vest_other`. /// /// Is a no-op if the amount to be vested is zero. + /// + /// NOTE: This doesn't alter the free balance of the account. fn add_vesting_schedule( who: &T::AccountId, locked: BalanceOf, From 366b93b3d620d80e09005317a94131aa85dd03f8 Mon Sep 17 00:00:00 2001 From: Zeke Mostov <32168567+emostov@users.noreply.github.com> Date: Tue, 6 Jul 2021 09:52:23 -0700 Subject: [PATCH 085/144] Apply suggestions from code review Co-authored-by: Peter Goodspeed-Niklaus --- frame/vesting/src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index b77f2684d6808..9a07b2b3d24d4 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -98,11 +98,12 @@ impl Default for Releases { } } -/// Actions to take on a users `Vesting` storage entry. +/// Actions to take on a user's `Vesting` storage entry. +#[derive(Clone, Copy)] enum VestingAction { /// Do not actively remove any schedules. Passive, - /// Remove the schedules specified by the index. + /// Remove the schedule specified by the index. Remove(usize), /// Remove the two schedules, specified by index, so they can be merged. Merge(usize, usize), @@ -110,7 +111,7 @@ enum VestingAction { impl VestingAction { /// Whether or not the filter says the schedule index should be removed. - fn should_remove(&self, index: &usize) -> bool { + fn should_remove(&self, index: usize) -> bool { match self { Self::Passive => false, Self::Remove(index1) => index1 == index, From 01ecef5fb701814f75477a40e937a122997011d8 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 6 Jul 2021 13:39:49 -0700 Subject: [PATCH 086/144] no ref for should_remove param --- frame/vesting/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index b77f2684d6808..3d66fdd7683c5 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -110,11 +110,11 @@ enum VestingAction { impl VestingAction { /// Whether or not the filter says the schedule index should be removed. - fn should_remove(&self, index: &usize) -> bool { + fn should_remove(&self, index: usize) -> bool { match self { Self::Passive => false, - Self::Remove(index1) => index1 == index, - Self::Merge(index1, index2) => index1 == index || index2 == index, + Self::Remove(index1) => *index1 == index, + Self::Merge(index1, index2) => *index1 == index || *index2 == index, } } } @@ -544,7 +544,7 @@ impl Pallet { .enumerate() .filter_map(|(index, schedule)| { let locked_now = schedule.locked_at::(now); - if locked_now.is_zero() || action.should_remove(&index) { + if locked_now.is_zero() || action.should_remove(index) { None } else { // We track the locked amount only if the schedule is included. From 09ef3e8ce7f391f95aae7be490d1c8be72678713 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 6 Jul 2021 13:41:35 -0700 Subject: [PATCH 087/144] Remove unused vestingaction derive --- frame/vesting/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index e9f9fd26ffa26..c6e4549a92fde 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -99,7 +99,6 @@ impl Default for Releases { } /// Actions to take on a user's `Vesting` storage entry. -#[derive(Clone, Copy)] enum VestingAction { /// Do not actively remove any schedules. Passive, From ce37a9e42bfc78247cc65218136b288be90fac29 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 6 Jul 2021 14:33:31 -0700 Subject: [PATCH 088/144] Asserts to show balance unlock in merge benchmark --- frame/vesting/src/benchmarking.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index 24e5d8ee56d92..284abe83edf5b 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -19,13 +19,12 @@ #![cfg(feature = "runtime-benchmarks")] -use super::*; - -use frame_system::{RawOrigin, Pallet as System}; -use frame_benchmarking::{benchmarks, account, whitelisted_caller, impl_benchmark_test_suite}; -use sp_runtime::traits::{CheckedDiv, Bounded}; -use frame_support::assert_ok; +use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite, whitelisted_caller}; +use frame_support::{assert_err, assert_noop, assert_ok}; +use frame_system::{Pallet as System, RawOrigin}; +use sp_runtime::traits::{Bounded, CheckedDiv}; +use super::*; use crate::Pallet as Vesting; const SEED: u32 = 0; @@ -320,6 +319,9 @@ benchmarks! { let l in 0 .. MaxLocksOf::::get() - 1; let s in 2 .. T::MaxVestingSchedules::get(); + // Destination used just for currency transfers in asserts. + let test_dest: T::AccountId = account("test_dest", 0, SEED); + let caller: T::AccountId = account("caller", 0, SEED); let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); // Give target other locks. @@ -344,6 +346,8 @@ benchmarks! { s as usize, "There should be exactly max vesting schedules" ); + // The balance is not actually transferable because it has not been unlocked. + assert!(T::Currency::transfer(&caller, &test_dest, expected_balance, ExistenceRequirement::AllowDeath).is_err()); }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, s - 1) verify { let expected_schedule = VestingInfo::new::( @@ -371,6 +375,10 @@ benchmarks! { (s - 1) as usize, "Schedule count should reduce by 1" ); + // Since merge unlocks all schedules we can now transfer the balance. + assert_ok!( + T::Currency::transfer(&caller, &test_dest, expected_balance, ExistenceRequirement::AllowDeath) + ); } } From 272d663cb084b16bd64eb875c7a447b3d641550e Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 6 Jul 2021 15:03:40 -0700 Subject: [PATCH 089/144] Remove unused imports --- frame/vesting/src/benchmarking.rs | 2 +- frame/vesting/src/tests.rs | 7 +- frame/vesting/src/weights.rs | 173 +++++++++++++++--------------- 3 files changed, 91 insertions(+), 91 deletions(-) diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index 284abe83edf5b..b8e08fe7e8e19 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite, whitelisted_caller}; -use frame_support::{assert_err, assert_noop, assert_ok}; +use frame_support::assert_ok; use frame_system::{Pallet as System, RawOrigin}; use sp_runtime::traits::{Bounded, CheckedDiv}; diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index 7bc902fe5e2c4..5ce6e43d4ee49 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -209,11 +209,8 @@ fn vested_balance_should_transfer() { } #[test] -fn vested_balance_should_transferwith_multi_sched() { - ExtBuilder::default() - .existential_deposit(ED) - .build() - .execute_with(|| { +fn vested_balance_should_transfer_with_multi_sched() { + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { let sched0 = VestingInfo::new::(5*ED, 128, 0); assert_ok!(Vesting::vested_transfer(Some(13).into(), 1, sched0)); // Total of 2560 of locked for all the schedules. diff --git a/frame/vesting/src/weights.rs b/frame/vesting/src/weights.rs index 7f4091b48fd7e..15ce6f4c79ad2 100644 --- a/frame/vesting/src/weights.rs +++ b/frame/vesting/src/weights.rs @@ -18,22 +18,17 @@ //! Autogenerated weights for pallet_vesting //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-07-05, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 +//! DATE: 2021-07-06, STEPS: `[]`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 128 // Executed Command: // target/release/substrate // benchmark -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=pallet_vesting +// --pallet=pallet-vesting // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 // --output=./frame/vesting/src/weights.rs -// --template=./.maintain/frame-weight-template.hbs +// --template=.maintain/frame-weight-template.hbs #![allow(unused_parens)] @@ -58,70 +53,74 @@ pub trait WeightInfo { pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { fn vest_locked(l: u32, s: u32, ) -> Weight { - (60_524_000 as Weight) - // Standard Error: 1_000 - .saturating_add((158_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 5_000 - .saturating_add((181_000 as Weight).saturating_mul(s as Weight)) + (52_999_000 as Weight) + // Standard Error: 14_000 + .saturating_add((47_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 26_000 + .saturating_add((55_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn vest_unlocked(l: u32, s: u32, ) -> Weight { - (60_205_000 as Weight) - // Standard Error: 1_000 - .saturating_add((136_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 5_000 - .saturating_add((102_000 as Weight).saturating_mul(s as Weight)) + (48_366_000 as Weight) + // Standard Error: 11_000 + .saturating_add((91_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 20_000 + .saturating_add((68_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn vest_other_locked(l: u32, s: u32, ) -> Weight { - (60_649_000 as Weight) - // Standard Error: 2_000 - .saturating_add((149_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 7_000 - .saturating_add((173_000 as Weight).saturating_mul(s as Weight)) + (36_586_000 as Weight) + // Standard Error: 134_000 + .saturating_add((390_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 240_000 + .saturating_add((428_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { - (59_567_000 as Weight) - // Standard Error: 2_000 - .saturating_add((148_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 6_000 - .saturating_add((84_000 as Weight).saturating_mul(s as Weight)) + (51_765_000 as Weight) + // Standard Error: 20_000 + .saturating_add((61_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 37_000 + .saturating_add((63_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn vested_transfer(l: u32, _s: u32, ) -> Weight { - (105_528_000 as Weight) - // Standard Error: 6_000 - .saturating_add((149_000 as Weight).saturating_mul(l as Weight)) + fn vested_transfer(l: u32, s: u32, ) -> Weight { + (74_966_000 as Weight) + // Standard Error: 38_000 + .saturating_add((51_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 69_000 + .saturating_add((84_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn force_vested_transfer(l: u32, _s: u32, ) -> Weight { - (106_006_000 as Weight) - // Standard Error: 9_000 - .saturating_add((111_000 as Weight).saturating_mul(l as Weight)) + fn force_vested_transfer(l: u32, s: u32, ) -> Weight { + (65_685_000 as Weight) + // Standard Error: 38_000 + .saturating_add((205_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 68_000 + .saturating_add((206_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - (73_233_000 as Weight) - // Standard Error: 1_000 - .saturating_add((147_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 5_000 - .saturating_add((143_000 as Weight).saturating_mul(s as Weight)) + (52_923_000 as Weight) + // Standard Error: 19_000 + .saturating_add((97_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 36_000 + .saturating_add((70_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - (73_772_000 as Weight) - // Standard Error: 2_000 - .saturating_add((145_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 6_000 - .saturating_add((119_000 as Weight).saturating_mul(s as Weight)) + (49_726_000 as Weight) + // Standard Error: 24_000 + .saturating_add((146_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 44_000 + .saturating_add((121_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } @@ -130,70 +129,74 @@ impl WeightInfo for SubstrateWeight { // For backwards compatibility and tests impl WeightInfo for () { fn vest_locked(l: u32, s: u32, ) -> Weight { - (60_524_000 as Weight) - // Standard Error: 1_000 - .saturating_add((158_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 5_000 - .saturating_add((181_000 as Weight).saturating_mul(s as Weight)) + (52_999_000 as Weight) + // Standard Error: 14_000 + .saturating_add((47_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 26_000 + .saturating_add((55_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn vest_unlocked(l: u32, s: u32, ) -> Weight { - (60_205_000 as Weight) - // Standard Error: 1_000 - .saturating_add((136_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 5_000 - .saturating_add((102_000 as Weight).saturating_mul(s as Weight)) + (48_366_000 as Weight) + // Standard Error: 11_000 + .saturating_add((91_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 20_000 + .saturating_add((68_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn vest_other_locked(l: u32, s: u32, ) -> Weight { - (60_649_000 as Weight) - // Standard Error: 2_000 - .saturating_add((149_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 7_000 - .saturating_add((173_000 as Weight).saturating_mul(s as Weight)) + (36_586_000 as Weight) + // Standard Error: 134_000 + .saturating_add((390_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 240_000 + .saturating_add((428_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { - (59_567_000 as Weight) - // Standard Error: 2_000 - .saturating_add((148_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 6_000 - .saturating_add((84_000 as Weight).saturating_mul(s as Weight)) + (51_765_000 as Weight) + // Standard Error: 20_000 + .saturating_add((61_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 37_000 + .saturating_add((63_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn vested_transfer(l: u32, _s: u32, ) -> Weight { - (105_528_000 as Weight) - // Standard Error: 6_000 - .saturating_add((149_000 as Weight).saturating_mul(l as Weight)) + fn vested_transfer(l: u32, s: u32, ) -> Weight { + (74_966_000 as Weight) + // Standard Error: 38_000 + .saturating_add((51_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 69_000 + .saturating_add((84_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn force_vested_transfer(l: u32, _s: u32, ) -> Weight { - (106_006_000 as Weight) - // Standard Error: 9_000 - .saturating_add((111_000 as Weight).saturating_mul(l as Weight)) + fn force_vested_transfer(l: u32, s: u32, ) -> Weight { + (65_685_000 as Weight) + // Standard Error: 38_000 + .saturating_add((205_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 68_000 + .saturating_add((206_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - (73_233_000 as Weight) - // Standard Error: 1_000 - .saturating_add((147_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 5_000 - .saturating_add((143_000 as Weight).saturating_mul(s as Weight)) + (52_923_000 as Weight) + // Standard Error: 19_000 + .saturating_add((97_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 36_000 + .saturating_add((70_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - (73_772_000 as Weight) - // Standard Error: 2_000 - .saturating_add((145_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 6_000 - .saturating_add((119_000 as Weight).saturating_mul(s as Weight)) + (49_726_000 as Weight) + // Standard Error: 24_000 + .saturating_add((146_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 44_000 + .saturating_add((121_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } From 0b227c35165b9682381e68a8c575aeedcfcb80c4 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 6 Jul 2021 16:16:53 -0700 Subject: [PATCH 090/144] trivial --- frame/vesting/src/mock.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/frame/vesting/src/mock.rs b/frame/vesting/src/mock.rs index 52af34eee8864..d9d85cff91637 100644 --- a/frame/vesting/src/mock.rs +++ b/frame/vesting/src/mock.rs @@ -110,6 +110,7 @@ impl Default for ExtBuilder { Self { existential_deposit: 1, vesting_genesis_config: None } } } + impl ExtBuilder { pub fn existential_deposit(mut self, existential_deposit: u64) -> Self { self.existential_deposit = existential_deposit; From 0630003e30769c7f9712e7b562c4cf2dad3dabe5 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 6 Jul 2021 20:20:11 -0700 Subject: [PATCH 091/144] Fix benchmark asserts to handle non-multiple of 20 locked --- frame/vesting/src/benchmarking.rs | 65 ++++++---- frame/vesting/src/weights.rs | 196 +++++++++++++++--------------- 2 files changed, 136 insertions(+), 125 deletions(-) diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index b8e08fe7e8e19..66d1758b16b7a 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -32,6 +32,15 @@ const SEED: u32 = 0; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; +// Duration of schedules used in `add_vesting_schedules`. +fn schedule_duration() -> u32 { + if T::MinVestedTransfer::get() % 20u32.into() == Zero::zero() { + 20u32 + } else { + 21u32 + } +} + fn add_locks(who: &T::AccountId, n: u8) { for id in 0 .. n { let lock_id = [id; 8]; @@ -46,10 +55,9 @@ fn add_vesting_schedules( n: u32, ) -> Result, &'static str> { let min_transfer = T::MinVestedTransfer::get(); - let per_block_duration_20 = min_transfer.checked_div(&20u32.into()).unwrap(); - let locked = min_transfer; - let per_block = per_block_duration_20; + // If 20 does not evenly divide `min_transfer` the duration will be 21 blocks. + let per_block = min_transfer.checked_div(&20u32.into()).unwrap(); let starting_block = 1u32; let source: T::AccountId = account("source", 0, SEED); @@ -116,8 +124,8 @@ benchmarks! { add_locks::(&caller, l as u8); add_vesting_schedules::(caller_lookup, s)?; - // At block 21, everything is unvested. - System::::set_block_number(21u32.into()); + // At block 22, everything is guaranteed unlocked. + System::::set_block_number(22u32.into()); assert_eq!( Vesting::::vesting_balance(&caller), Some(BalanceOf::::zero()), @@ -171,8 +179,9 @@ benchmarks! { add_locks::(&other, l as u8); add_vesting_schedules::(other_lookup.clone(), s)?; - // At block 21, everything is unvested. - System::::set_block_number(21u32.into()); + // At block 22, everything is guaranteed unlocked. + System::::set_block_number(22u32.into()); + assert_eq!( Vesting::::vesting_balance(&other), Some(BalanceOf::::zero()), @@ -205,12 +214,12 @@ benchmarks! { let mut expected_balance = add_vesting_schedules::(target_lookup.clone(), s)?; let transfer_amount = T::MinVestedTransfer::get(); - let per_block_duration_20 = transfer_amount.checked_div(&20u32.into()).unwrap(); + let per_block = transfer_amount.checked_div(&20u32.into()).unwrap(); expected_balance += transfer_amount; let vesting_schedule = VestingInfo::new::( transfer_amount, - per_block_duration_20, + per_block, 1u32.into(), ); }: _(RawOrigin::Signed(caller), target_lookup, vesting_schedule) @@ -243,12 +252,12 @@ benchmarks! { let mut expected_balance = add_vesting_schedules::(target_lookup.clone(), s)?; let transfer_amount = T::MinVestedTransfer::get(); - let per_block_duration_20 = transfer_amount.checked_div(&20u32.into()).unwrap(); + let per_block = transfer_amount.checked_div(&20u32.into()).unwrap(); expected_balance += transfer_amount; let vesting_schedule = VestingInfo::new::( transfer_amount, - per_block_duration_20, + per_block, 1u32.into(), ); }: _(RawOrigin::Root, source_lookup, target_lookup, vesting_schedule) @@ -277,7 +286,6 @@ benchmarks! { let expected_balance = add_vesting_schedules::(caller_lookup.clone(), s)?; // Track the value of the schedules added by `add_vesting_schedules`. let transfer_amount = T::MinVestedTransfer::get(); - let per_block_duration_20 = transfer_amount.checked_div(&20u32.into()).unwrap(); // Schedules are not vesting at block 0. assert_eq!(System::::block_number(), T::BlockNumber::zero()); @@ -293,9 +301,10 @@ benchmarks! { ); }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, s - 1) verify { + let expected_locked = transfer_amount * 2u32.into(); let expected_schedule = VestingInfo::new::( - T::MinVestedTransfer::get() * 2u32.into(), - per_block_duration_20 * 2u32.into(), + expected_locked, + expected_locked.checked_div(&schedule_duration::().into()).unwrap(), 1u32.into(), ); let expected_index = (s - 2) as usize; @@ -327,15 +336,17 @@ benchmarks! { // Give target other locks. add_locks::(&caller, l as u8); // Add max vesting schedules. - let mut expected_balance = add_vesting_schedules::(caller_lookup.clone(), s)?; + let total_transferred = add_vesting_schedules::(caller_lookup.clone(), s)?; // Values of the schedules added by `add_vesting_schedules`. let transfer_amount = T::MinVestedTransfer::get(); - let per_block_duration_20 = transfer_amount.checked_div(&20u32.into()).unwrap(); - - // Go to half way through all the schedules duration. (They all start at 1, and have a duration of 20). - System::::set_block_number(11u32.into()); - // We expect half the original locked balance. - expected_balance = expected_balance / 2u32.into(); + let per_block = transfer_amount.checked_div(&20u32.into()).unwrap(); + + // Go to about half way through all the schedules duration. (They all start at 1, and have a duration of 20 or 21). + let half_way = 10u32; + System::::set_block_number((half_way + 1).into()); + // We expect half the original locked balance (+ any remainder that vests on the last block). + let expected_balance = total_transferred - per_block * s.into() * half_way.into(); + let transferrable = total_transferred - expected_balance; assert_eq!( Vesting::::vesting_balance(&caller), Some(expected_balance), @@ -347,12 +358,16 @@ benchmarks! { "There should be exactly max vesting schedules" ); // The balance is not actually transferable because it has not been unlocked. - assert!(T::Currency::transfer(&caller, &test_dest, expected_balance, ExistenceRequirement::AllowDeath).is_err()); + assert!(T::Currency::transfer(&caller, &test_dest, transferrable, ExistenceRequirement::AllowDeath).is_err()); }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, s - 1) verify { + let duration = if schedule_duration::() == 20u32 { 10u32 } else { 11u32 }; + let total_locked = transfer_amount * 2u32.into(); + let unlocked_so_far = per_block * half_way.into() * 2u32.into(); + let remaining_locked = total_locked - unlocked_so_far; let expected_schedule = VestingInfo::new::( - transfer_amount, - per_block_duration_20 * 2u32.into(), + remaining_locked, + remaining_locked / duration.into(), 11u32.into(), ); let expected_index = (s - 2) as usize; @@ -377,7 +392,7 @@ benchmarks! { ); // Since merge unlocks all schedules we can now transfer the balance. assert_ok!( - T::Currency::transfer(&caller, &test_dest, expected_balance, ExistenceRequirement::AllowDeath) + T::Currency::transfer(&caller, &test_dest, transferrable, ExistenceRequirement::AllowDeath) ); } } diff --git a/frame/vesting/src/weights.rs b/frame/vesting/src/weights.rs index 15ce6f4c79ad2..5af21a68eff4a 100644 --- a/frame/vesting/src/weights.rs +++ b/frame/vesting/src/weights.rs @@ -18,11 +18,11 @@ //! Autogenerated weights for pallet_vesting //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-07-06, STEPS: `[]`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2021-07-07, STEPS: `[]`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 128 // Executed Command: -// target/release/substrate +// target/debug/substrate // benchmark // --pallet=pallet-vesting // --extrinsic=* @@ -30,11 +30,13 @@ // --output=./frame/vesting/src/weights.rs // --template=.maintain/frame-weight-template.hbs - #![allow(unused_parens)] #![allow(unused_imports)] -use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use frame_support::{ + traits::Get, + weights::{constants::RocksDbWeight, Weight}, +}; use sp_std::marker::PhantomData; /// Weight functions needed for pallet_vesting. @@ -52,75 +54,72 @@ pub trait WeightInfo { /// Weights for pallet_vesting using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - fn vest_locked(l: u32, s: u32, ) -> Weight { - (52_999_000 as Weight) - // Standard Error: 14_000 - .saturating_add((47_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 26_000 - .saturating_add((55_000 as Weight).saturating_mul(s as Weight)) + fn vest_locked(_l: u32, _s: u32) -> Weight { + (816_920_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } - fn vest_unlocked(l: u32, s: u32, ) -> Weight { - (48_366_000 as Weight) - // Standard Error: 11_000 - .saturating_add((91_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 20_000 - .saturating_add((68_000 as Weight).saturating_mul(s as Weight)) + + fn vest_unlocked(l: u32, s: u32) -> Weight { + (384_483_000 as Weight) + // Standard Error: 1_818_000 + .saturating_add((4_972_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 3_248_000 + .saturating_add((4_990_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } - fn vest_other_locked(l: u32, s: u32, ) -> Weight { - (36_586_000 as Weight) - // Standard Error: 134_000 - .saturating_add((390_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 240_000 - .saturating_add((428_000 as Weight).saturating_mul(s as Weight)) + + fn vest_other_locked(l: u32, _s: u32) -> Weight { + (699_604_000 as Weight) + // Standard Error: 2_051_000 + .saturating_add((2_732_000 as Weight).saturating_mul(l as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { - (51_765_000 as Weight) - // Standard Error: 20_000 - .saturating_add((61_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 37_000 - .saturating_add((63_000 as Weight).saturating_mul(s as Weight)) + + fn vest_other_unlocked(l: u32, s: u32) -> Weight { + (649_516_000 as Weight) + // Standard Error: 2_626_000 + .saturating_add((3_369_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 4_693_000 + .saturating_add((443_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn vested_transfer(l: u32, s: u32, ) -> Weight { - (74_966_000 as Weight) - // Standard Error: 38_000 - .saturating_add((51_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 69_000 - .saturating_add((84_000 as Weight).saturating_mul(s as Weight)) + + fn vested_transfer(l: u32, _s: u32) -> Weight { + (1_112_831_000 as Weight) + // Standard Error: 1_170_000 + .saturating_add((1_761_000 as Weight).saturating_mul(l as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn force_vested_transfer(l: u32, s: u32, ) -> Weight { - (65_685_000 as Weight) - // Standard Error: 38_000 - .saturating_add((205_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 68_000 - .saturating_add((206_000 as Weight).saturating_mul(s as Weight)) + + fn force_vested_transfer(l: u32, s: u32) -> Weight { + (973_750_000 as Weight) + // Standard Error: 1_962_000 + .saturating_add((3_043_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 3_506_000 + .saturating_add((316_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } - fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - (52_923_000 as Weight) - // Standard Error: 19_000 - .saturating_add((97_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 36_000 - .saturating_add((70_000 as Weight).saturating_mul(s as Weight)) + + fn not_unlocking_merge_schedules(l: u32, s: u32) -> Weight { + (670_637_000 as Weight) + // Standard Error: 2_507_000 + .saturating_add((4_049_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 4_675_000 + .saturating_add((784_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - (49_726_000 as Weight) - // Standard Error: 24_000 - .saturating_add((146_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 44_000 - .saturating_add((121_000 as Weight).saturating_mul(s as Weight)) + + fn unlocking_merge_schedules(l: u32, _s: u32) -> Weight { + (728_296_000 as Weight) + // Standard Error: 3_129_000 + .saturating_add((2_908_000 as Weight).saturating_mul(l as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } @@ -128,75 +127,72 @@ impl WeightInfo for SubstrateWeight { // For backwards compatibility and tests impl WeightInfo for () { - fn vest_locked(l: u32, s: u32, ) -> Weight { - (52_999_000 as Weight) - // Standard Error: 14_000 - .saturating_add((47_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 26_000 - .saturating_add((55_000 as Weight).saturating_mul(s as Weight)) + fn vest_locked(_l: u32, _s: u32) -> Weight { + (816_920_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } - fn vest_unlocked(l: u32, s: u32, ) -> Weight { - (48_366_000 as Weight) - // Standard Error: 11_000 - .saturating_add((91_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 20_000 - .saturating_add((68_000 as Weight).saturating_mul(s as Weight)) + + fn vest_unlocked(l: u32, s: u32) -> Weight { + (384_483_000 as Weight) + // Standard Error: 1_818_000 + .saturating_add((4_972_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 3_248_000 + .saturating_add((4_990_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } - fn vest_other_locked(l: u32, s: u32, ) -> Weight { - (36_586_000 as Weight) - // Standard Error: 134_000 - .saturating_add((390_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 240_000 - .saturating_add((428_000 as Weight).saturating_mul(s as Weight)) + + fn vest_other_locked(l: u32, _s: u32) -> Weight { + (699_604_000 as Weight) + // Standard Error: 2_051_000 + .saturating_add((2_732_000 as Weight).saturating_mul(l as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { - (51_765_000 as Weight) - // Standard Error: 20_000 - .saturating_add((61_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 37_000 - .saturating_add((63_000 as Weight).saturating_mul(s as Weight)) + + fn vest_other_unlocked(l: u32, s: u32) -> Weight { + (649_516_000 as Weight) + // Standard Error: 2_626_000 + .saturating_add((3_369_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 4_693_000 + .saturating_add((443_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn vested_transfer(l: u32, s: u32, ) -> Weight { - (74_966_000 as Weight) - // Standard Error: 38_000 - .saturating_add((51_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 69_000 - .saturating_add((84_000 as Weight).saturating_mul(s as Weight)) + + fn vested_transfer(l: u32, _s: u32) -> Weight { + (1_112_831_000 as Weight) + // Standard Error: 1_170_000 + .saturating_add((1_761_000 as Weight).saturating_mul(l as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn force_vested_transfer(l: u32, s: u32, ) -> Weight { - (65_685_000 as Weight) - // Standard Error: 38_000 - .saturating_add((205_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 68_000 - .saturating_add((206_000 as Weight).saturating_mul(s as Weight)) + + fn force_vested_transfer(l: u32, s: u32) -> Weight { + (973_750_000 as Weight) + // Standard Error: 1_962_000 + .saturating_add((3_043_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 3_506_000 + .saturating_add((316_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } - fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - (52_923_000 as Weight) - // Standard Error: 19_000 - .saturating_add((97_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 36_000 - .saturating_add((70_000 as Weight).saturating_mul(s as Weight)) + + fn not_unlocking_merge_schedules(l: u32, s: u32) -> Weight { + (670_637_000 as Weight) + // Standard Error: 2_507_000 + .saturating_add((4_049_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 4_675_000 + .saturating_add((784_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - (49_726_000 as Weight) - // Standard Error: 24_000 - .saturating_add((146_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 44_000 - .saturating_add((121_000 as Weight).saturating_mul(s as Weight)) + + fn unlocking_merge_schedules(l: u32, _s: u32) -> Weight { + (728_296_000 as Weight) + // Standard Error: 3_129_000 + .saturating_add((2_908_000 as Weight).saturating_mul(l as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } From 03bbd593141d5b0bc805a33c8b3c22ff4bf97f60 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 6 Jul 2021 20:27:25 -0700 Subject: [PATCH 092/144] Add generate_storage_info --- frame/vesting/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index c6e4549a92fde..0a38cfecd184a 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -54,7 +54,7 @@ mod vesting_info; pub mod weights; -use codec::{Decode, Encode}; +use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{ ensure, pallet_prelude::*, @@ -86,7 +86,7 @@ const LOG_TARGET: &'static str = "runtime::vesting"; // A value placed in storage that represents the current version of the Vesting storage. // This value is used by `on_runtime_upgrade` to determine whether we run storage migration logic. -#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug)] +#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, MaxEncodedLen)] enum Releases { V0, V1, @@ -191,6 +191,7 @@ pub mod pallet { #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] + #[pallet::generate_storage_info] pub struct Pallet(_); #[pallet::genesis_config] From 22a1cae39d8d6b06f7c67d31db723b00acf4e798 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 6 Jul 2021 21:00:17 -0700 Subject: [PATCH 093/144] migration :facepalm --- frame/vesting/src/lib.rs | 10 ++-------- frame/vesting/src/migrations.rs | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 0a38cfecd184a..f695d5b6abc24 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -456,15 +456,9 @@ impl Pallet { let locked = schedule1 .locked_at::(now) .saturating_add(schedule2.locked_at::(now)); - // This shouldn't happen because we know at least one ending block is greater than now. + // This shouldn't happen because we know at least one ending block is greater than now, + // thus at least a schedule a some locked balance. debug_assert!(!locked.is_zero(), "merge_vesting_info validation checks failed to catch a locked of 0"); - if locked.is_zero() { - log::warn!( - target: LOG_TARGET, - "merge_vesting_info validation checks failed to catch a locked of 0" - ); - return Ok(None); - } let ending_block = schedule1_ending_block.max(schedule2_ending_block); let starting_block = now.max(schedule1.starting_block()).max(schedule2.starting_block()); diff --git a/frame/vesting/src/migrations.rs b/frame/vesting/src/migrations.rs index 4faa6158be1ee..54a6d5102a7ca 100644 --- a/frame/vesting/src/migrations.rs +++ b/frame/vesting/src/migrations.rs @@ -59,7 +59,7 @@ pub(crate) mod v1 { for (_key, schedules) in Vesting::::iter() { // Assert the new bound vec respects size. - assert!(schedules.len() == 0, "A bounded vec with incorrect count of items was created."); + assert!(schedules.len() == 1, "A bounded vec with incorrect count of items was created."); for s in schedules { // Check for infinite schedules. From ff8c2173994489752ad8a1a125baf9456e8a339d Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 6 Jul 2021 21:08:15 -0700 Subject: [PATCH 094/144] Remove per_block 0 logic --- frame/vesting/src/migrations.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/frame/vesting/src/migrations.rs b/frame/vesting/src/migrations.rs index 54a6d5102a7ca..88744d92adcb5 100644 --- a/frame/vesting/src/migrations.rs +++ b/frame/vesting/src/migrations.rs @@ -62,8 +62,6 @@ pub(crate) mod v1 { assert!(schedules.len() == 1, "A bounded vec with incorrect count of items was created."); for s in schedules { - // Check for infinite schedules. - assert!(s.per_block() > Zero::zero(), "A schedule with per_block of 0 exists"); // It is ok if this does not pass, but ideally pre-existing schedules would pass // this validation logic so we can be more confident about edge cases. debug_assert!( From ee82bf5c8b96b64155295f5cf73a39e6cc8a0942 Mon Sep 17 00:00:00 2001 From: Zeke Mostov <32168567+emostov@users.noreply.github.com> Date: Tue, 6 Jul 2021 21:15:04 -0700 Subject: [PATCH 095/144] Update frame/vesting/src/lib.rs --- frame/vesting/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index f695d5b6abc24..7147c578bf378 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -610,7 +610,7 @@ impl Pallet { let schedule2 = *schedules.get(idx2).ok_or(Error::::ScheduleIndexOutOfBounds)?; // The length of `schedules` decreases by 2 here since we filter out 2 schedules. - // Thus we know below that we can pushed the new merged schedule without error (assuming + // Thus we know below that we can push the new merged schedule without error (assuming // initial state was valid). let (mut schedules, mut locked_now) = Self::report_schedule_updates(schedules.to_vec(), action); From 5c02c57a84b71190ff304f6f2f70e11232ee7827 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 6 Jul 2021 21:46:06 -0700 Subject: [PATCH 096/144] Do not check for ending later than greatest block --- Cargo.lock | 1 - frame/vesting/Cargo.toml | 1 - frame/vesting/src/lib.rs | 4 ---- frame/vesting/src/tests.rs | 33 +++++-------------------------- frame/vesting/src/vesting_info.rs | 20 +++++-------------- 5 files changed, 10 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bd6f57149ee80..9bb478d579077 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5700,7 +5700,6 @@ dependencies = [ "frame-support", "frame-system", "hex-literal", - "log", "pallet-balances", "parity-scale-codec", "sp-core", diff --git a/frame/vesting/Cargo.toml b/frame/vesting/Cargo.toml index da5dcd5945aaf..25890fea038de 100644 --- a/frame/vesting/Cargo.toml +++ b/frame/vesting/Cargo.toml @@ -20,7 +20,6 @@ sp-runtime = { version = "3.0.0", default-features = false, path = "../../primit frame-support = { version = "3.0.0", default-features = false, path = "../support" } frame-system = { version = "3.0.0", default-features = false, path = "../system" } frame-benchmarking = { version = "3.1.0", default-features = false, path = "../benchmarking", optional = true } -log = { version = "0.4.0", default-features = false } [dev-dependencies] sp-io = { version = "3.0.0", path = "../../primitives/io" } diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index f695d5b6abc24..b3be214a979c2 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -82,7 +82,6 @@ type MaxLocksOf = <::Currency as LockableCurrency<::AccountId>>::MaxLocks; const VESTING_ID: LockIdentifier = *b"vesting "; -const LOG_TARGET: &'static str = "runtime::vesting"; // A value placed in storage that represents the current version of the Vesting storage. // This value is used by `on_runtime_upgrade` to determine whether we run storage migration logic. @@ -268,9 +267,6 @@ pub mod pallet { ScheduleIndexOutOfBounds, /// Failed to create a new schedule because some parameter was invalid. e.g. `locked` was 0. InvalidScheduleParams, - /// A schedule contained a `per_block` of 0 or `locked / per_block > BlockNumber::max_value()`, - /// thus rendering it unable to ever fully unlock funds. - InfiniteSchedule, } #[pallet::call] diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index 5ce6e43d4ee49..613c757df18d8 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -406,18 +406,10 @@ fn vested_transfer_correctly_fails() { let schedule_per_block_0 = VestingInfo::new::(::MinVestedTransfer::get(), 0, 10); assert_noop!( Vesting::vested_transfer(Some(13).into(), 4, schedule_per_block_0), - Error::::InfiniteSchedule, + Error::::InvalidScheduleParams, ); - // `locked / per_block + starting_block > BlockNumber::max_value()` - let start = 10u64; - let schedule_end_gt_max_blocknumber = VestingInfo::new::(u64::MAX - start + 1, 1, start); - assert_noop!( - Vesting::vested_transfer(Some(3).into(), 4, schedule_end_gt_max_blocknumber), - Error::::InfiniteSchedule, - ); - - // `locked` is 0 + // `locked` is 0. let schedule_locked_0 = VestingInfo::new::(0, 1, 10); assert_noop!( Vesting::vested_transfer(Some(3).into(), 4, schedule_locked_0), @@ -564,15 +556,7 @@ fn force_vested_transfer_correctly_fails() { VestingInfo::new::(::MinVestedTransfer::get(), 0, 10); assert_noop!( Vesting::force_vested_transfer(RawOrigin::Root.into(), 13, 4, schedule_per_block_0), - Error::::InfiniteSchedule, - ); - - // `locked / per_block + starting_block > BlockNumber::max_value()` - let start = 10u64; - let schedule_end_gt_max_blocknumber = VestingInfo::new::(u64::MAX - start + 1, 1, start); - assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_end_gt_max_blocknumber), - Error::::InfiniteSchedule, + Error::::InvalidScheduleParams, ); // `locked` is 0. @@ -1136,18 +1120,11 @@ fn vesting_info_validate_works() { // `per_block` cannot be 0. match VestingInfo::new::(min_transfer + 1, 0u64, 10u64).validate::() { - Err(Error::::InfiniteSchedule) => (), - _ => panic!(), - } - - // `locked / per_block + starting_block` cannot be bigger than BlockNumber::max_value`. - let start = 10; - match VestingInfo::new::(u64::MAX - start + 1, 1u64, start).validate::() { - Err(Error::::InfiniteSchedule) => (), + Err(Error::::InvalidScheduleParams) => (), _ => panic!(), } - // With valid inputs it does not error and does not modify the inputs. + // With valid inputs it does not error. let valid = VestingInfo::new::(min_transfer, 1u64, 10u64); assert_ok!(valid.validate::()); } diff --git a/frame/vesting/src/vesting_info.rs b/frame/vesting/src/vesting_info.rs index ce76ef72f5e71..6d1646131fe15 100644 --- a/frame/vesting/src/vesting_info.rs +++ b/frame/vesting/src/vesting_info.rs @@ -50,17 +50,10 @@ where pub fn validate, T: Config>( &self, ) -> Result<(), Error> { - ensure!(!self.locked.is_zero(), Error::::InvalidScheduleParams); - - let max_block = BlockNumberToBalance::convert(BlockNumber::max_value()); - let starting_block = BlockNumberToBalance::convert(self.starting_block()); - match self.locked.checked_div(&self.raw_per_block()) { - None => return Err(Error::::InfiniteSchedule), // `per_block` is 0 - Some(duration) => { - let end = duration.saturating_add(starting_block); - ensure!(end < max_block, Error::::InfiniteSchedule) - } - }; + ensure!( + !self.locked.is_zero() && !self.raw_per_block().is_zero(), + Error::::InvalidScheduleParams + ); Ok(()) } @@ -70,22 +63,19 @@ where self.locked } - /// Amount that gets unlocked every block after `starting_block`. Corrects for `per_block` of 0. - /// We dont let `per_block` be less than one, or else the vesting will never end. + /// We don't let `per_block` be less than one, or else the vesting will never end. /// This should be used whenever accessing `per_block` unless explicitly checking for 0 values. pub fn per_block(&self) -> Balance { self.per_block.max(One::one()) } - /// Get the unmodified `per_block`. Generally should not be used, but is is useful for /// validating `per_block`. pub fn raw_per_block(&self) -> Balance { self.per_block } - /// Starting block for unlocking(vesting). pub fn starting_block(&self) -> BlockNumber { self.starting_block From af400f3a4b43b69dc48428260ab72c21523b823e Mon Sep 17 00:00:00 2001 From: Zeke Mostov <32168567+emostov@users.noreply.github.com> Date: Tue, 6 Jul 2021 22:12:22 -0700 Subject: [PATCH 097/144] Apply suggestions from code review --- frame/vesting/src/vesting_info.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/vesting/src/vesting_info.rs b/frame/vesting/src/vesting_info.rs index 6d1646131fe15..70978ebab3aa4 100644 --- a/frame/vesting/src/vesting_info.rs +++ b/frame/vesting/src/vesting_info.rs @@ -64,13 +64,13 @@ where } /// Amount that gets unlocked every block after `starting_block`. Corrects for `per_block` of 0. - /// We don't let `per_block` be less than one, or else the vesting will never end. + /// We don't let `per_block` be less than 1, or else the vesting will never end. /// This should be used whenever accessing `per_block` unless explicitly checking for 0 values. pub fn per_block(&self) -> Balance { self.per_block.max(One::one()) } - /// Get the unmodified `per_block`. Generally should not be used, but is is useful for + /// Get the unmodified `per_block`. Generally should not be used, but is useful for /// validating `per_block`. pub fn raw_per_block(&self) -> Balance { self.per_block From 6f2a618111413a6762b648f51ee27dbc071c0dea Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 6 Jul 2021 23:09:44 -0700 Subject: [PATCH 098/144] Benchmarks: simplify vesting schedule creation --- frame/vesting/src/benchmarking.rs | 49 +++++++++---------------------- 1 file changed, 14 insertions(+), 35 deletions(-) diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index 66d1758b16b7a..600d76322bd67 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -22,7 +22,7 @@ use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite, whitelisted_caller}; use frame_support::assert_ok; use frame_system::{Pallet as System, RawOrigin}; -use sp_runtime::traits::{Bounded, CheckedDiv}; +use sp_runtime::traits::{Bounded, CheckedDiv, CheckedMul}; use super::*; use crate::Pallet as Vesting; @@ -32,15 +32,6 @@ const SEED: u32 = 0; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; -// Duration of schedules used in `add_vesting_schedules`. -fn schedule_duration() -> u32 { - if T::MinVestedTransfer::get() % 20u32.into() == Zero::zero() { - 20u32 - } else { - 21u32 - } -} - fn add_locks(who: &T::AccountId, n: u8) { for id in 0 .. n { let lock_id = [id; 8]; @@ -55,9 +46,9 @@ fn add_vesting_schedules( n: u32, ) -> Result, &'static str> { let min_transfer = T::MinVestedTransfer::get(); - let locked = min_transfer; - // If 20 does not evenly divide `min_transfer` the duration will be 21 blocks. - let per_block = min_transfer.checked_div(&20u32.into()).unwrap(); + let locked = min_transfer.checked_mul(&20u32.into()).unwrap(); + // Schedule has a duration of 20. + let per_block = min_transfer; let starting_block = 1u32; let source: T::AccountId = account("source", 0, SEED); @@ -179,8 +170,8 @@ benchmarks! { add_locks::(&other, l as u8); add_vesting_schedules::(other_lookup.clone(), s)?; - // At block 22, everything is guaranteed unlocked. - System::::set_block_number(22u32.into()); + // At block 21 everything is unlocked. + System::::set_block_number(21u32.into()); assert_eq!( Vesting::::vesting_balance(&other), @@ -284,8 +275,6 @@ benchmarks! { add_locks::(&caller, l as u8); // Add max vesting schedules. let expected_balance = add_vesting_schedules::(caller_lookup.clone(), s)?; - // Track the value of the schedules added by `add_vesting_schedules`. - let transfer_amount = T::MinVestedTransfer::get(); // Schedules are not vesting at block 0. assert_eq!(System::::block_number(), T::BlockNumber::zero()); @@ -301,10 +290,9 @@ benchmarks! { ); }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, s - 1) verify { - let expected_locked = transfer_amount * 2u32.into(); let expected_schedule = VestingInfo::new::( - expected_locked, - expected_locked.checked_div(&schedule_duration::().into()).unwrap(), + T::MinVestedTransfer::get() * 20u32.into() * 2u32.into(), + T::MinVestedTransfer::get() * 2u32.into(), 1u32.into(), ); let expected_index = (s - 2) as usize; @@ -337,16 +325,11 @@ benchmarks! { add_locks::(&caller, l as u8); // Add max vesting schedules. let total_transferred = add_vesting_schedules::(caller_lookup.clone(), s)?; - // Values of the schedules added by `add_vesting_schedules`. - let transfer_amount = T::MinVestedTransfer::get(); - let per_block = transfer_amount.checked_div(&20u32.into()).unwrap(); // Go to about half way through all the schedules duration. (They all start at 1, and have a duration of 20 or 21). - let half_way = 10u32; - System::::set_block_number((half_way + 1).into()); + System::::set_block_number(11u32.into()); // We expect half the original locked balance (+ any remainder that vests on the last block). - let expected_balance = total_transferred - per_block * s.into() * half_way.into(); - let transferrable = total_transferred - expected_balance; + let expected_balance = total_transferred / 2u32.into(); assert_eq!( Vesting::::vesting_balance(&caller), Some(expected_balance), @@ -358,16 +341,12 @@ benchmarks! { "There should be exactly max vesting schedules" ); // The balance is not actually transferable because it has not been unlocked. - assert!(T::Currency::transfer(&caller, &test_dest, transferrable, ExistenceRequirement::AllowDeath).is_err()); + assert!(T::Currency::transfer(&caller, &test_dest, expected_balance, ExistenceRequirement::AllowDeath).is_err()); }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, s - 1) verify { - let duration = if schedule_duration::() == 20u32 { 10u32 } else { 11u32 }; - let total_locked = transfer_amount * 2u32.into(); - let unlocked_so_far = per_block * half_way.into() * 2u32.into(); - let remaining_locked = total_locked - unlocked_so_far; let expected_schedule = VestingInfo::new::( - remaining_locked, - remaining_locked / duration.into(), + T::MinVestedTransfer::get() * 2u32.into() * 10u32.into(), + T::MinVestedTransfer::get() * 2u32.into(), 11u32.into(), ); let expected_index = (s - 2) as usize; @@ -392,7 +371,7 @@ benchmarks! { ); // Since merge unlocks all schedules we can now transfer the balance. assert_ok!( - T::Currency::transfer(&caller, &test_dest, transferrable, ExistenceRequirement::AllowDeath) + T::Currency::transfer(&caller, &test_dest, expected_balance, ExistenceRequirement::AllowDeath) ); } } From b2109ff7b0171c4385b74324e4c69c634c37cde2 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Wed, 7 Jul 2021 12:46:34 -0700 Subject: [PATCH 099/144] Add log back for migration --- Cargo.lock | 1 + frame/vesting/Cargo.toml | 3 ++- frame/vesting/src/migrations.rs | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 9bb478d579077..bd6f57149ee80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5700,6 +5700,7 @@ dependencies = [ "frame-support", "frame-system", "hex-literal", + "log", "pallet-balances", "parity-scale-codec", "sp-core", diff --git a/frame/vesting/Cargo.toml b/frame/vesting/Cargo.toml index 25890fea038de..4446319afc202 100644 --- a/frame/vesting/Cargo.toml +++ b/frame/vesting/Cargo.toml @@ -20,6 +20,7 @@ sp-runtime = { version = "3.0.0", default-features = false, path = "../../primit frame-support = { version = "3.0.0", default-features = false, path = "../support" } frame-system = { version = "3.0.0", default-features = false, path = "../system" } frame-benchmarking = { version = "3.1.0", default-features = false, path = "../benchmarking", optional = true } +log = { version = "0.4.0", default-features = false, optional = true } [dev-dependencies] sp-io = { version = "3.0.0", path = "../../primitives/io" } @@ -38,4 +39,4 @@ std = [ "frame-system/std", ] runtime-benchmarks = ["frame-benchmarking"] -try-runtime = ["frame-support/try-runtime"] +try-runtime = ["frame-support/try-runtime", "log"] diff --git a/frame/vesting/src/migrations.rs b/frame/vesting/src/migrations.rs index 88744d92adcb5..7bee14d8f64fd 100644 --- a/frame/vesting/src/migrations.rs +++ b/frame/vesting/src/migrations.rs @@ -19,6 +19,8 @@ use super::*; +const LOG_TARGET: &'static str = "runtime::vesting"; + // Migration from single schedule to multiple schedules. pub(crate) mod v1 { use super::*; From cb57017b793b29fb00f03f1b994b3ee904508a7a Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Wed, 7 Jul 2021 20:37:29 -0700 Subject: [PATCH 100/144] Add note in ext docs explaining that all schedules will vest --- frame/vesting/src/lib.rs | 8 ++++++-- frame/vesting/src/migrations.rs | 6 ++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 122c5a19b1f31..061d1ca9d88f8 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -245,11 +245,11 @@ pub mod pallet { T::AccountId = "AccountId", BalanceOf = "Balance", T::BlockNumber = "BlockNumber" )] pub enum Event { - /// The amount vested has been updated. This could indicate more funds are available. The + /// The amount vested has been updated. This could indicate a change in funds available. The /// balance given is the amount which is left unvested (and thus locked). /// \[account, unvested\] VestingUpdated(T::AccountId, BalanceOf), - /// An \[account\] has become fully vested. No further vesting can happen. + /// An \[account\] has become fully vested. VestingCompleted(T::AccountId), } @@ -328,6 +328,8 @@ pub mod pallet { /// /// Emits `VestingCreated`. /// + /// NOTE: This will unlock all schedules through the current block. + /// /// # /// - `O(1)`. /// - DbWeight: 3 Reads, 3 Writes @@ -357,6 +359,8 @@ pub mod pallet { /// /// Emits `VestingCreated`. /// + /// NOTE: This will unlock all schedules through the current block. + /// /// # /// - `O(1)`. /// - DbWeight: 4 Reads, 4 Writes diff --git a/frame/vesting/src/migrations.rs b/frame/vesting/src/migrations.rs index 7bee14d8f64fd..ee9d9694700bb 100644 --- a/frame/vesting/src/migrations.rs +++ b/frame/vesting/src/migrations.rs @@ -19,8 +19,6 @@ use super::*; -const LOG_TARGET: &'static str = "runtime::vesting"; - // Migration from single schedule to multiple schedules. pub(crate) mod v1 { use super::*; @@ -30,7 +28,7 @@ pub(crate) mod v1 { assert!(StorageVersion::::get() == Releases::V0, "Storage version too high."); log::debug!( - target: LOG_TARGET, + target: "runtime::vesting", "Vesting storage version v1 PRE migration checks succesful!" ); @@ -74,7 +72,7 @@ pub(crate) mod v1 { } log::debug!( - target: LOG_TARGET, + target: "runtime::vesting", "Vesting storage version v1 POST migration checks succesful!" ); Ok(()) From 1c0b8db37bf9f9dff243737848d7118d13788dd6 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Wed, 7 Jul 2021 22:43:46 -0700 Subject: [PATCH 101/144] Make integrity test work --- frame/vesting/Cargo.toml | 2 +- frame/vesting/src/lib.rs | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/frame/vesting/Cargo.toml b/frame/vesting/Cargo.toml index 4446319afc202..51d88ef694cc4 100644 --- a/frame/vesting/Cargo.toml +++ b/frame/vesting/Cargo.toml @@ -21,9 +21,9 @@ frame-support = { version = "3.0.0", default-features = false, path = "../suppor frame-system = { version = "3.0.0", default-features = false, path = "../system" } frame-benchmarking = { version = "3.1.0", default-features = false, path = "../benchmarking", optional = true } log = { version = "0.4.0", default-features = false, optional = true } +sp-io = { version = "3.0.0", default-features = false, path = "../../primitives/io" } [dev-dependencies] -sp-io = { version = "3.0.0", path = "../../primitives/io" } sp-core = { version = "3.0.0", path = "../../primitives/core" } pallet-balances = { version = "3.0.0", path = "../balances" } sp-storage = { version = "3.0.0", path = "../../primitives/storage" } diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 061d1ca9d88f8..a1b957e95f65a 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -167,8 +167,15 @@ pub mod pallet { } fn integrity_test() { - assert!(T::MinVestedTransfer::get() >= ::Currency::minimum_balance()); - assert!(T::MaxVestingSchedules::get() > 0); + sp_std::if_std! { + sp_io::TestExternalities::new_empty().execute_with(|| + assert!( + T::MinVestedTransfer::get() >= ::Currency::minimum_balance() + && T::MaxVestingSchedules::get() > 0, + "`MinVestedTransfer` must >= existential deposit and `MaxVestingSchedules` cannot == 0" + ) + ); + } } } From dcdcda5697ab7d6de28584507f0cc632aad1d709 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 8 Jul 2021 11:33:06 -0700 Subject: [PATCH 102/144] Improve integrity test --- frame/vesting/src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index a1b957e95f65a..44ea4bbb2139b 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -167,12 +167,13 @@ pub mod pallet { } fn integrity_test() { + assert!(T::MaxVestingSchedules::get() > 0, "`MaxVestingSchedules` must ge greater than 0"); sp_std::if_std! { sp_io::TestExternalities::new_empty().execute_with(|| + // `Currency::minimum_balance()` needs `TestExternalities`. assert!( - T::MinVestedTransfer::get() >= ::Currency::minimum_balance() - && T::MaxVestingSchedules::get() > 0, - "`MinVestedTransfer` must >= existential deposit and `MaxVestingSchedules` cannot == 0" + T::MinVestedTransfer::get() >= ::Currency::minimum_balance(), + "`MinVestedTransfer` must greater than or equal to minimum balance for Currency." ) ); } From 2ea52d240de6b5bc7d3b19e8dbc7305db0da37bb Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 8 Jul 2021 12:11:43 -0700 Subject: [PATCH 103/144] Remove unnescary type param from VestingInfo::new --- frame/vesting/src/lib.rs | 12 +-- frame/vesting/src/tests.rs | 143 ++++++++++++++---------------- frame/vesting/src/vesting_info.rs | 17 ++-- 3 files changed, 85 insertions(+), 87 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 44ea4bbb2139b..a4a16cfc38b25 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -235,7 +235,7 @@ pub mod pallet { let locked = balance.saturating_sub(liquid); let length_as_balance = T::BlockNumberToBalance::convert(length); let per_block = locked / length_as_balance.max(sp_runtime::traits::One::one()); - let vesting_info = VestingInfo::new::(locked, per_block, begin); + let vesting_info = VestingInfo::new(locked, per_block, begin); vesting_info.validate::() .expect("Invalid VestingInfo params at genesis"); @@ -480,7 +480,7 @@ impl Pallet { // While `per_block` should never be 0, it is possible that the created schedule would end // after the highest possible block, which is a case we are ok with, but `validate` fails. - let schedule = VestingInfo::new::(locked, per_block, starting_block); + let schedule = VestingInfo::new(locked, per_block, starting_block); Ok(Some(schedule)) } @@ -687,9 +687,11 @@ where per_block: BalanceOf, starting_block: T::BlockNumber, ) -> DispatchResult { - if locked.is_zero() { return Ok(()); } + if locked.is_zero() { + return Ok(()); + } - let vesting_schedule = VestingInfo::new::(locked, per_block, starting_block); + let vesting_schedule = VestingInfo::new(locked, per_block, starting_block); // Check for `per_block` or `locked` of 0 and ending block greater than max block. vesting_schedule.validate::()?; @@ -718,7 +720,7 @@ where starting_block: T::BlockNumber, ) -> DispatchResult { // Check for `per_block` or `locked` of 0 and ending block greater than max block. - VestingInfo::new::(locked, per_block, starting_block) + VestingInfo::new(locked, per_block, starting_block) .validate::()?; ensure!( diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index 613c757df18d8..2d8dcf07368fb 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -49,17 +49,17 @@ fn check_vesting_status() { assert_eq!(user1_free_balance, ED * 10); // Account 1 has free balance assert_eq!(user2_free_balance, ED * 20); // Account 2 has free balance assert_eq!(user12_free_balance, ED * 10); // Account 12 has free balance - let user1_vesting_schedule = VestingInfo::new::( + let user1_vesting_schedule = VestingInfo::new( ED * 5, 128, // Vesting over 10 blocks 0, ); - let user2_vesting_schedule = VestingInfo::new::( + let user2_vesting_schedule = VestingInfo::new( ED * 20, ED, // Vesting over 20 blocks 10, ); - let user12_vesting_schedule = VestingInfo::new::( + let user12_vesting_schedule = VestingInfo::new( ED * 5, 64, // Vesting over 20 blocks 10, @@ -103,7 +103,7 @@ fn check_vesting_status() { fn check_vesting_status_for_multi_schedule_account() { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { assert_eq!(System::block_number(), 1); - let sched0 = VestingInfo::new::( + let sched0 = VestingInfo::new( ED * 20, ED, // Vesting over 20 blocks 10, @@ -117,7 +117,7 @@ fn check_vesting_status_for_multi_schedule_account() { assert_eq!(Vesting::vesting_balance(&2), Some(free_balance)); // Add a 2nd schedule that is already unlocking by block #1. - let sched1 = VestingInfo::new::( + let sched1 = VestingInfo::new( ED * 10, ED, // Vesting over 10 blocks 0, @@ -132,7 +132,7 @@ fn check_vesting_status_for_multi_schedule_account() { assert_eq!(Vesting::vesting_balance(&2), Some(free_balance - sched1.per_block())); // Add a 3rd schedule. - let sched2 = VestingInfo::new::( + let sched2 = VestingInfo::new( ED * 30, ED, // Vesting over 30 blocks 5, @@ -211,12 +211,12 @@ fn vested_balance_should_transfer() { #[test] fn vested_balance_should_transfer_with_multi_sched() { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - let sched0 = VestingInfo::new::(5*ED, 128, 0); - assert_ok!(Vesting::vested_transfer(Some(13).into(), 1, sched0)); - // Total of 2560 of locked for all the schedules. - assert_eq!(Vesting::vesting(&1).unwrap(), vec![sched0, sched0]); + let sched0 = VestingInfo::new(5 * ED, 128, 0); + assert_ok!(Vesting::vested_transfer(Some(13).into(), 1, sched0)); + // Total of 2560 of locked for all the schedules. + assert_eq!(Vesting::vesting(&1).unwrap(), vec![sched0, sched0]); - let user1_free_balance = Balances::free_balance(&1); + let user1_free_balance = Balances::free_balance(&1); assert_eq!(user1_free_balance, 3840); // Account 1 has free balance // Account 1 has only 256 units unlocking at block 1 (plus 1280 unvested) @@ -255,7 +255,7 @@ fn vested_balance_should_transfer_using_vest_other_with_multi_sched() { .existential_deposit(ED) .build() .execute_with(|| { - let sched0 = VestingInfo::new::(5*ED, 128, 0); + let sched0 = VestingInfo::new(5*ED, 128, 0); assert_ok!(Vesting::vested_transfer(Some(13).into(), 1, sched0)); // Total of 2560 of locked for all the schedules. assert_eq!(Vesting::vesting(&1).unwrap(), vec![sched0, sched0]); @@ -318,7 +318,7 @@ fn liquid_funds_should_transfer_with_delayed_vesting() { assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); // Account 12 has delayed vesting - let user12_vesting_schedule = VestingInfo::new::( + let user12_vesting_schedule = VestingInfo::new( 256 * 5, 64, // Vesting over 20 blocks 10, @@ -343,7 +343,7 @@ fn vested_transfer_works() { // Account 4 should not have any vesting yet. assert_eq!(Vesting::vesting(&4), None); // Make the schedule for the new transfer. - let new_vesting_schedule = VestingInfo::new::( + let new_vesting_schedule = VestingInfo::new( 256 * 5, 64, // Vesting over 20 blocks 10, @@ -387,7 +387,7 @@ fn vested_transfer_correctly_fails() { assert_eq!(user4_free_balance, ED * 40); // Account 2 should already have a vesting schedule. - let user2_vesting_schedule = VestingInfo::new::( + let user2_vesting_schedule = VestingInfo::new( ED * 20, ED, // Vesting over 20 blocks 10, @@ -396,21 +396,21 @@ fn vested_transfer_correctly_fails() { // Fails due to too low transfer amount. let new_vesting_schedule_too_low = - VestingInfo::new::(::MinVestedTransfer::get() - 1, 64, 10); + VestingInfo::new(::MinVestedTransfer::get() - 1, 64, 10); assert_noop!( Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule_too_low), Error::::AmountLow, ); // `per_block` is 0, which would result in a schedule with infinite duration. - let schedule_per_block_0 = VestingInfo::new::(::MinVestedTransfer::get(), 0, 10); + let schedule_per_block_0 = VestingInfo::new(::MinVestedTransfer::get(), 0, 10); assert_noop!( Vesting::vested_transfer(Some(13).into(), 4, schedule_per_block_0), Error::::InvalidScheduleParams, ); // `locked` is 0. - let schedule_locked_0 = VestingInfo::new::(0, 1, 10); + let schedule_locked_0 = VestingInfo::new(0, 1, 10); assert_noop!( Vesting::vested_transfer(Some(3).into(), 4, schedule_locked_0), Error::::AmountLow, @@ -430,7 +430,7 @@ fn vested_transfer_allows_max_schedules() { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { let mut user_4_free_balance = Balances::free_balance(&4); let max_schedules = ::MaxVestingSchedules::get(); - let sched = VestingInfo::new::( + let sched = VestingInfo::new( ::MinVestedTransfer::get(), 1, // Vest over 2 * 256 blocks. 10, @@ -477,7 +477,7 @@ fn force_vested_transfer_works() { // Account 4 should not have any vesting yet. assert_eq!(Vesting::vesting(&4), None); // Make the schedule for the new transfer. - let new_vesting_schedule = VestingInfo::new::( + let new_vesting_schedule = VestingInfo::new( ED * 5, 64, // Vesting over 20 blocks 10, @@ -531,7 +531,7 @@ fn force_vested_transfer_correctly_fails() { assert_eq!(user2_free_balance, ED * 20); assert_eq!(user4_free_balance, ED * 40); // Account 2 should already have a vesting schedule. - let user2_vesting_schedule = VestingInfo::new::( + let user2_vesting_schedule = VestingInfo::new( ED * 20, ED, // Vesting over 20 blocks 10, @@ -540,7 +540,7 @@ fn force_vested_transfer_correctly_fails() { // Too low transfer amount. let new_vesting_schedule_too_low = - VestingInfo::new::(::MinVestedTransfer::get() - 1, 64, 10); + VestingInfo::new(::MinVestedTransfer::get() - 1, 64, 10); assert_noop!( Vesting::force_vested_transfer( RawOrigin::Root.into(), @@ -553,14 +553,14 @@ fn force_vested_transfer_correctly_fails() { // `per_block` is 0. let schedule_per_block_0 = - VestingInfo::new::(::MinVestedTransfer::get(), 0, 10); + VestingInfo::new(::MinVestedTransfer::get(), 0, 10); assert_noop!( Vesting::force_vested_transfer(RawOrigin::Root.into(), 13, 4, schedule_per_block_0), Error::::InvalidScheduleParams, ); // `locked` is 0. - let schedule_locked_0 = VestingInfo::new::(0, 1, 10); + let schedule_locked_0 = VestingInfo::new(0, 1, 10); assert_noop!( Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), Error::::AmountLow, @@ -579,7 +579,7 @@ fn force_vested_transfer_allows_max_schedules() { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { let mut user_4_free_balance = Balances::free_balance(&4); let max_schedules = ::MaxVestingSchedules::get(); - let sched = VestingInfo::new::( + let sched = VestingInfo::new( ::MinVestedTransfer::get(), 1, // Vest over 2 * 256 blocks. 10, @@ -617,7 +617,7 @@ fn force_vested_transfer_allows_max_schedules() { fn merge_schedules_that_have_not_started() { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { // Account 2 should already have a vesting schedule. - let sched0 = VestingInfo::new::( + let sched0 = VestingInfo::new( ED * 20, ED, // Vest over 20 blocks. 10, @@ -633,7 +633,7 @@ fn merge_schedules_that_have_not_started() { // Since we merged identical schedules, the new schedule finishes at the same // time as the original, just with double the amount - let sched1 = VestingInfo::new::( + let sched1 = VestingInfo::new( sched0.locked() * 2, sched0.per_block() * 2, 10, // starts at the block the schedules are merged @@ -649,14 +649,14 @@ fn merge_ongoing_schedules() { // Merging two schedules that have started will vest both before merging. ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { // Account 2 should already have a vesting schedule. - let sched0 = VestingInfo::new::( + let sched0 = VestingInfo::new( ED * 20, ED, // Vest over 20 blocks 10, ); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); - let sched1 = VestingInfo::new::( + let sched1 = VestingInfo::new( ED * 10, ED, // Vest over 10 blocks sched0.starting_block() + 5, // Start at block 15 @@ -694,7 +694,7 @@ fn merge_ongoing_schedules() { // amount to unlock per block. let sched2_per_block = sched2_locked / sched2_duration; - let sched2 = VestingInfo::new::(sched2_locked, sched2_per_block, cur_block); + let sched2 = VestingInfo::new(sched2_locked, sched2_per_block, cur_block); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2]); // And just to double check, we assert the new merged schedule we be cleaned up as expected. @@ -708,17 +708,17 @@ fn merging_shifts_other_schedules_index() { // Schedules being merged are filtered out, schedules to the right of any merged // schedule shift left and the merged schedule is always last. ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - let sched0 = VestingInfo::new::( + let sched0 = VestingInfo::new( ED * 10, ED, // Vesting over 10 blocks 10, ); - let sched1 = VestingInfo::new::( + let sched1 = VestingInfo::new( ED * 11, ED, // Vesting over 11 blocks 11, ); - let sched2 = VestingInfo::new::( + let sched2 = VestingInfo::new( ED * 12, ED, // Vesting over 12 blocks 12, @@ -758,7 +758,7 @@ fn merging_shifts_other_schedules_index() { .max(sched0.ending_block_as_balance::().unwrap()); let sched3_duration = sched3_end - sched3_start; let sched3_per_block = sched3_locked / sched3_duration; - let sched3 = VestingInfo::new::(sched3_locked, sched3_per_block, sched3_start); + let sched3 = VestingInfo::new(sched3_locked, sched3_per_block, sched3_start); // The not touched schedule moves left and the new merged schedule is appended. assert_eq!(Vesting::vesting(&3).unwrap(), vec![sched1, sched3]); @@ -773,7 +773,7 @@ fn merge_ongoing_and_yet_to_be_started_schedules() { // started. ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { // Account 2 should already have a vesting schedule. - let sched0 = VestingInfo::new::( + let sched0 = VestingInfo::new( ED * 20, ED, // Vesting over 20 blocks 10, @@ -801,7 +801,7 @@ fn merge_ongoing_and_yet_to_be_started_schedules() { System::set_block_number(cur_block); // And add a schedule that starts after this block, but before sched0 finishes. - let sched1 = VestingInfo::new::( + let sched1 = VestingInfo::new( ED * 10, 1, // Vesting over 256 * 10 (2560) blocks cur_block + 1, @@ -827,7 +827,7 @@ fn merge_ongoing_and_yet_to_be_started_schedules() { let sched2_duration = sched2_end - sched2_start; let sched2_per_block = sched2_locked / sched2_duration; - let sched2 = VestingInfo::new::(sched2_locked, sched2_per_block, sched2_start); + let sched2 = VestingInfo::new(sched2_locked, sched2_per_block, sched2_start); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched2]); }); } @@ -838,14 +838,14 @@ fn merge_finished_and_ongoing_schedules() { // without any alterations, as the merged one. ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { // Account 2 should already have a vesting schedule. - let sched0 = VestingInfo::new::( + let sched0 = VestingInfo::new( ED * 20, ED, // Vesting over 20 blocks. 10, ); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); - let sched1 = VestingInfo::new::( + let sched1 = VestingInfo::new( ED * 40, ED, // Vesting over 40 blocks. 10, @@ -854,7 +854,7 @@ fn merge_finished_and_ongoing_schedules() { // Transfer a 3rd schedule, so we can demonstrate how schedule indices change. // (We are not merging this schedule.) - let sched2 = VestingInfo::new::( + let sched2 = VestingInfo::new( ED * 30, ED, // Vesting over 30 blocks. 10, @@ -900,7 +900,7 @@ fn merge_finishing_schedules_does_not_create_a_new_one() { // If both schedules finish by the current block we don't create new one ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { // Account 2 should already have a vesting schedule. - let sched0 = VestingInfo::new::( + let sched0 = VestingInfo::new( ED * 20, ED, // 20 block duration. 10, @@ -908,7 +908,7 @@ fn merge_finishing_schedules_does_not_create_a_new_one() { assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); // Create sched1 and transfer it to account 2. - let sched1 = VestingInfo::new::( + let sched1 = VestingInfo::new( ED * 30, ED, // 30 block duration. 10, @@ -941,14 +941,14 @@ fn merge_finishing_schedules_does_not_create_a_new_one() { fn merge_finished_and_yet_to_be_started_schedules() { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { // Account 2 should already have a vesting schedule. - let sched0 = VestingInfo::new::( + let sched0 = VestingInfo::new( ED * 20, ED, // 20 block duration. 10, // Ends at block 30 ); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); - let sched1 = VestingInfo::new::( + let sched1 = VestingInfo::new( ED * 30, ED * 2, // 30 block duration. 35, @@ -956,7 +956,7 @@ fn merge_finished_and_yet_to_be_started_schedules() { assert_ok!(Vesting::vested_transfer(Some(13).into(), 2, sched1)); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); - let sched2 = VestingInfo::new::( + let sched2 = VestingInfo::new( ED * 40, ED, // 40 block duration. 30, @@ -988,7 +988,7 @@ fn merge_finished_and_yet_to_be_started_schedules() { fn merge_schedules_throws_proper_errors() { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { // Account 2 should already have a vesting schedule. - let sched0 = VestingInfo::new::( + let sched0 = VestingInfo::new( ED * 20, ED, // 20 block duration. 10, @@ -1039,16 +1039,16 @@ fn generates_multiple_schedules_from_genesis_config() { .vesting_genesis_config(vesting_config) .build() .execute_with(|| { - let user1_sched1 = VestingInfo::new::(5 * ED, 128, 0u64); + let user1_sched1 = VestingInfo::new(5 * ED, 128, 0u64); assert_eq!(Vesting::vesting(&1).unwrap(), vec![user1_sched1]); - let user2_sched1 = VestingInfo::new::(1 * ED, 12, 10u64); - let user2_sched2 = VestingInfo::new::(2 * ED, 25, 10u64); + let user2_sched1 = VestingInfo::new(1 * ED, 12, 10u64); + let user2_sched2 = VestingInfo::new(2 * ED, 25, 10u64); assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_sched1, user2_sched2]); - let user12_sched1 = VestingInfo::new::(1 * ED, 12, 10u64); - let user12_sched2 = VestingInfo::new::(2 * ED, 25, 10u64); - let user12_sched3 = VestingInfo::new::(3 * ED, 38, 10u64); + let user12_sched1 = VestingInfo::new(1 * ED, 12, 10u64); + let user12_sched2 = VestingInfo::new(2 * ED, 25, 10u64); + let user12_sched3 = VestingInfo::new(3 * ED, 38, 10u64); assert_eq!( Vesting::vesting(&12).unwrap(), vec![user12_sched1, user12_sched2, user12_sched3] @@ -1079,27 +1079,23 @@ fn build_genesis_has_storage_version_v1() { #[test] fn merge_vesting_handles_per_block_0() { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - let sched0 = VestingInfo::new::( - ED, - 0, // Vesting over 256 blocks. + let sched0 = VestingInfo::new( + ED, 0, // Vesting over 256 blocks. 1, ); assert_eq!( sched0.ending_block_as_balance::(), Ok(257) ); - let sched1 = VestingInfo::new::( + let sched1 = VestingInfo::new( ED * 2, 0, // Vesting over 512 blocks. 10, ); assert_eq!(sched1.ending_block_as_balance::(), Ok(512u64 + 10)); - let merged = VestingInfo::new::(764, 1, 10); - assert_eq!( - Vesting::merge_vesting_info(5, sched0, sched1), - Ok(Some(merged)) - ); + let merged = VestingInfo::new(764, 1, 10); + assert_eq!(Vesting::merge_vesting_info(5, sched0, sched1), Ok(Some(merged))); }); } @@ -1107,51 +1103,48 @@ fn merge_vesting_handles_per_block_0() { fn vesting_info_validate_works() { let min_transfer = ::MinVestedTransfer::get(); // Does not check for min transfer. - match VestingInfo::new::(min_transfer - 1, 1u64, 10u64).validate::() { + match VestingInfo::new(min_transfer - 1, 1u64, 10u64).validate::() { Ok(_) => (), _ => panic!(), } // `locked` cannot be 0. - match VestingInfo::new::(0, 1u64, 10u64).validate::() { + match VestingInfo::new(0, 1u64, 10u64).validate::() { Err(Error::::InvalidScheduleParams) => (), _ => panic!(), } // `per_block` cannot be 0. - match VestingInfo::new::(min_transfer + 1, 0u64, 10u64).validate::() { + match VestingInfo::new(min_transfer + 1, 0u64, 10u64).validate::() { Err(Error::::InvalidScheduleParams) => (), _ => panic!(), } // With valid inputs it does not error. - let valid = VestingInfo::new::(min_transfer, 1u64, 10u64); + let valid = VestingInfo::new(min_transfer, 1u64, 10u64); assert_ok!(valid.validate::()); } #[test] fn vesting_info_ending_block_as_balance_works() { // Treats `per_block` 0 as 1. - let per_block_0 = VestingInfo::new::(256u32, 0u32, 10u32); - assert_eq!( - per_block_0.ending_block_as_balance::(), - Ok(266) - ); + let per_block_0 = VestingInfo::new(256u32, 0u32, 10u32); + assert_eq!(per_block_0.ending_block_as_balance::(), Ok(266)); // `per_block >= locked` always results in a schedule ending the block after it starts - let per_block_gt_locked = VestingInfo::new::(256u32, 256 * 2u32, 10u32); + let per_block_gt_locked = VestingInfo::new(256u32, 256 * 2u32, 10u32); assert_eq!( per_block_gt_locked.ending_block_as_balance::().unwrap(), 1 + per_block_gt_locked.starting_block() ); - let per_block_eq_locked = VestingInfo::new::(256u32, 256u32, 10u32); + let per_block_eq_locked = VestingInfo::new(256u32, 256u32, 10u32); assert_eq!( per_block_gt_locked.ending_block_as_balance::().unwrap(), per_block_eq_locked.ending_block_as_balance::().unwrap() ); // Correctly calcs end if `locked % per_block != 0`. (We need a block to unlock the remainder). - let imperfect_per_block = VestingInfo::new::(256u32, 250u32, 10u32); + let imperfect_per_block = VestingInfo::new(256u32, 250u32, 10u32); assert_eq!( imperfect_per_block.ending_block_as_balance::().unwrap(), imperfect_per_block.starting_block() + 2u32, @@ -1165,11 +1158,11 @@ fn vesting_info_ending_block_as_balance_works() { #[test] fn per_block_works() { - let per_block_0 = VestingInfo::new::(256u32, 0u32, 10u32); + let per_block_0 = VestingInfo::new(256u32, 0u32, 10u32); assert_eq!(per_block_0.per_block(), 1u32); assert_eq!(per_block_0.raw_per_block(), 0u32); - let per_block_1 = VestingInfo::new::(256u32, 1u32, 10u32); + let per_block_1 = VestingInfo::new(256u32, 1u32, 10u32); assert_eq!(per_block_1.per_block(), 1u32); assert_eq!(per_block_1.raw_per_block(), 1u32); } diff --git a/frame/vesting/src/vesting_info.rs b/frame/vesting/src/vesting_info.rs index 70978ebab3aa4..35913e4724c82 100644 --- a/frame/vesting/src/vesting_info.rs +++ b/frame/vesting/src/vesting_info.rs @@ -37,7 +37,7 @@ where BlockNumber: AtLeast32BitUnsigned + Copy + Bounded, { /// Instantiate a new `VestingInfo`. - pub fn new( + pub fn new( locked: Balance, per_block: Balance, starting_block: BlockNumber, @@ -86,19 +86,22 @@ where &self, n: BlockNumber, ) -> Balance { - // Number of blocks that count toward vesting - // Saturating to 0 when n < starting_block + // Number of blocks that count toward vesting; + // saturating to 0 when n < starting_block. let vested_block_count = n.saturating_sub(self.starting_block); let vested_block_count = BlockNumberToBalance::convert(vested_block_count); - // Return amount that is still locked in vesting + // Return amount that is still locked in vesting. vested_block_count - .checked_mul(&self.per_block()) // per_block accessor guarantees at least 1. + .checked_mul(&self.per_block()) // `per_block` accessor guarantees at least 1. .map(|to_unlock| self.locked.saturating_sub(to_unlock)) .unwrap_or(Zero::zero()) } /// Block number at which the schedule ends (as type `Balance`). - pub fn ending_block_as_balance, T: Config>( + pub fn ending_block_as_balance< + BlockNumberToBalance: Convert, + T: Config, + >( &self, ) -> Result { let starting_block = BlockNumberToBalance::convert(self.starting_block); @@ -112,7 +115,7 @@ where Zero::zero() } else { // `per_block` does not perfectly divide `locked`, so we need an extra block to - // unlock some amount less than per_block + // unlock some amount less than `per_block`. One::one() } }; From 6d13d902eae0ff91a0a74f3af9da9b70960d6552 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 8 Jul 2021 12:22:11 -0700 Subject: [PATCH 104/144] Remove unnescary resut for ending_block_as_balance --- frame/vesting/src/lib.rs | 4 ++-- frame/vesting/src/tests.rs | 34 +++++++++++++++---------------- frame/vesting/src/vesting_info.rs | 4 ++-- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index a4a16cfc38b25..fedb0b2a7c6cb 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -445,8 +445,8 @@ impl Pallet { schedule1: VestingInfo, T::BlockNumber>, schedule2: VestingInfo, T::BlockNumber>, ) -> Result, T::BlockNumber>>, DispatchError> { - let schedule1_ending_block = schedule1.ending_block_as_balance::()?; - let schedule2_ending_block = schedule2.ending_block_as_balance::()?; + let schedule1_ending_block = schedule1.ending_block_as_balance::(); + let schedule2_ending_block = schedule2.ending_block_as_balance::(); let now_as_balance = T::BlockNumberToBalance::convert(now); // Check if one or both schedules have ended. diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index 2d8dcf07368fb..e6298890e05d2 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -687,8 +687,7 @@ fn merge_ongoing_schedules() { // End block of the new schedule is the greater of either merged schedule. let sched2_end = sched1 .ending_block_as_balance::() - .unwrap() - .max(sched0.ending_block_as_balance::().unwrap()); + .max(sched0.ending_block_as_balance::()); let sched2_duration = sched2_end - cur_block; // Based off the new schedules total locked and its duration, we can calculate the // amount to unlock per block. @@ -754,8 +753,7 @@ fn merging_shifts_other_schedules_index() { // and will end at the max possible block. let sched3_end = sched2 .ending_block_as_balance::() - .unwrap() - .max(sched0.ending_block_as_balance::().unwrap()); + .max(sched0.ending_block_as_balance::()); let sched3_duration = sched3_end - sched3_start; let sched3_per_block = sched3_locked / sched3_duration; let sched3 = VestingInfo::new(sched3_locked, sched3_per_block, sched3_start); @@ -781,7 +779,7 @@ fn merge_ongoing_and_yet_to_be_started_schedules() { assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); // Fast forward to half way through the life of sched1. - let mut cur_block = (sched0.starting_block() + sched0.ending_block_as_balance::().unwrap()) / 2; + let mut cur_block = (sched0.starting_block() + sched0.ending_block_as_balance::()) / 2; assert_eq!(cur_block, 20); System::set_block_number(cur_block); @@ -822,8 +820,8 @@ fn merge_ongoing_and_yet_to_be_started_schedules() { let sched2_locked = sched0.locked_at::(cur_block) + sched1.locked_at::(cur_block); // and will end at the max possible block. - let sched2_end = sched0.ending_block_as_balance::().unwrap() - .max(sched1.ending_block_as_balance::().unwrap()); + let sched2_end = sched0.ending_block_as_balance::() + .max(sched1.ending_block_as_balance::()); let sched2_duration = sched2_end - sched2_start; let sched2_per_block = sched2_locked / sched2_duration; @@ -865,7 +863,7 @@ fn merge_finished_and_ongoing_schedules() { assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1, sched2]); // Fast forward to sched0's end block. - let cur_block = sched0.ending_block_as_balance::().unwrap(); + let cur_block = sched0.ending_block_as_balance::(); System::set_block_number(cur_block); assert_eq!(System::block_number(), 30); @@ -916,8 +914,8 @@ fn merge_finishing_schedules_does_not_create_a_new_one() { assert_ok!(Vesting::vested_transfer(Some(3).into(), 2, sched1)); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); - let all_scheds_end = sched0.ending_block_as_balance::().unwrap() - .max(sched1.ending_block_as_balance::().unwrap()); + let all_scheds_end = sched0.ending_block_as_balance::() + .max(sched1.ending_block_as_balance::()); assert_eq!(all_scheds_end, 40); System::set_block_number(all_scheds_end); @@ -1085,14 +1083,14 @@ fn merge_vesting_handles_per_block_0() { ); assert_eq!( sched0.ending_block_as_balance::(), - Ok(257) + 257 ); let sched1 = VestingInfo::new( ED * 2, 0, // Vesting over 512 blocks. 10, ); - assert_eq!(sched1.ending_block_as_balance::(), Ok(512u64 + 10)); + assert_eq!(sched1.ending_block_as_balance::(), 512u64 + 10); let merged = VestingInfo::new(764, 1, 10); assert_eq!(Vesting::merge_vesting_info(5, sched0, sched1), Ok(Some(merged))); @@ -1129,29 +1127,29 @@ fn vesting_info_validate_works() { fn vesting_info_ending_block_as_balance_works() { // Treats `per_block` 0 as 1. let per_block_0 = VestingInfo::new(256u32, 0u32, 10u32); - assert_eq!(per_block_0.ending_block_as_balance::(), Ok(266)); + assert_eq!(per_block_0.ending_block_as_balance::(), 266); // `per_block >= locked` always results in a schedule ending the block after it starts let per_block_gt_locked = VestingInfo::new(256u32, 256 * 2u32, 10u32); assert_eq!( - per_block_gt_locked.ending_block_as_balance::().unwrap(), + per_block_gt_locked.ending_block_as_balance::(), 1 + per_block_gt_locked.starting_block() ); let per_block_eq_locked = VestingInfo::new(256u32, 256u32, 10u32); assert_eq!( - per_block_gt_locked.ending_block_as_balance::().unwrap(), - per_block_eq_locked.ending_block_as_balance::().unwrap() + per_block_gt_locked.ending_block_as_balance::(), + per_block_eq_locked.ending_block_as_balance::() ); // Correctly calcs end if `locked % per_block != 0`. (We need a block to unlock the remainder). let imperfect_per_block = VestingInfo::new(256u32, 250u32, 10u32); assert_eq!( - imperfect_per_block.ending_block_as_balance::().unwrap(), + imperfect_per_block.ending_block_as_balance::(), imperfect_per_block.starting_block() + 2u32, ); assert_eq!( imperfect_per_block - .locked_at::(imperfect_per_block.ending_block_as_balance::().unwrap()), + .locked_at::(imperfect_per_block.ending_block_as_balance::()), 0 ); } diff --git a/frame/vesting/src/vesting_info.rs b/frame/vesting/src/vesting_info.rs index 35913e4724c82..62dd974a1e0bd 100644 --- a/frame/vesting/src/vesting_info.rs +++ b/frame/vesting/src/vesting_info.rs @@ -103,7 +103,7 @@ where T: Config, >( &self, - ) -> Result { + ) -> Balance { let starting_block = BlockNumberToBalance::convert(self.starting_block); let duration = if self.per_block() >= self.locked { // If `per_block` is bigger than `locked`, the schedule will end @@ -120,6 +120,6 @@ where } }; - Ok(starting_block.saturating_add(duration)) + starting_block.saturating_add(duration) } } From 213a867cecdf89fc896edc219243487958ac140f Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 8 Jul 2021 12:27:22 -0700 Subject: [PATCH 105/144] Remove T param from ending_block_as_balance --- frame/vesting/src/lib.rs | 4 ++-- frame/vesting/src/tests.rs | 36 +++++++++++++++---------------- frame/vesting/src/vesting_info.rs | 5 +---- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index fedb0b2a7c6cb..018211b2ff5a8 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -445,8 +445,8 @@ impl Pallet { schedule1: VestingInfo, T::BlockNumber>, schedule2: VestingInfo, T::BlockNumber>, ) -> Result, T::BlockNumber>>, DispatchError> { - let schedule1_ending_block = schedule1.ending_block_as_balance::(); - let schedule2_ending_block = schedule2.ending_block_as_balance::(); + let schedule1_ending_block = schedule1.ending_block_as_balance::(); + let schedule2_ending_block = schedule2.ending_block_as_balance::(); let now_as_balance = T::BlockNumberToBalance::convert(now); // Check if one or both schedules have ended. diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index e6298890e05d2..9116e574a1edd 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -686,8 +686,8 @@ fn merge_ongoing_schedules() { .saturating_add(sched0.locked_at::(cur_block)); // End block of the new schedule is the greater of either merged schedule. let sched2_end = sched1 - .ending_block_as_balance::() - .max(sched0.ending_block_as_balance::()); + .ending_block_as_balance::() + .max(sched0.ending_block_as_balance::()); let sched2_duration = sched2_end - cur_block; // Based off the new schedules total locked and its duration, we can calculate the // amount to unlock per block. @@ -752,8 +752,8 @@ fn merging_shifts_other_schedules_index() { sched2.locked_at::(cur_block) + sched0.locked_at::(cur_block); // and will end at the max possible block. let sched3_end = sched2 - .ending_block_as_balance::() - .max(sched0.ending_block_as_balance::()); + .ending_block_as_balance::() + .max(sched0.ending_block_as_balance::()); let sched3_duration = sched3_end - sched3_start; let sched3_per_block = sched3_locked / sched3_duration; let sched3 = VestingInfo::new(sched3_locked, sched3_per_block, sched3_start); @@ -779,7 +779,7 @@ fn merge_ongoing_and_yet_to_be_started_schedules() { assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); // Fast forward to half way through the life of sched1. - let mut cur_block = (sched0.starting_block() + sched0.ending_block_as_balance::()) / 2; + let mut cur_block = (sched0.starting_block() + sched0.ending_block_as_balance::()) / 2; assert_eq!(cur_block, 20); System::set_block_number(cur_block); @@ -820,8 +820,8 @@ fn merge_ongoing_and_yet_to_be_started_schedules() { let sched2_locked = sched0.locked_at::(cur_block) + sched1.locked_at::(cur_block); // and will end at the max possible block. - let sched2_end = sched0.ending_block_as_balance::() - .max(sched1.ending_block_as_balance::()); + let sched2_end = sched0.ending_block_as_balance::() + .max(sched1.ending_block_as_balance::()); let sched2_duration = sched2_end - sched2_start; let sched2_per_block = sched2_locked / sched2_duration; @@ -863,7 +863,7 @@ fn merge_finished_and_ongoing_schedules() { assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1, sched2]); // Fast forward to sched0's end block. - let cur_block = sched0.ending_block_as_balance::(); + let cur_block = sched0.ending_block_as_balance::(); System::set_block_number(cur_block); assert_eq!(System::block_number(), 30); @@ -914,8 +914,8 @@ fn merge_finishing_schedules_does_not_create_a_new_one() { assert_ok!(Vesting::vested_transfer(Some(3).into(), 2, sched1)); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); - let all_scheds_end = sched0.ending_block_as_balance::() - .max(sched1.ending_block_as_balance::()); + let all_scheds_end = sched0.ending_block_as_balance::() + .max(sched1.ending_block_as_balance::()); assert_eq!(all_scheds_end, 40); System::set_block_number(all_scheds_end); @@ -1082,7 +1082,7 @@ fn merge_vesting_handles_per_block_0() { 1, ); assert_eq!( - sched0.ending_block_as_balance::(), + sched0.ending_block_as_balance::(), 257 ); let sched1 = VestingInfo::new( @@ -1090,7 +1090,7 @@ fn merge_vesting_handles_per_block_0() { 0, // Vesting over 512 blocks. 10, ); - assert_eq!(sched1.ending_block_as_balance::(), 512u64 + 10); + assert_eq!(sched1.ending_block_as_balance::(), 512u64 + 10); let merged = VestingInfo::new(764, 1, 10); assert_eq!(Vesting::merge_vesting_info(5, sched0, sched1), Ok(Some(merged))); @@ -1127,29 +1127,29 @@ fn vesting_info_validate_works() { fn vesting_info_ending_block_as_balance_works() { // Treats `per_block` 0 as 1. let per_block_0 = VestingInfo::new(256u32, 0u32, 10u32); - assert_eq!(per_block_0.ending_block_as_balance::(), 266); + assert_eq!(per_block_0.ending_block_as_balance::(), 266); // `per_block >= locked` always results in a schedule ending the block after it starts let per_block_gt_locked = VestingInfo::new(256u32, 256 * 2u32, 10u32); assert_eq!( - per_block_gt_locked.ending_block_as_balance::(), + per_block_gt_locked.ending_block_as_balance::(), 1 + per_block_gt_locked.starting_block() ); let per_block_eq_locked = VestingInfo::new(256u32, 256u32, 10u32); assert_eq!( - per_block_gt_locked.ending_block_as_balance::(), - per_block_eq_locked.ending_block_as_balance::() + per_block_gt_locked.ending_block_as_balance::(), + per_block_eq_locked.ending_block_as_balance::() ); // Correctly calcs end if `locked % per_block != 0`. (We need a block to unlock the remainder). let imperfect_per_block = VestingInfo::new(256u32, 250u32, 10u32); assert_eq!( - imperfect_per_block.ending_block_as_balance::(), + imperfect_per_block.ending_block_as_balance::(), imperfect_per_block.starting_block() + 2u32, ); assert_eq!( imperfect_per_block - .locked_at::(imperfect_per_block.ending_block_as_balance::()), + .locked_at::(imperfect_per_block.ending_block_as_balance::()), 0 ); } diff --git a/frame/vesting/src/vesting_info.rs b/frame/vesting/src/vesting_info.rs index 62dd974a1e0bd..bb0317b1d5aca 100644 --- a/frame/vesting/src/vesting_info.rs +++ b/frame/vesting/src/vesting_info.rs @@ -98,10 +98,7 @@ where } /// Block number at which the schedule ends (as type `Balance`). - pub fn ending_block_as_balance< - BlockNumberToBalance: Convert, - T: Config, - >( + pub fn ending_block_as_balance>( &self, ) -> Balance { let starting_block = BlockNumberToBalance::convert(self.starting_block); From fc094da55ae82fb5c358418482bb1ec3bd2b1e1c Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 8 Jul 2021 12:31:48 -0700 Subject: [PATCH 106/144] Reduce visibility of raw_per_block --- frame/vesting/src/vesting_info.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/vesting/src/vesting_info.rs b/frame/vesting/src/vesting_info.rs index bb0317b1d5aca..7e55ddf441a36 100644 --- a/frame/vesting/src/vesting_info.rs +++ b/frame/vesting/src/vesting_info.rs @@ -72,7 +72,7 @@ where /// Get the unmodified `per_block`. Generally should not be used, but is useful for /// validating `per_block`. - pub fn raw_per_block(&self) -> Balance { + pub(crate) fn raw_per_block(&self) -> Balance { self.per_block } From 9f1a90c34369654d5a6852b930ae1a9f45796fe8 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 8 Jul 2021 12:44:09 -0700 Subject: [PATCH 107/144] Remove unused type param for validate --- frame/vesting/src/lib.rs | 9 ++++----- frame/vesting/src/tests.rs | 8 ++++---- frame/vesting/src/vesting_info.rs | 5 +---- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 018211b2ff5a8..284a2f57f00ef 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -236,7 +236,7 @@ pub mod pallet { let length_as_balance = T::BlockNumberToBalance::convert(length); let per_block = locked / length_as_balance.max(sp_runtime::traits::One::one()); let vesting_info = VestingInfo::new(locked, per_block, begin); - vesting_info.validate::() + vesting_info.validate::() .expect("Invalid VestingInfo params at genesis"); Vesting::::try_append(who, vesting_info) @@ -493,7 +493,7 @@ impl Pallet { ) -> DispatchResult { // Validate user inputs. ensure!(schedule.locked() >= T::MinVestedTransfer::get(), Error::::AmountLow); - schedule.validate::()?; + schedule.validate::()?; let target = T::Lookup::lookup(target)?; let source = T::Lookup::lookup(source)?; @@ -693,7 +693,7 @@ where let vesting_schedule = VestingInfo::new(locked, per_block, starting_block); // Check for `per_block` or `locked` of 0 and ending block greater than max block. - vesting_schedule.validate::()?; + vesting_schedule.validate::()?; let mut schedules = Self::vesting(who).unwrap_or_default(); @@ -720,8 +720,7 @@ where starting_block: T::BlockNumber, ) -> DispatchResult { // Check for `per_block` or `locked` of 0 and ending block greater than max block. - VestingInfo::new(locked, per_block, starting_block) - .validate::()?; + VestingInfo::new(locked, per_block, starting_block).validate::()?; ensure!( Vesting::::decode_len(who).unwrap_or_default() < diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index 9116e574a1edd..41bd8a2ce99ca 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -1101,26 +1101,26 @@ fn merge_vesting_handles_per_block_0() { fn vesting_info_validate_works() { let min_transfer = ::MinVestedTransfer::get(); // Does not check for min transfer. - match VestingInfo::new(min_transfer - 1, 1u64, 10u64).validate::() { + match VestingInfo::new(min_transfer - 1, 1u64, 10u64).validate::() { Ok(_) => (), _ => panic!(), } // `locked` cannot be 0. - match VestingInfo::new(0, 1u64, 10u64).validate::() { + match VestingInfo::new(0, 1u64, 10u64).validate::() { Err(Error::::InvalidScheduleParams) => (), _ => panic!(), } // `per_block` cannot be 0. - match VestingInfo::new(min_transfer + 1, 0u64, 10u64).validate::() { + match VestingInfo::new(min_transfer + 1, 0u64, 10u64).validate::() { Err(Error::::InvalidScheduleParams) => (), _ => panic!(), } // With valid inputs it does not error. let valid = VestingInfo::new(min_transfer, 1u64, 10u64); - assert_ok!(valid.validate::()); + assert_ok!(valid.validate::()); } #[test] diff --git a/frame/vesting/src/vesting_info.rs b/frame/vesting/src/vesting_info.rs index 7e55ddf441a36..2778afcc8e3b9 100644 --- a/frame/vesting/src/vesting_info.rs +++ b/frame/vesting/src/vesting_info.rs @@ -18,7 +18,6 @@ //! Module to enforce private fields on `VestingInfo`. use super::*; -use codec::MaxEncodedLen; /// Struct to encode the vesting schedule of an individual account. #[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen)] @@ -47,9 +46,7 @@ where /// Validate parameters for `VestingInfo`. Note that this does not check /// against `MinVestedTransfer` or the current block. - pub fn validate, T: Config>( - &self, - ) -> Result<(), Error> { + pub fn validate(&self) -> Result<(), Error> { ensure!( !self.locked.is_zero() && !self.raw_per_block().is_zero(), Error::::InvalidScheduleParams From 28dab15a1b8304e9ab7a05a2af146fe032155b19 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 8 Jul 2021 12:54:05 -0700 Subject: [PATCH 108/144] update old comment --- frame/vesting/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 284a2f57f00ef..b99dca7ec2ecf 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -692,7 +692,7 @@ where } let vesting_schedule = VestingInfo::new(locked, per_block, starting_block); - // Check for `per_block` or `locked` of 0 and ending block greater than max block. + // Check for `per_block` or `locked` of 0. vesting_schedule.validate::()?; let mut schedules = Self::vesting(who).unwrap_or_default(); @@ -719,7 +719,7 @@ where per_block: BalanceOf, starting_block: T::BlockNumber, ) -> DispatchResult { - // Check for `per_block` or `locked` of 0 and ending block greater than max block. + // Check for `per_block` or `locked` of 0. VestingInfo::new(locked, per_block, starting_block).validate::()?; ensure!( From da798992417a1074cf0da8cdf8008d21509f0149 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 8 Jul 2021 14:18:34 -0700 Subject: [PATCH 109/144] Make log a dep; log warn in migrate --- frame/vesting/Cargo.toml | 4 ++-- frame/vesting/src/migrations.rs | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/frame/vesting/Cargo.toml b/frame/vesting/Cargo.toml index 51d88ef694cc4..0e7f96777a37b 100644 --- a/frame/vesting/Cargo.toml +++ b/frame/vesting/Cargo.toml @@ -20,7 +20,7 @@ sp-runtime = { version = "3.0.0", default-features = false, path = "../../primit frame-support = { version = "3.0.0", default-features = false, path = "../support" } frame-system = { version = "3.0.0", default-features = false, path = "../system" } frame-benchmarking = { version = "3.1.0", default-features = false, path = "../benchmarking", optional = true } -log = { version = "0.4.0", default-features = false, optional = true } +log = { version = "0.4.0", default-features = false } sp-io = { version = "3.0.0", default-features = false, path = "../../primitives/io" } [dev-dependencies] @@ -39,4 +39,4 @@ std = [ "frame-system/std", ] runtime-benchmarks = ["frame-benchmarking"] -try-runtime = ["frame-support/try-runtime", "log"] +try-runtime = ["frame-support/try-runtime"] diff --git a/frame/vesting/src/migrations.rs b/frame/vesting/src/migrations.rs index ee9d9694700bb..38a025959b860 100644 --- a/frame/vesting/src/migrations.rs +++ b/frame/vesting/src/migrations.rs @@ -46,6 +46,10 @@ pub(crate) mod v1 { BoundedVec, T::BlockNumber>, T::MaxVestingSchedules>, > = vec![vesting_info].try_into().ok(); + if v.is_none() { + log::warn!(target: "runtime::vesting", "migration failed to move a vesting schedule into a BoundedVec"); + } + v }, ); @@ -64,8 +68,8 @@ pub(crate) mod v1 { for s in schedules { // It is ok if this does not pass, but ideally pre-existing schedules would pass // this validation logic so we can be more confident about edge cases. - debug_assert!( - s.validate::().is_ok(), + assert!( + s.validate::().is_ok(), "A schedule does not pass new validation logic" ); } From 7a05c7162f6bcc650b131b9b6fd5eec10015dcc6 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 8 Jul 2021 14:51:13 -0700 Subject: [PATCH 110/144] VestingInfo.validate returns Err(()), no T type param --- frame/vesting/src/lib.rs | 11 ++++++----- frame/vesting/src/tests.rs | 20 +++++--------------- frame/vesting/src/vesting_info.rs | 15 +++++++-------- 3 files changed, 18 insertions(+), 28 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index b99dca7ec2ecf..3555447cd642c 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -236,7 +236,7 @@ pub mod pallet { let length_as_balance = T::BlockNumberToBalance::convert(length); let per_block = locked / length_as_balance.max(sp_runtime::traits::One::one()); let vesting_info = VestingInfo::new(locked, per_block, begin); - vesting_info.validate::() + vesting_info.validate() .expect("Invalid VestingInfo params at genesis"); Vesting::::try_append(who, vesting_info) @@ -493,8 +493,7 @@ impl Pallet { ) -> DispatchResult { // Validate user inputs. ensure!(schedule.locked() >= T::MinVestedTransfer::get(), Error::::AmountLow); - schedule.validate::()?; - + schedule.validate().map_err(|_| Error::::InvalidScheduleParams)?; let target = T::Lookup::lookup(target)?; let source = T::Lookup::lookup(source)?; @@ -693,7 +692,7 @@ where let vesting_schedule = VestingInfo::new(locked, per_block, starting_block); // Check for `per_block` or `locked` of 0. - vesting_schedule.validate::()?; + vesting_schedule.validate().map_err(|_| Error::::InvalidScheduleParams)?; let mut schedules = Self::vesting(who).unwrap_or_default(); @@ -720,7 +719,9 @@ where starting_block: T::BlockNumber, ) -> DispatchResult { // Check for `per_block` or `locked` of 0. - VestingInfo::new(locked, per_block, starting_block).validate::()?; + VestingInfo::new(locked, per_block, starting_block) + .validate() + .map_err(|_| Error::::InvalidScheduleParams)?; ensure!( Vesting::::decode_len(who).unwrap_or_default() < diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index 41bd8a2ce99ca..efaf887976b0a 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use frame_support::{assert_noop, assert_ok, assert_storage_noop, dispatch::EncodeLike}; +use frame_support::{assert_noop, assert_ok, assert_storage_noop, assert_err, dispatch::EncodeLike}; use frame_system::RawOrigin; use sp_runtime::traits::{BadOrigin, Identity}; @@ -1101,26 +1101,16 @@ fn merge_vesting_handles_per_block_0() { fn vesting_info_validate_works() { let min_transfer = ::MinVestedTransfer::get(); // Does not check for min transfer. - match VestingInfo::new(min_transfer - 1, 1u64, 10u64).validate::() { - Ok(_) => (), - _ => panic!(), - } + assert_ok!(VestingInfo::new(min_transfer - 1, 1u64, 10u64).validate()); // `locked` cannot be 0. - match VestingInfo::new(0, 1u64, 10u64).validate::() { - Err(Error::::InvalidScheduleParams) => (), - _ => panic!(), - } + assert_err!(VestingInfo::new(0, 1u64, 10u64).validate(), ()); // `per_block` cannot be 0. - match VestingInfo::new(min_transfer + 1, 0u64, 10u64).validate::() { - Err(Error::::InvalidScheduleParams) => (), - _ => panic!(), - } + assert_err!(VestingInfo::new(min_transfer + 1, 0u64, 10u64).validate(), ()); // With valid inputs it does not error. - let valid = VestingInfo::new(min_transfer, 1u64, 10u64); - assert_ok!(valid.validate::()); + assert_ok!(VestingInfo::new(min_transfer, 1u64, 10u64).validate()); } #[test] diff --git a/frame/vesting/src/vesting_info.rs b/frame/vesting/src/vesting_info.rs index 2778afcc8e3b9..877c24327b4bd 100644 --- a/frame/vesting/src/vesting_info.rs +++ b/frame/vesting/src/vesting_info.rs @@ -45,14 +45,13 @@ where } /// Validate parameters for `VestingInfo`. Note that this does not check - /// against `MinVestedTransfer` or the current block. - pub fn validate(&self) -> Result<(), Error> { - ensure!( - !self.locked.is_zero() && !self.raw_per_block().is_zero(), - Error::::InvalidScheduleParams - ); - - Ok(()) + /// against `MinVestedTransfer`. + pub fn validate(&self) -> Result<(), ()> { + if self.locked.is_zero() || self.raw_per_block().is_zero() { + Err(()) + } else { + Ok(()) + } } /// Locked amount at schedule creation. From 45726f764710da36829fb3ef131d48799b4d30d0 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 8 Jul 2021 17:30:27 -0700 Subject: [PATCH 111/144] Try improve report_schedule_updates --- frame/vesting/src/lib.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 3555447cd642c..18edbffb14992 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -98,6 +98,7 @@ impl Default for Releases { } /// Actions to take on a user's `Vesting` storage entry. +#[derive(Clone, Copy)] enum VestingAction { /// Do not actively remove any schedules. Passive, @@ -116,6 +117,20 @@ impl VestingAction { Self::Merge(index1, index2) => *index1 == index || *index2 == index, } } + + /// Pick the schedules that this action dictates should continue vesting undisturbed. + fn pick_schedules<'a, T: Config>( + &'a self, + schedules: Vec, T::BlockNumber>>, + ) -> impl Iterator, T::BlockNumber>> + 'a { + schedules.into_iter().enumerate().filter_map(move |(index, schedule)| { + if self.should_remove(index) { + None + } else { + Some(schedule) + } + }) + } } #[frame_support::pallet] @@ -541,15 +556,13 @@ impl Pallet { let now = >::block_number(); let mut total_locked_now: BalanceOf = Zero::zero(); - let filtered_schedules = schedules - .into_iter() - .enumerate() - .filter_map(|(index, schedule)| { + let filtered_schedules = action + .pick_schedules::(schedules) + .filter_map(|schedule| { let locked_now = schedule.locked_at::(now); - if locked_now.is_zero() || action.should_remove(index) { + if locked_now.is_zero() { None } else { - // We track the locked amount only if the schedule is included. total_locked_now = total_locked_now.saturating_add(locked_now); Some(schedule) } From 81a29afe62dc067e5a70c09436395745daf758e5 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 8 Jul 2021 17:45:55 -0700 Subject: [PATCH 112/144] is_valid, not validate --- frame/vesting/src/lib.rs | 15 +++++++-------- frame/vesting/src/tests.rs | 10 +++++----- frame/vesting/src/vesting_info.rs | 8 ++------ 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 18edbffb14992..74a454daf96d6 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -251,8 +251,7 @@ pub mod pallet { let length_as_balance = T::BlockNumberToBalance::convert(length); let per_block = locked / length_as_balance.max(sp_runtime::traits::One::one()); let vesting_info = VestingInfo::new(locked, per_block, begin); - vesting_info.validate() - .expect("Invalid VestingInfo params at genesis"); + if !vesting_info.is_valid() { panic!("Invalid VestingInfo params at genesis") }; Vesting::::try_append(who, vesting_info) .expect("Too many vesting schedules at genesis."); @@ -494,7 +493,7 @@ impl Pallet { }; // While `per_block` should never be 0, it is possible that the created schedule would end - // after the highest possible block, which is a case we are ok with, but `validate` fails. + // after the highest possible block, which is a case we are ok with, but `is_valid` fails. let schedule = VestingInfo::new(locked, per_block, starting_block); Ok(Some(schedule)) @@ -508,7 +507,7 @@ impl Pallet { ) -> DispatchResult { // Validate user inputs. ensure!(schedule.locked() >= T::MinVestedTransfer::get(), Error::::AmountLow); - schedule.validate().map_err(|_| Error::::InvalidScheduleParams)?; + if !schedule.is_valid() { return Err(Error::::InvalidScheduleParams.into()); }; let target = T::Lookup::lookup(target)?; let source = T::Lookup::lookup(source)?; @@ -705,7 +704,7 @@ where let vesting_schedule = VestingInfo::new(locked, per_block, starting_block); // Check for `per_block` or `locked` of 0. - vesting_schedule.validate().map_err(|_| Error::::InvalidScheduleParams)?; + if !vesting_schedule.is_valid() { return Err(Error::::InvalidScheduleParams.into()); }; let mut schedules = Self::vesting(who).unwrap_or_default(); @@ -732,9 +731,9 @@ where starting_block: T::BlockNumber, ) -> DispatchResult { // Check for `per_block` or `locked` of 0. - VestingInfo::new(locked, per_block, starting_block) - .validate() - .map_err(|_| Error::::InvalidScheduleParams)?; + if !VestingInfo::new(locked, per_block, starting_block).is_valid() { + return Err(Error::::InvalidScheduleParams.into()) + } ensure!( Vesting::::decode_len(who).unwrap_or_default() < diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index efaf887976b0a..b760bb5d900fe 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use frame_support::{assert_noop, assert_ok, assert_storage_noop, assert_err, dispatch::EncodeLike}; +use frame_support::{assert_noop, assert_ok, assert_storage_noop, dispatch::EncodeLike}; use frame_system::RawOrigin; use sp_runtime::traits::{BadOrigin, Identity}; @@ -1101,16 +1101,16 @@ fn merge_vesting_handles_per_block_0() { fn vesting_info_validate_works() { let min_transfer = ::MinVestedTransfer::get(); // Does not check for min transfer. - assert_ok!(VestingInfo::new(min_transfer - 1, 1u64, 10u64).validate()); + assert_eq!(VestingInfo::new(min_transfer - 1, 1u64, 10u64).is_valid(), true); // `locked` cannot be 0. - assert_err!(VestingInfo::new(0, 1u64, 10u64).validate(), ()); + assert_eq!(VestingInfo::new(0, 1u64, 10u64).is_valid(), false); // `per_block` cannot be 0. - assert_err!(VestingInfo::new(min_transfer + 1, 0u64, 10u64).validate(), ()); + assert_eq!(VestingInfo::new(min_transfer + 1, 0u64, 10u64).is_valid(), false); // With valid inputs it does not error. - assert_ok!(VestingInfo::new(min_transfer, 1u64, 10u64).validate()); + assert_eq!(VestingInfo::new(min_transfer, 1u64, 10u64).is_valid(), true); } #[test] diff --git a/frame/vesting/src/vesting_info.rs b/frame/vesting/src/vesting_info.rs index 877c24327b4bd..72171910086cd 100644 --- a/frame/vesting/src/vesting_info.rs +++ b/frame/vesting/src/vesting_info.rs @@ -46,12 +46,8 @@ where /// Validate parameters for `VestingInfo`. Note that this does not check /// against `MinVestedTransfer`. - pub fn validate(&self) -> Result<(), ()> { - if self.locked.is_zero() || self.raw_per_block().is_zero() { - Err(()) - } else { - Ok(()) - } + pub fn is_valid(&self) -> bool { + !self.locked.is_zero() && !self.raw_per_block().is_zero() } /// Locked amount at schedule creation. From 66bddb199cac7b79d4a5ee3a023e000dc946baa7 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 8 Jul 2021 20:26:32 -0700 Subject: [PATCH 113/144] revert node runtime reorg; --- bin/node/runtime/src/lib.rs | 5 ++--- frame/vesting/src/lib.rs | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index d0e892159ae99..4b45d4e817e8b 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1036,13 +1036,12 @@ impl pallet_vesting::Config for Runtime { } impl pallet_mmr::Config for Runtime { - type Hash = ::Hash; + const INDEXING_PREFIX: &'static [u8] = b"mmr"; type Hashing = ::Hashing; + type Hash = ::Hash; type LeafData = frame_system::Pallet; type OnNewRoot = (); type WeightInfo = (); - - const INDEXING_PREFIX: &'static [u8] = b"mmr"; } parameter_types! { diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 74a454daf96d6..10cdf91831e2f 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -571,6 +571,22 @@ impl Pallet { (filtered_schedules, total_locked_now) } + fn track_locked(schedules: impl Iterator, T::BlockNumber>> ) -> (impl Iterator, T::BlockNumber>>, BalanceOf) { + let now = >::block_number(); + let mut total_locked_now: BalanceOf = Zero::zero(); + let locked_schedules = schedules.filter_map(|schedule| { + let locked_now = schedule.locked_at::(now); + if locked_now.is_zero() { + None + } else { + total_locked_now = total_locked_now.saturating_add(locked_now); + Some(schedule) + } + }); + + (locked_schedules, total_locked_now) + } + /// Write an accounts updated vesting lock to storage. fn write_lock(who: &T::AccountId, total_locked_now: BalanceOf) { if total_locked_now.is_zero() { From 83de83c5ad1d550e1cf9a6eacd7aca7232fe8ace Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Fri, 9 Jul 2021 11:15:25 -0700 Subject: [PATCH 114/144] change schedule validity check to just warning --- frame/vesting/src/lib.rs | 16 ---------------- frame/vesting/src/migrations.rs | 19 ++++++++++++------- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 10cdf91831e2f..74a454daf96d6 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -571,22 +571,6 @@ impl Pallet { (filtered_schedules, total_locked_now) } - fn track_locked(schedules: impl Iterator, T::BlockNumber>> ) -> (impl Iterator, T::BlockNumber>>, BalanceOf) { - let now = >::block_number(); - let mut total_locked_now: BalanceOf = Zero::zero(); - let locked_schedules = schedules.filter_map(|schedule| { - let locked_now = schedule.locked_at::(now); - if locked_now.is_zero() { - None - } else { - total_locked_now = total_locked_now.saturating_add(locked_now); - Some(schedule) - } - }); - - (locked_schedules, total_locked_now) - } - /// Write an accounts updated vesting lock to storage. fn write_lock(who: &T::AccountId, total_locked_now: BalanceOf) { if total_locked_now.is_zero() { diff --git a/frame/vesting/src/migrations.rs b/frame/vesting/src/migrations.rs index 38a025959b860..1962aad32b1cd 100644 --- a/frame/vesting/src/migrations.rs +++ b/frame/vesting/src/migrations.rs @@ -29,7 +29,7 @@ pub(crate) mod v1 { log::debug!( target: "runtime::vesting", - "Vesting storage version v1 PRE migration checks succesful!" + "migration: Vesting storage version v1 PRE migration checks succesful!" ); Ok(()) @@ -47,7 +47,10 @@ pub(crate) mod v1 { > = vec![vesting_info].try_into().ok(); if v.is_none() { - log::warn!(target: "runtime::vesting", "migration failed to move a vesting schedule into a BoundedVec"); + log::warn!( + target: "runtime::vesting", + "migration: Failed to move a vesting schedule into a BoundedVec" + ); } v @@ -68,16 +71,18 @@ pub(crate) mod v1 { for s in schedules { // It is ok if this does not pass, but ideally pre-existing schedules would pass // this validation logic so we can be more confident about edge cases. - assert!( - s.validate::().is_ok(), - "A schedule does not pass new validation logic" - ); + if !s.is_valid() { + log::warn!( + target: "runtime::vesting", + "migration: A schedule does not pass new validation logic.", + ) + } } } log::debug!( target: "runtime::vesting", - "Vesting storage version v1 POST migration checks succesful!" + "migration: Vesting storage version v1 POST migration checks succesful!" ); Ok(()) } From 869103d2c07db299003b18047bf1d87b6d62eb33 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 13 Jul 2021 16:04:26 -0700 Subject: [PATCH 115/144] Simplify merge_vesting_info return type --- frame/vesting/src/lib.rs | 15 +++++++-------- frame/vesting/src/tests.rs | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 74a454daf96d6..5f6825f625aa2 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -458,7 +458,7 @@ impl Pallet { now: T::BlockNumber, schedule1: VestingInfo, T::BlockNumber>, schedule2: VestingInfo, T::BlockNumber>, - ) -> Result, T::BlockNumber>>, DispatchError> { + ) -> Option, T::BlockNumber>> { let schedule1_ending_block = schedule1.ending_block_as_balance::(); let schedule2_ending_block = schedule2.ending_block_as_balance::(); let now_as_balance = T::BlockNumberToBalance::convert(now); @@ -466,11 +466,11 @@ impl Pallet { // Check if one or both schedules have ended. match (schedule1_ending_block <= now_as_balance, schedule2_ending_block <= now_as_balance) { // If both schedules have ended, we don't merge and exit early. - (true, true) => return Ok(None), + (true, true) => return None, // If one schedule has ended, we treat the one that has not ended as the new // merged schedule. - (true, false) => return Ok(Some(schedule2)), - (false, true) => return Ok(Some(schedule1)), + (true, false) => return Some(schedule2), + (false, true) => return Some(schedule1), // If neither schedule has ended don't exit early. _ => {} } @@ -492,11 +492,10 @@ impl Pallet { (locked / duration).max(One::one()) }; - // While `per_block` should never be 0, it is possible that the created schedule would end - // after the highest possible block, which is a case we are ok with, but `is_valid` fails. let schedule = VestingInfo::new(locked, per_block, starting_block); + debug_assert!(schedule.is_valid(), "merge_vesting_info schedule validation check failed"); - Ok(Some(schedule)) + Some(schedule) } // Execute a vested transfer from `source` to `target` with the given `schedule`. @@ -635,7 +634,7 @@ impl Pallet { Self::report_schedule_updates(schedules.to_vec(), action); let now = >::block_number(); - if let Some(new_schedule) = Self::merge_vesting_info(now, schedule1, schedule2)? { + if let Some(new_schedule) = Self::merge_vesting_info(now, schedule1, schedule2) { // Merging created a new schedule so we: // 1) need to add it to the accounts vesting schedule collection, schedules.push(new_schedule); diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index b760bb5d900fe..49e69f88061a6 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -1093,7 +1093,7 @@ fn merge_vesting_handles_per_block_0() { assert_eq!(sched1.ending_block_as_balance::(), 512u64 + 10); let merged = VestingInfo::new(764, 1, 10); - assert_eq!(Vesting::merge_vesting_info(5, sched0, sched1), Ok(Some(merged))); + assert_eq!(Vesting::merge_vesting_info(5, sched0, sched1), Some(merged)); }); } From ab4cb46373817875d8af9defa43df9f40c1baca2 Mon Sep 17 00:00:00 2001 From: Zeke Mostov <32168567+emostov@users.noreply.github.com> Date: Tue, 13 Jul 2021 16:12:52 -0700 Subject: [PATCH 116/144] Apply suggestions from code review --- frame/vesting/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 5f6825f625aa2..3a52a6c2f8b39 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -681,7 +681,7 @@ where /// Adds a vesting schedule to a given account. /// - /// If the account has `MaxVestingSchedules`, an Error is returned and nothing ` + /// If the account has `MaxVestingSchedules`, an Error is returned and nothing /// is updated. /// /// On success, a linearly reducing amount of funds will be locked. In order to realise any @@ -722,7 +722,6 @@ where // Ensure we can call `add_vesting_schedule` without error. This should always // be called prior to `add_vesting_schedule`. - // fn can_add_vesting_schedule( who: &T::AccountId, locked: BalanceOf, From b040038c21a7d478dafde5a9165c764da30a57ec Mon Sep 17 00:00:00 2001 From: Zeke Mostov <32168567+emostov@users.noreply.github.com> Date: Tue, 13 Jul 2021 16:20:00 -0700 Subject: [PATCH 117/144] Apply suggestions from code review Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- frame/vesting/src/tests.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index 49e69f88061a6..1340accd52c79 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -213,7 +213,7 @@ fn vested_balance_should_transfer_with_multi_sched() { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { let sched0 = VestingInfo::new(5 * ED, 128, 0); assert_ok!(Vesting::vested_transfer(Some(13).into(), 1, sched0)); - // Total of 2560 of locked for all the schedules. + // Total of 10*ED of locked for all the schedules. assert_eq!(Vesting::vesting(&1).unwrap(), vec![sched0, sched0]); let user1_free_balance = Balances::free_balance(&1); @@ -227,7 +227,7 @@ fn vested_balance_should_transfer_with_multi_sched() { } #[test] -fn vest_correctly_fails() { +fn non_vested_cannot_vest() { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { assert!(!>::contains_key(4)); assert_noop!(Vesting::vest(Some(4).into()), Error::::NotVesting); @@ -257,7 +257,7 @@ fn vested_balance_should_transfer_using_vest_other_with_multi_sched() { .execute_with(|| { let sched0 = VestingInfo::new(5*ED, 128, 0); assert_ok!(Vesting::vested_transfer(Some(13).into(), 1, sched0)); - // Total of 2560 of locked for all the schedules. + // Total of 10*ED of locked for all the schedules. assert_eq!(Vesting::vesting(&1).unwrap(), vec![sched0, sched0]); let user1_free_balance = Balances::free_balance(&1); @@ -271,7 +271,7 @@ fn vested_balance_should_transfer_using_vest_other_with_multi_sched() { } #[test] -fn vest_other_correctly_fails() { +fn non_vested_cannot_vest_other() { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { assert!(!>::contains_key(4)); assert_noop!(Vesting::vest_other(Some(3).into(), 4), Error::::NotVesting); @@ -674,7 +674,7 @@ fn merge_ongoing_schedules() { assert_ok!(Vesting::merge_schedules(Some(2).into(), 0, 1)); - // Merging schedules vests all pre-existing schedules prior to merging, which is reflected + // Merging schedules un-vests all pre-existing schedules prior to merging, which is reflected // in account 2's updated usable balance. let sched0_vested_now = sched0.per_block() * (cur_block - sched0.starting_block()); let sched1_vested_now = sched1.per_block() * (cur_block - sched1.starting_block()); From adb28b026b0bbd5e3ae8eaddc0fec62a62d233f0 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 13 Jul 2021 16:27:46 -0700 Subject: [PATCH 118/144] Add warning for migration --- frame/vesting/src/lib.rs | 1 + frame/vesting/src/migrations.rs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 3a52a6c2f8b39..4f25167ebbb74 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -182,6 +182,7 @@ pub mod pallet { } fn integrity_test() { + // NOTE: Migration v1 will delete vesting schedules if `MaxVestingSchedules < 1`. assert!(T::MaxVestingSchedules::get() > 0, "`MaxVestingSchedules` must ge greater than 0"); sp_std::if_std! { sp_io::TestExternalities::new_empty().execute_with(|| diff --git a/frame/vesting/src/migrations.rs b/frame/vesting/src/migrations.rs index 1962aad32b1cd..db4f6092a98dc 100644 --- a/frame/vesting/src/migrations.rs +++ b/frame/vesting/src/migrations.rs @@ -35,7 +35,8 @@ pub(crate) mod v1 { Ok(()) } - /// Migrate from single schedule to multi schedule storage + /// Migrate from single schedule to multi schedule storage. + /// WARNING: This migration will delete schedules if `MaxVestingSchedules < 1`. pub(crate) fn migrate() -> Weight { let mut reads_writes = 0; From f3caa670f0db875e2e1299be655ba1a7be837cc9 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 13 Jul 2021 16:36:15 -0700 Subject: [PATCH 119/144] Fix indentation --- frame/vesting/src/tests.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index 1340accd52c79..8e2978406541c 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -217,13 +217,13 @@ fn vested_balance_should_transfer_with_multi_sched() { assert_eq!(Vesting::vesting(&1).unwrap(), vec![sched0, sched0]); let user1_free_balance = Balances::free_balance(&1); - assert_eq!(user1_free_balance, 3840); // Account 1 has free balance + assert_eq!(user1_free_balance, 3840); // Account 1 has free balance - // Account 1 has only 256 units unlocking at block 1 (plus 1280 unvested) - assert_eq!(Vesting::vesting_balance(&1), Some(2304)); - assert_ok!(Vesting::vest(Some(1).into())); - assert_ok!(Balances::transfer(Some(1).into(), 2, 1536)); - }); + // Account 1 has only 256 units unlocking at block 1 (plus 1280 already fee). + assert_eq!(Vesting::vesting_balance(&1), Some(2304)); + assert_ok!(Vesting::vest(Some(1).into())); + assert_ok!(Balances::transfer(Some(1).into(), 2, 1536)); + }); } #[test] @@ -263,7 +263,7 @@ fn vested_balance_should_transfer_using_vest_other_with_multi_sched() { let user1_free_balance = Balances::free_balance(&1); assert_eq!(user1_free_balance, 3840); // Account 1 has free balance - // Account 1 has only 256 units unlocking at block 1 (plus 1280 unvested) + // Account 1 has only 256 units unlocking at block 1 (plus 1280 already free). assert_eq!(Vesting::vesting_balance(&1), Some(2304)); assert_ok!(Vesting::vest_other(Some(2).into(), 1)); assert_ok!(Balances::transfer(Some(1).into(), 2, 1536)); From ee1b434a3837397eae1b6dbc6e2325b7cf6d191b Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 13 Jul 2021 16:41:58 -0700 Subject: [PATCH 120/144] Delete duplicate warnings --- frame/vesting/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 4f25167ebbb74..3a52a6c2f8b39 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -182,7 +182,6 @@ pub mod pallet { } fn integrity_test() { - // NOTE: Migration v1 will delete vesting schedules if `MaxVestingSchedules < 1`. assert!(T::MaxVestingSchedules::get() > 0, "`MaxVestingSchedules` must ge greater than 0"); sp_std::if_std! { sp_io::TestExternalities::new_empty().execute_with(|| From 83425fd914db340e4a13b6d900d0388beb755f66 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 13 Jul 2021 16:44:24 -0700 Subject: [PATCH 121/144] Reduce diff in node runtime --- bin/node/runtime/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 4b45d4e817e8b..efa96cb204f9f 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1027,12 +1027,12 @@ parameter_types! { } impl pallet_vesting::Config for Runtime { - type BlockNumberToBalance = ConvertInto; - type Currency = Balances; type Event = Event; - type MaxVestingSchedules = MaxVestingSchedules; + type Currency = Balances; + type BlockNumberToBalance = ConvertInto; type MinVestedTransfer = MinVestedTransfer; type WeightInfo = pallet_vesting::weights::SubstrateWeight; + type MaxVestingSchedules = MaxVestingSchedules; } impl pallet_mmr::Config for Runtime { From 6db9f58b68fd582f23c0c3e7184bda50e353f817 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 13 Jul 2021 17:06:27 -0700 Subject: [PATCH 122/144] Fix benchmark build --- frame/vesting/src/benchmarking.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index 600d76322bd67..e60724ff7c999 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -61,7 +61,7 @@ fn add_vesting_schedules( for _ in 0 .. n { total_locked += locked; - let schedule = VestingInfo::new::(locked, per_block, starting_block.into()); + let schedule = VestingInfo::new(locked, per_block, starting_block.into()); assert_ok!(Vesting::::do_vested_transfer( source_lookup.clone(), target.clone(), @@ -208,7 +208,7 @@ benchmarks! { let per_block = transfer_amount.checked_div(&20u32.into()).unwrap(); expected_balance += transfer_amount; - let vesting_schedule = VestingInfo::new::( + let vesting_schedule = VestingInfo::new( transfer_amount, per_block, 1u32.into(), @@ -246,7 +246,7 @@ benchmarks! { let per_block = transfer_amount.checked_div(&20u32.into()).unwrap(); expected_balance += transfer_amount; - let vesting_schedule = VestingInfo::new::( + let vesting_schedule = VestingInfo::new( transfer_amount, per_block, 1u32.into(), @@ -290,7 +290,7 @@ benchmarks! { ); }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, s - 1) verify { - let expected_schedule = VestingInfo::new::( + let expected_schedule = VestingInfo::new( T::MinVestedTransfer::get() * 20u32.into() * 2u32.into(), T::MinVestedTransfer::get() * 2u32.into(), 1u32.into(), @@ -344,7 +344,7 @@ benchmarks! { assert!(T::Currency::transfer(&caller, &test_dest, expected_balance, ExistenceRequirement::AllowDeath).is_err()); }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, s - 1) verify { - let expected_schedule = VestingInfo::new::( + let expected_schedule = VestingInfo::new( T::MinVestedTransfer::get() * 2u32.into() * 10u32.into(), T::MinVestedTransfer::get() * 2u32.into(), 11u32.into(), From 5e4f653cbdd85132a8759ba6c3099e4260d59f68 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 13 Jul 2021 17:14:36 -0700 Subject: [PATCH 123/144] Upgrade cargo.toml to use 4.0.0-dev --- frame/vesting/Cargo.toml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/frame/vesting/Cargo.toml b/frame/vesting/Cargo.toml index b934fa7bd2184..f38a75481c8cc 100644 --- a/frame/vesting/Cargo.toml +++ b/frame/vesting/Cargo.toml @@ -15,18 +15,18 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] } enumflags2 = { version = "0.6.2" } -sp-std = { version = "3.0.0", default-features = false, path = "../../primitives/std" } -sp-runtime = { version = "3.0.0", default-features = false, path = "../../primitives/runtime" } -frame-support = { version = "3.0.0", default-features = false, path = "../support" } -frame-system = { version = "3.0.0", default-features = false, path = "../system" } -frame-benchmarking = { version = "3.1.0", default-features = false, path = "../benchmarking", optional = true } +sp-std = { version = "4.0.0-dev", default-features = false, path = "../../primitives/std" } +sp-runtime = { version = "4.0.0-dev", default-features = false, path = "../../primitives/runtime" } +frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } +frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } +frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../benchmarking", optional = true } log = { version = "0.4.0", default-features = false } -sp-io = { version = "3.0.0", default-features = false, path = "../../primitives/io" } +sp-io = { version = "4.0.0-dev", default-features = false, path = "../../primitives/io" } [dev-dependencies] -sp-core = { version = "3.0.0", path = "../../primitives/core" } -pallet-balances = { version = "3.0.0", path = "../balances" } -sp-storage = { version = "3.0.0", path = "../../primitives/storage" } +sp-core = { version = "4.0.0-dev", path = "../../primitives/core" } +pallet-balances = { version = "4.0.0-dev", path = "../balances" } +sp-storage = { version = "4.0.0-dev", path = "../../primitives/storage" } hex-literal = "0.3.1" From 6e76ec0152cd6e4aa27d7a869a7c29e03b851913 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Tue, 13 Jul 2021 18:45:56 -0700 Subject: [PATCH 124/144] Cleanup --- frame/vesting/src/lib.rs | 6 +++--- frame/vesting/src/mock.rs | 2 +- frame/vesting/src/tests.rs | 26 +++++++++++++------------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 3a52a6c2f8b39..fa6730ca189d4 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -541,12 +541,12 @@ impl Pallet { /// filter out completed and specified schedules. /// /// Returns a tuple that consists of: - /// - vec of vesting schedules, where completed schedules and those - /// specified by filter are remove. (Note the vec is not checked for respecting + /// - vec of vesting schedules, where completed schedules and those specified + /// by filter are removed. (Note the vec is not checked for respecting /// bounded length.) /// - the amount locked at the current block number based on the given schedules. /// - /// NOTE: the amount locked does not include any schedules that are filtered out. + /// NOTE: the amount locked does not include any schedules that are filtered out via `action`. fn report_schedule_updates( schedules: Vec, T::BlockNumber>>, action: VestingAction, diff --git a/frame/vesting/src/mock.rs b/frame/vesting/src/mock.rs index 8296e4cedfb82..bcdc41c387014 100644 --- a/frame/vesting/src/mock.rs +++ b/frame/vesting/src/mock.rs @@ -86,7 +86,7 @@ impl pallet_balances::Config for Test { } parameter_types! { // Ideally all tests should use a value <= `MinVestedTransfer` when configuring the - // existential deposit. This is ensured the by the integrity test. + // existential deposit. In production this is ensured the by the runtime integrity test. pub const MinVestedTransfer: u64 = 256 * 2; pub static ExistentialDeposit: u64 = 0; pub const MaxVestingSchedules: u32 = 3; diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index 8e2978406541c..326f4097d2cf6 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -213,7 +213,7 @@ fn vested_balance_should_transfer_with_multi_sched() { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { let sched0 = VestingInfo::new(5 * ED, 128, 0); assert_ok!(Vesting::vested_transfer(Some(13).into(), 1, sched0)); - // Total of 10*ED of locked for all the schedules. + // Total 10*ED locked for all the schedules. assert_eq!(Vesting::vesting(&1).unwrap(), vec![sched0, sched0]); let user1_free_balance = Balances::free_balance(&1); @@ -456,7 +456,7 @@ fn vested_transfer_allows_max_schedules() { // so the free balance does not change. assert_eq!(Balances::free_balance(&4), user_4_free_balance); - // Account 4 has fully vested when all the schedules end + // Account 4 has fully vested when all the schedules end, System::set_block_number(::MinVestedTransfer::get() + sched.starting_block()); assert_eq!(Vesting::vesting_balance(&4), Some(0)); // and after unlocking its schedules are removed from storage. @@ -625,18 +625,18 @@ fn merge_schedules_that_have_not_started() { assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); assert_eq!(Balances::usable_balance(&2), 0); - // Add a schedule that is identical to the one that already exists + // Add a schedule that is identical to the one that already exists. assert_ok!(Vesting::vested_transfer(Some(3).into(), 2, sched0)); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched0]); assert_eq!(Balances::usable_balance(&2), 0); assert_ok!(Vesting::merge_schedules(Some(2).into(), 0, 1)); // Since we merged identical schedules, the new schedule finishes at the same - // time as the original, just with double the amount + // time as the original, just with double the amount. let sched1 = VestingInfo::new( sched0.locked() * 2, sched0.per_block() * 2, - 10, // starts at the block the schedules are merged + 10, // Starts at the block the schedules are merged/ ); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched1]); @@ -651,20 +651,20 @@ fn merge_ongoing_schedules() { // Account 2 should already have a vesting schedule. let sched0 = VestingInfo::new( ED * 20, - ED, // Vest over 20 blocks + ED, // Vest over 20 blocks. 10, ); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); let sched1 = VestingInfo::new( ED * 10, - ED, // Vest over 10 blocks - sched0.starting_block() + 5, // Start at block 15 + ED, // Vest over 10 blocks. + sched0.starting_block() + 5, // Start at block 15. ); assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched1)); assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); - // Got to half way through the second schedule where both schedules are actively vesting + // Got to half way through the second schedule where both schedules are actively vesting. let cur_block = 20; System::set_block_number(cur_block); @@ -709,17 +709,17 @@ fn merging_shifts_other_schedules_index() { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { let sched0 = VestingInfo::new( ED * 10, - ED, // Vesting over 10 blocks + ED, // Vesting over 10 blocks. 10, ); let sched1 = VestingInfo::new( ED * 11, - ED, // Vesting over 11 blocks + ED, // Vesting over 11 blocks. 11, ); let sched2 = VestingInfo::new( ED * 12, - ED, // Vesting over 12 blocks + ED, // Vesting over 12 blocks. 12, ); @@ -1117,7 +1117,7 @@ fn vesting_info_validate_works() { fn vesting_info_ending_block_as_balance_works() { // Treats `per_block` 0 as 1. let per_block_0 = VestingInfo::new(256u32, 0u32, 10u32); - assert_eq!(per_block_0.ending_block_as_balance::(), 266); + assert_eq!(per_block_0.ending_block_as_balance::(), 256 + 10); // `per_block >= locked` always results in a schedule ending the block after it starts let per_block_gt_locked = VestingInfo::new(256u32, 256 * 2u32, 10u32); From c524815750b7d89585ae801bd9f86b314bb1548c Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 19 Jul 2021 17:28:12 -0700 Subject: [PATCH 125/144] MaxVestingSchedulesGetter initial impl --- frame/vesting/src/lib.rs | 37 +++++++++++++++++++++------------ frame/vesting/src/migrations.rs | 2 +- frame/vesting/src/tests.rs | 5 +++++ 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index fa6730ca189d4..ef9b594d1fb81 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -133,6 +133,17 @@ impl VestingAction { } } +/// MaxVestingSchedules getter. Guarantees a value of at least 1. +pub struct MaxVestingSchedulesGetter(PhantomData); + +impl Get for MaxVestingSchedulesGetter { + /// MaxVestingSchedules getter. Guarantees a value of at least 1. + fn get() -> u32 { + T::MaxVestingSchedules::get().max(One::one()) + } +} + + #[frame_support::pallet] pub mod pallet { use super::*; @@ -182,7 +193,7 @@ pub mod pallet { } fn integrity_test() { - assert!(T::MaxVestingSchedules::get() > 0, "`MaxVestingSchedules` must ge greater than 0"); + // assert!(T::MaxVestingSchedules::get() > 0, "`MaxVestingSchedules` must ge greater than 0"); sp_std::if_std! { sp_io::TestExternalities::new_empty().execute_with(|| // `Currency::minimum_balance()` needs `TestExternalities`. @@ -202,7 +213,7 @@ pub mod pallet { _, Blake2_128Concat, T::AccountId, - BoundedVec, T::BlockNumber>, T::MaxVestingSchedules>, + BoundedVec, T::BlockNumber>, MaxVestingSchedulesGetter::>, >; /// Storage version of the pallet. @@ -306,8 +317,8 @@ pub mod pallet { /// - Reads: Vesting Storage, Balances Locks, [Sender Account] /// - Writes: Vesting Storage, Balances Locks, [Sender Account] /// # - #[pallet::weight(T::WeightInfo::vest_locked(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) - .max(T::WeightInfo::vest_unlocked(MaxLocksOf::::get(), T::MaxVestingSchedules::get())) + #[pallet::weight(T::WeightInfo::vest_locked(MaxLocksOf::::get(), MaxVestingSchedulesGetter::::get()) + .max(T::WeightInfo::vest_unlocked(MaxLocksOf::::get(), MaxVestingSchedulesGetter::::get())) )] pub fn vest(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; @@ -329,8 +340,8 @@ pub mod pallet { /// - Reads: Vesting Storage, Balances Locks, Target Account /// - Writes: Vesting Storage, Balances Locks, Target Account /// # - #[pallet::weight(T::WeightInfo::vest_other_locked(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) - .max(T::WeightInfo::vest_other_unlocked(MaxLocksOf::::get(), T::MaxVestingSchedules::get())) + #[pallet::weight(T::WeightInfo::vest_other_locked(MaxLocksOf::::get(), MaxVestingSchedulesGetter::::get()) + .max(T::WeightInfo::vest_other_unlocked(MaxLocksOf::::get(), MaxVestingSchedulesGetter::::get())) )] pub fn vest_other( origin: OriginFor, @@ -359,7 +370,7 @@ pub mod pallet { /// - Writes: Vesting Storage, Balances Locks, Target Account, [Sender Account] /// # #[pallet::weight( - T::WeightInfo::vested_transfer(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) + T::WeightInfo::vested_transfer(MaxLocksOf::::get(), MaxVestingSchedulesGetter::::get()) )] pub fn vested_transfer( origin: OriginFor, @@ -390,7 +401,7 @@ pub mod pallet { /// - Writes: Vesting Storage, Balances Locks, Target Account, Source Account /// # #[pallet::weight( - T::WeightInfo::force_vested_transfer(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) + T::WeightInfo::force_vested_transfer(MaxLocksOf::::get(), MaxVestingSchedulesGetter::::get()) )] pub fn force_vested_transfer( origin: OriginFor, @@ -422,8 +433,8 @@ pub mod pallet { /// - `schedule1_index`: index of the first schedule to merge. /// - `schedule2_index`: index of the second schedule to merge. #[pallet::weight( - T::WeightInfo::not_unlocking_merge_schedules(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) - .max(T::WeightInfo::unlocking_merge_schedules(MaxLocksOf::::get(), T::MaxVestingSchedules::get())) + T::WeightInfo::not_unlocking_merge_schedules(MaxLocksOf::::get(), MaxVestingSchedulesGetter::::get()) + .max(T::WeightInfo::unlocking_merge_schedules(MaxLocksOf::::get(), MaxVestingSchedulesGetter::::get())) )] pub fn merge_schedules( origin: OriginFor, @@ -589,7 +600,7 @@ impl Pallet { ) -> Result<(), DispatchError> { let schedules: BoundedVec< VestingInfo, T::BlockNumber>, - T::MaxVestingSchedules, + MaxVestingSchedulesGetter::, > = schedules.try_into().map_err(|_| Error::::AtMaxVestingSchedules)?; if schedules.len() == 0 { @@ -734,8 +745,8 @@ where } ensure!( - Vesting::::decode_len(who).unwrap_or_default() < - T::MaxVestingSchedules::get() as usize, + (Vesting::::decode_len(who).unwrap_or_default() as u32) < + MaxVestingSchedulesGetter::::get(), Error::::AtMaxVestingSchedules ); diff --git a/frame/vesting/src/migrations.rs b/frame/vesting/src/migrations.rs index db4f6092a98dc..606ac97e1a662 100644 --- a/frame/vesting/src/migrations.rs +++ b/frame/vesting/src/migrations.rs @@ -44,7 +44,7 @@ pub(crate) mod v1 { |_key, vesting_info| { reads_writes += 1; let v: Option< - BoundedVec, T::BlockNumber>, T::MaxVestingSchedules>, + BoundedVec, T::BlockNumber>, MaxVestingSchedulesGetter>, > = vec![vesting_info].try_into().ok(); if v.is_none() { diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index 326f4097d2cf6..e48cb4bc23105 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -1154,3 +1154,8 @@ fn per_block_works() { assert_eq!(per_block_1.per_block(), 1u32); assert_eq!(per_block_1.raw_per_block(), 1u32); } + +#[test] +fn min_vested_transfer_getter_works() { + // TODO +} From 830e04bf84e751b47cbe7fbd4f9e36986e0ccb11 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 19 Jul 2021 17:36:24 -0700 Subject: [PATCH 126/144] MinVestedTransfer getter inintial impl --- frame/vesting/src/lib.rs | 19 +++++-------------- frame/vesting/src/tests.rs | 7 ++++++- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index ef9b594d1fb81..9756d3e1fe28b 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -191,19 +191,6 @@ pub mod pallet { fn post_upgrade() -> Result<(), &'static str> { migrations::v1::post_migrate::() } - - fn integrity_test() { - // assert!(T::MaxVestingSchedules::get() > 0, "`MaxVestingSchedules` must ge greater than 0"); - sp_std::if_std! { - sp_io::TestExternalities::new_empty().execute_with(|| - // `Currency::minimum_balance()` needs `TestExternalities`. - assert!( - T::MinVestedTransfer::get() >= ::Currency::minimum_balance(), - "`MinVestedTransfer` must greater than or equal to minimum balance for Currency." - ) - ); - } - } } /// Information regarding the vesting of a given account. @@ -516,7 +503,7 @@ impl Pallet { schedule: VestingInfo, T::BlockNumber>, ) -> DispatchResult { // Validate user inputs. - ensure!(schedule.locked() >= T::MinVestedTransfer::get(), Error::::AmountLow); + ensure!(schedule.locked() >= Self::min_vested_transfer(), Error::::AmountLow); if !schedule.is_valid() { return Err(Error::::InvalidScheduleParams.into()); }; let target = T::Lookup::lookup(target)?; let source = T::Lookup::lookup(source)?; @@ -668,6 +655,10 @@ impl Pallet { Ok((schedules, locked_now)) } + + fn min_vested_transfer() -> BalanceOf { + T::MinVestedTransfer::get().max(T::Currency::minimum_balance()) + } } impl VestingSchedule for Pallet diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index e48cb4bc23105..f435f61529b67 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -1156,6 +1156,11 @@ fn per_block_works() { } #[test] -fn min_vested_transfer_getter_works() { +fn max_vesting_schedules_getter_works() { // TODO } + +#[test] +fn min_vested_transfer_getter_works() { + // TODO +} \ No newline at end of file From 2946f75e9c0859de167b081a9f940e6f3956f298 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 19 Jul 2021 19:40:20 -0700 Subject: [PATCH 127/144] Test MaxVestingSchedules & MinVestedTransfer getters; use getters in benchmarks --- frame/vesting/src/benchmarking.rs | 30 +++++++++---------- frame/vesting/src/mock.rs | 15 +++++++--- frame/vesting/src/tests.rs | 48 +++++++++++++++++++++++++++++-- 3 files changed, 71 insertions(+), 22 deletions(-) diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index e60724ff7c999..ea9fc0ebf081e 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -45,7 +45,7 @@ fn add_vesting_schedules( target: ::Source, n: u32, ) -> Result, &'static str> { - let min_transfer = T::MinVestedTransfer::get(); + let min_transfer = Vesting::::min_vested_transfer(); let locked = min_transfer.checked_mul(&20u32.into()).unwrap(); // Schedule has a duration of 20. let per_block = min_transfer; @@ -78,7 +78,7 @@ fn add_vesting_schedules( benchmarks! { vest_locked { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 1 .. T::MaxVestingSchedules::get(); + let s in 1 .. MaxVestingSchedulesGetter::::get(); let caller: T::AccountId = whitelisted_caller(); let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); @@ -106,7 +106,7 @@ benchmarks! { vest_unlocked { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 1 .. T::MaxVestingSchedules::get(); + let s in 1 .. MaxVestingSchedulesGetter::::get(); let caller: T::AccountId = whitelisted_caller(); let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); @@ -134,7 +134,7 @@ benchmarks! { vest_other_locked { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 1 .. T::MaxVestingSchedules::get(); + let s in 1 .. MaxVestingSchedulesGetter::::get(); let other: T::AccountId = account("other", 0, SEED); let other_lookup: ::Source = T::Lookup::unlookup(other.clone()); @@ -163,7 +163,7 @@ benchmarks! { vest_other_unlocked { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 1 .. T::MaxVestingSchedules::get(); + let s in 1 .. MaxVestingSchedulesGetter::::get(); let other: T::AccountId = account("other", 0, SEED); let other_lookup: ::Source = T::Lookup::unlookup(other.clone()); @@ -192,7 +192,7 @@ benchmarks! { vested_transfer { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 0 .. T::MaxVestingSchedules::get() - 1; + let s in 0 .. MaxVestingSchedulesGetter::::get() - 1; let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); @@ -204,7 +204,7 @@ benchmarks! { // Add one less than max vesting schedules. let mut expected_balance = add_vesting_schedules::(target_lookup.clone(), s)?; - let transfer_amount = T::MinVestedTransfer::get(); + let transfer_amount = Vesting::::min_vested_transfer(); let per_block = transfer_amount.checked_div(&20u32.into()).unwrap(); expected_balance += transfer_amount; @@ -229,7 +229,7 @@ benchmarks! { force_vested_transfer { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 0 .. T::MaxVestingSchedules::get() - 1; + let s in 0 .. MaxVestingSchedulesGetter::::get() - 1; let source: T::AccountId = account("source", 0, SEED); let source_lookup: ::Source = T::Lookup::unlookup(source.clone()); @@ -242,7 +242,7 @@ benchmarks! { // Add one less than max vesting schedules let mut expected_balance = add_vesting_schedules::(target_lookup.clone(), s)?; - let transfer_amount = T::MinVestedTransfer::get(); + let transfer_amount = Vesting::::min_vested_transfer(); let per_block = transfer_amount.checked_div(&20u32.into()).unwrap(); expected_balance += transfer_amount; @@ -267,7 +267,7 @@ benchmarks! { not_unlocking_merge_schedules { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 2 .. T::MaxVestingSchedules::get(); + let s in 2 .. MaxVestingSchedulesGetter::::get(); let caller: T::AccountId = account("caller", 0, SEED); let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); @@ -291,8 +291,8 @@ benchmarks! { }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, s - 1) verify { let expected_schedule = VestingInfo::new( - T::MinVestedTransfer::get() * 20u32.into() * 2u32.into(), - T::MinVestedTransfer::get() * 2u32.into(), + Vesting::::min_vested_transfer() * 20u32.into() * 2u32.into(), + Vesting::::min_vested_transfer() * 2u32.into(), 1u32.into(), ); let expected_index = (s - 2) as usize; @@ -314,7 +314,7 @@ benchmarks! { unlocking_merge_schedules { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 2 .. T::MaxVestingSchedules::get(); + let s in 2 .. MaxVestingSchedulesGetter::::get(); // Destination used just for currency transfers in asserts. let test_dest: T::AccountId = account("test_dest", 0, SEED); @@ -345,8 +345,8 @@ benchmarks! { }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, s - 1) verify { let expected_schedule = VestingInfo::new( - T::MinVestedTransfer::get() * 2u32.into() * 10u32.into(), - T::MinVestedTransfer::get() * 2u32.into(), + Vesting::::min_vested_transfer() * 2u32.into() * 10u32.into(), + Vesting::::min_vested_transfer() * 2u32.into(), 11u32.into(), ); let expected_index = (s - 2) as usize; diff --git a/frame/vesting/src/mock.rs b/frame/vesting/src/mock.rs index bcdc41c387014..a15ddbeae3be0 100644 --- a/frame/vesting/src/mock.rs +++ b/frame/vesting/src/mock.rs @@ -85,11 +85,9 @@ impl pallet_balances::Config for Test { type WeightInfo = (); } parameter_types! { - // Ideally all tests should use a value <= `MinVestedTransfer` when configuring the - // existential deposit. In production this is ensured the by the runtime integrity test. pub const MinVestedTransfer: u64 = 256 * 2; pub static ExistentialDeposit: u64 = 0; - pub const MaxVestingSchedules: u32 = 3; + pub static MaxVestingSchedules: u32 = 3; } impl Config for Test { type BlockNumberToBalance = Identity; @@ -103,11 +101,12 @@ impl Config for Test { pub struct ExtBuilder { existential_deposit: u64, vesting_genesis_config: Option>, + max_vesting_schedules: Option, } impl Default for ExtBuilder { fn default() -> Self { - Self { existential_deposit: 1, vesting_genesis_config: None } + Self { existential_deposit: 1, vesting_genesis_config: None, max_vesting_schedules: None } } } @@ -122,8 +121,16 @@ impl ExtBuilder { self } + pub fn max_vesting_schedules(mut self, max_vesting_schedules: u32) -> Self { + self.max_vesting_schedules = Some(max_vesting_schedules); + self + } + pub fn build(self) -> sp_io::TestExternalities { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); + if let Some(max_vesting_schedules) = self.max_vesting_schedules { + MAX_VESTING_SCHEDULES.with(|v| *v.borrow_mut() = max_vesting_schedules); + } let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); pallet_balances::GenesisConfig:: { balances: vec![ diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index f435f61529b67..a784c7e33478c 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -1155,12 +1155,54 @@ fn per_block_works() { assert_eq!(per_block_1.raw_per_block(), 1u32); } +// MaxVestingSchedules getter always returns at least 1. #[test] fn max_vesting_schedules_getter_works() { - // TODO + ExtBuilder::default() + .existential_deposit(ED) + .max_vesting_schedules(0) + .build() + .execute_with(|| { + // When MaxVestingSchedules is 0, + assert_eq!(::MaxVestingSchedules::get(), 0); + // MaxVestingSchedulesGetter returns 1. + assert_eq!(MaxVestingSchedulesGetter::::get(), 1); + }); + + ExtBuilder::default() + .existential_deposit(ED) + .max_vesting_schedules(2) + .build() + .execute_with(|| { + // When MaxVestingSchedules is greater than 0, + assert_eq!(::MaxVestingSchedules::get(), 2); + // MaxVestingSchedulesGetter returns the MaxVestingSchedules' value. + assert_eq!(MaxVestingSchedulesGetter::::get(), 2); + }); } +// MinVestedTransfer getter always returns at least existential deposit. #[test] fn min_vested_transfer_getter_works() { - // TODO -} \ No newline at end of file + ExtBuilder::default() + // Set ED to 10 * 256. + .existential_deposit(10 * 256) + .build() + .execute_with(|| { + // When MinVestedTransfer is configured below ED, + assert_eq!(::MinVestedTransfer::get(), 2 * 256); + // min_vested_transfer makes sure it is at least ED. + assert_eq!(Vesting::min_vested_transfer(), 10 * 256); + }); + + ExtBuilder::default() + // Set ED to 256. + .existential_deposit(256) + .build() + .execute_with(|| { + // When MinVestedTransfer is greater than ED, + assert_eq!(::MinVestedTransfer::get(), 2 * 256); + // min_vested_transfer does not adjust. + assert_eq!(Vesting::min_vested_transfer(), ::MinVestedTransfer::get()); + }); +} From 82e8061680d9ba98a3fe2e163d6d0accb55d3da4 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Wed, 21 Jul 2021 12:49:45 -0700 Subject: [PATCH 128/144] Run cargo fmt --- frame/executive/src/lib.rs | 17 +- frame/vesting/src/benchmarking.rs | 4 +- frame/vesting/src/lib.rs | 38 +- frame/vesting/src/migrations.rs | 10 +- frame/vesting/src/tests.rs | 553 +++++++++++++++--------------- 5 files changed, 307 insertions(+), 315 deletions(-) diff --git a/frame/executive/src/lib.rs b/frame/executive/src/lib.rs index 65512998252ac..3e2cdd241f6df 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -1226,9 +1226,9 @@ mod tests { let header = new_test_ext(1).execute_with(|| { // Make sure `on_runtime_upgrade` is called. - RUNTIME_VERSION.with(|v| *v.borrow_mut() = sp_version::RuntimeVersion { - spec_version: 1, - ..Default::default() + RUNTIME_VERSION.with(|v| { + *v.borrow_mut() = + sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } }); // Let's build some fake block. @@ -1246,16 +1246,15 @@ mod tests { }); // Reset to get the correct new genesis below. - RUNTIME_VERSION.with(|v| *v.borrow_mut() = sp_version::RuntimeVersion { - spec_version: 0, - ..Default::default() + RUNTIME_VERSION.with(|v| { + *v.borrow_mut() = sp_version::RuntimeVersion { spec_version: 0, ..Default::default() } }); new_test_ext(1).execute_with(|| { // Make sure `on_runtime_upgrade` is called. - RUNTIME_VERSION.with(|v| *v.borrow_mut() = sp_version::RuntimeVersion { - spec_version: 1, - ..Default::default() + RUNTIME_VERSION.with(|v| { + *v.borrow_mut() = + sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } }); >>::execute_block(Block::new(header, vec![xt])); diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index ea9fc0ebf081e..51a55862156bd 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -33,7 +33,7 @@ type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; fn add_locks(who: &T::AccountId, n: u8) { - for id in 0 .. n { + for id in 0..n { let lock_id = [id; 8]; let locked = 256u32; let reasons = WithdrawReasons::TRANSFER | WithdrawReasons::RESERVE; @@ -58,7 +58,7 @@ fn add_vesting_schedules( System::::set_block_number(T::BlockNumber::zero()); let mut total_locked: BalanceOf = Zero::zero(); - for _ in 0 .. n { + for _ in 0..n { total_locked += locked; let schedule = VestingInfo::new(locked, per_block, starting_block.into()); diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 7279ba1946861..b9068a1eae791 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -122,7 +122,7 @@ impl VestingAction { fn pick_schedules<'a, T: Config>( &'a self, schedules: Vec, T::BlockNumber>>, - ) -> impl Iterator, T::BlockNumber>> + 'a { + ) -> impl Iterator, T::BlockNumber>> + 'a { schedules.into_iter().enumerate().filter_map(move |(index, schedule)| { if self.should_remove(index) { None @@ -143,7 +143,6 @@ impl Get for MaxVestingSchedulesGetter { } } - #[frame_support::pallet] pub mod pallet { use super::*; @@ -200,7 +199,7 @@ pub mod pallet { _, Blake2_128Concat, T::AccountId, - BoundedVec, T::BlockNumber>, MaxVestingSchedulesGetter::>, + BoundedVec, T::BlockNumber>, MaxVestingSchedulesGetter>, >; /// Storage version of the pallet. @@ -247,7 +246,9 @@ pub mod pallet { let length_as_balance = T::BlockNumberToBalance::convert(length); let per_block = locked / length_as_balance.max(sp_runtime::traits::One::one()); let vesting_info = VestingInfo::new(locked, per_block, begin); - if !vesting_info.is_valid() { panic!("Invalid VestingInfo params at genesis") }; + if !vesting_info.is_valid() { + panic!("Invalid VestingInfo params at genesis") + }; Vesting::::try_append(who, vesting_info) .expect("Too many vesting schedules at genesis."); @@ -429,7 +430,7 @@ pub mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; if schedule1_index == schedule2_index { - return Ok(()); + return Ok(()) }; let schedule1_index = schedule1_index as usize; let schedule2_index = schedule2_index as usize; @@ -437,8 +438,7 @@ pub mod pallet { let schedules = Self::vesting(&who).ok_or(Error::::NotVesting)?; let merge_action = VestingAction::Merge(schedule1_index, schedule2_index); - let (schedules, locked_now) = - Self::exec_action(schedules.to_vec(), merge_action)?; + let (schedules, locked_now) = Self::exec_action(schedules.to_vec(), merge_action)?; Self::write_vesting(&who, schedules)?; Self::write_lock(&who, locked_now); @@ -469,7 +469,7 @@ impl Pallet { (true, false) => return Some(schedule2), (false, true) => return Some(schedule1), // If neither schedule has ended don't exit early. - _ => {} + _ => {}, } let locked = schedule1 @@ -477,7 +477,10 @@ impl Pallet { .saturating_add(schedule2.locked_at::(now)); // This shouldn't happen because we know at least one ending block is greater than now, // thus at least a schedule a some locked balance. - debug_assert!(!locked.is_zero(), "merge_vesting_info validation checks failed to catch a locked of 0"); + debug_assert!( + !locked.is_zero(), + "merge_vesting_info validation checks failed to catch a locked of 0" + ); let ending_block = schedule1_ending_block.max(schedule2_ending_block); let starting_block = now.max(schedule1.starting_block()).max(schedule2.starting_block()); @@ -503,7 +506,9 @@ impl Pallet { ) -> DispatchResult { // Validate user inputs. ensure!(schedule.locked() >= Self::min_vested_transfer(), Error::::AmountLow); - if !schedule.is_valid() { return Err(Error::::InvalidScheduleParams.into()); }; + if !schedule.is_valid() { + return Err(Error::::InvalidScheduleParams.into()) + }; let target = T::Lookup::lookup(target)?; let source = T::Lookup::lookup(source)?; @@ -586,7 +591,7 @@ impl Pallet { ) -> Result<(), DispatchError> { let schedules: BoundedVec< VestingInfo, T::BlockNumber>, - MaxVestingSchedulesGetter::, + MaxVestingSchedulesGetter, > = schedules.try_into().map_err(|_| Error::::AtMaxVestingSchedules)?; if schedules.len() == 0 { @@ -643,7 +648,7 @@ impl Pallet { } // In the None case there was no new schedule to account for. (schedules, locked_now) - } + }, _ => Self::report_schedule_updates(schedules.to_vec(), action), }; @@ -699,12 +704,14 @@ where starting_block: T::BlockNumber, ) -> DispatchResult { if locked.is_zero() { - return Ok(()); + return Ok(()) } let vesting_schedule = VestingInfo::new(locked, per_block, starting_block); // Check for `per_block` or `locked` of 0. - if !vesting_schedule.is_valid() { return Err(Error::::InvalidScheduleParams.into()); }; + if !vesting_schedule.is_valid() { + return Err(Error::::InvalidScheduleParams.into()) + }; let mut schedules = Self::vesting(who).unwrap_or_default(); @@ -748,8 +755,7 @@ where let schedules = Self::vesting(who).ok_or(Error::::NotVesting)?; let remove_action = VestingAction::Remove(schedule_index as usize); - let (schedules, locked_now) = - Self::exec_action(schedules.to_vec(), remove_action)?; + let (schedules, locked_now) = Self::exec_action(schedules.to_vec(), remove_action)?; Self::write_vesting(&who, schedules)?; Self::write_lock(who, locked_now); diff --git a/frame/vesting/src/migrations.rs b/frame/vesting/src/migrations.rs index 606ac97e1a662..90614c3be5e0a 100644 --- a/frame/vesting/src/migrations.rs +++ b/frame/vesting/src/migrations.rs @@ -44,7 +44,10 @@ pub(crate) mod v1 { |_key, vesting_info| { reads_writes += 1; let v: Option< - BoundedVec, T::BlockNumber>, MaxVestingSchedulesGetter>, + BoundedVec< + VestingInfo, T::BlockNumber>, + MaxVestingSchedulesGetter, + >, > = vec![vesting_info].try_into().ok(); if v.is_none() { @@ -67,7 +70,10 @@ pub(crate) mod v1 { for (_key, schedules) in Vesting::::iter() { // Assert the new bound vec respects size. - assert!(schedules.len() == 1, "A bounded vec with incorrect count of items was created."); + assert!( + schedules.len() == 1, + "A bounded vec with incorrect count of items was created." + ); for s in schedules { // It is ok if this does not pass, but ideally pre-existing schedules would pass diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index 6c873acec0d96..b2291f06f551b 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -39,64 +39,61 @@ where #[test] fn check_vesting_status() { - ExtBuilder::default() - .existential_deposit(ED) - .build() - .execute_with(|| { - let user1_free_balance = Balances::free_balance(&1); - let user2_free_balance = Balances::free_balance(&2); - let user12_free_balance = Balances::free_balance(&12); - assert_eq!(user1_free_balance, ED * 10); // Account 1 has free balance - assert_eq!(user2_free_balance, ED * 20); // Account 2 has free balance - assert_eq!(user12_free_balance, ED * 10); // Account 12 has free balance - let user1_vesting_schedule = VestingInfo::new( - ED * 5, - 128, // Vesting over 10 blocks - 0, - ); - let user2_vesting_schedule = VestingInfo::new( - ED * 20, - ED, // Vesting over 20 blocks - 10, - ); - let user12_vesting_schedule = VestingInfo::new( - ED * 5, - 64, // Vesting over 20 blocks - 10, - ); - assert_eq!(Vesting::vesting(&1).unwrap(), vec![user1_vesting_schedule]); // Account 1 has a vesting schedule - assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); // Account 2 has a vesting schedule - assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); // Account 12 has a vesting schedule - - // Account 1 has only 128 units vested from their illiquid ED * 5 units at block 1 - assert_eq!(Vesting::vesting_balance(&1), Some(128 * 9)); - // Account 2 has their full balance locked - assert_eq!(Vesting::vesting_balance(&2), Some(user2_free_balance)); - // Account 12 has only their illiquid funds locked - assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - ED * 5)); - - System::set_block_number(10); - assert_eq!(System::block_number(), 10); - - // Account 1 has fully vested by block 10 - assert_eq!(Vesting::vesting_balance(&1), Some(0)); - // Account 2 has started vesting by block 10 - assert_eq!(Vesting::vesting_balance(&2), Some(user2_free_balance)); - // Account 12 has started vesting by block 10 - assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - ED * 5)); - - System::set_block_number(30); - assert_eq!(System::block_number(), 30); - - assert_eq!(Vesting::vesting_balance(&1), Some(0)); // Account 1 is still fully vested, and not negative - assert_eq!(Vesting::vesting_balance(&2), Some(0)); // Account 2 has fully vested by block 30 - assert_eq!(Vesting::vesting_balance(&12), Some(0)); // Account 2 has fully vested by block 30 - - // Once we unlock the funds, they are removed from storage. - vest_and_assert_no_vesting::(1); - vest_and_assert_no_vesting::(2); - vest_and_assert_no_vesting::(12); - }); + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let user1_free_balance = Balances::free_balance(&1); + let user2_free_balance = Balances::free_balance(&2); + let user12_free_balance = Balances::free_balance(&12); + assert_eq!(user1_free_balance, ED * 10); // Account 1 has free balance + assert_eq!(user2_free_balance, ED * 20); // Account 2 has free balance + assert_eq!(user12_free_balance, ED * 10); // Account 12 has free balance + let user1_vesting_schedule = VestingInfo::new( + ED * 5, + 128, // Vesting over 10 blocks + 0, + ); + let user2_vesting_schedule = VestingInfo::new( + ED * 20, + ED, // Vesting over 20 blocks + 10, + ); + let user12_vesting_schedule = VestingInfo::new( + ED * 5, + 64, // Vesting over 20 blocks + 10, + ); + assert_eq!(Vesting::vesting(&1).unwrap(), vec![user1_vesting_schedule]); // Account 1 has a vesting schedule + assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); // Account 2 has a vesting schedule + assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); // Account 12 has a vesting schedule + + // Account 1 has only 128 units vested from their illiquid ED * 5 units at block 1 + assert_eq!(Vesting::vesting_balance(&1), Some(128 * 9)); + // Account 2 has their full balance locked + assert_eq!(Vesting::vesting_balance(&2), Some(user2_free_balance)); + // Account 12 has only their illiquid funds locked + assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - ED * 5)); + + System::set_block_number(10); + assert_eq!(System::block_number(), 10); + + // Account 1 has fully vested by block 10 + assert_eq!(Vesting::vesting_balance(&1), Some(0)); + // Account 2 has started vesting by block 10 + assert_eq!(Vesting::vesting_balance(&2), Some(user2_free_balance)); + // Account 12 has started vesting by block 10 + assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - ED * 5)); + + System::set_block_number(30); + assert_eq!(System::block_number(), 30); + + assert_eq!(Vesting::vesting_balance(&1), Some(0)); // Account 1 is still fully vested, and not negative + assert_eq!(Vesting::vesting_balance(&2), Some(0)); // Account 2 has fully vested by block 30 + assert_eq!(Vesting::vesting_balance(&12), Some(0)); // Account 2 has fully vested by block 30 + + // Once we unlock the funds, they are removed from storage. + vest_and_assert_no_vesting::(1); + vest_and_assert_no_vesting::(2); + vest_and_assert_no_vesting::(12); + }); } #[test] @@ -242,23 +239,20 @@ fn vested_balance_should_transfer_using_vest_other() { #[test] fn vested_balance_should_transfer_using_vest_other_with_multi_sched() { - ExtBuilder::default() - .existential_deposit(ED) - .build() - .execute_with(|| { - let sched0 = VestingInfo::new(5*ED, 128, 0); - assert_ok!(Vesting::vested_transfer(Some(13).into(), 1, sched0)); - // Total of 10*ED of locked for all the schedules. - assert_eq!(Vesting::vesting(&1).unwrap(), vec![sched0, sched0]); - - let user1_free_balance = Balances::free_balance(&1); - assert_eq!(user1_free_balance, 3840); // Account 1 has free balance - - // Account 1 has only 256 units unlocking at block 1 (plus 1280 already free). - assert_eq!(Vesting::vesting_balance(&1), Some(2304)); - assert_ok!(Vesting::vest_other(Some(2).into(), 1)); - assert_ok!(Balances::transfer(Some(1).into(), 2, 1536)); - }); + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let sched0 = VestingInfo::new(5 * ED, 128, 0); + assert_ok!(Vesting::vested_transfer(Some(13).into(), 1, sched0)); + // Total of 10*ED of locked for all the schedules. + assert_eq!(Vesting::vesting(&1).unwrap(), vec![sched0, sched0]); + + let user1_free_balance = Balances::free_balance(&1); + assert_eq!(user1_free_balance, 3840); // Account 1 has free balance + + // Account 1 has only 256 units unlocking at block 1 (plus 1280 already free). + assert_eq!(Vesting::vesting_balance(&1), Some(2304)); + assert_ok!(Vesting::vest_other(Some(2).into(), 1)); + assert_ok!(Balances::transfer(Some(1).into(), 2, 1536)); + }); } #[test] @@ -295,122 +289,113 @@ fn extra_balance_should_transfer() { #[test] fn liquid_funds_should_transfer_with_delayed_vesting() { - ExtBuilder::default() - .existential_deposit(256) - .build() - .execute_with(|| { - let user12_free_balance = Balances::free_balance(&12); + ExtBuilder::default().existential_deposit(256).build().execute_with(|| { + let user12_free_balance = Balances::free_balance(&12); - assert_eq!(user12_free_balance, 2560); // Account 12 has free balance - // Account 12 has liquid funds - assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); + assert_eq!(user12_free_balance, 2560); // Account 12 has free balance + // Account 12 has liquid funds + assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); - // Account 12 has delayed vesting - let user12_vesting_schedule = VestingInfo::new( - 256 * 5, - 64, // Vesting over 20 blocks - 10, - ); - assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); + // Account 12 has delayed vesting + let user12_vesting_schedule = VestingInfo::new( + 256 * 5, + 64, // Vesting over 20 blocks + 10, + ); + assert_eq!(Vesting::vesting(&12).unwrap(), vec![user12_vesting_schedule]); - // Account 12 can still send liquid funds - assert_ok!(Balances::transfer(Some(12).into(), 3, 256 * 5)); - }); + // Account 12 can still send liquid funds + assert_ok!(Balances::transfer(Some(12).into(), 3, 256 * 5)); + }); } #[test] fn vested_transfer_works() { - ExtBuilder::default() - .existential_deposit(256) - .build() - .execute_with(|| { - let user3_free_balance = Balances::free_balance(&3); - let user4_free_balance = Balances::free_balance(&4); - assert_eq!(user3_free_balance, 256 * 30); - assert_eq!(user4_free_balance, 256 * 40); - // Account 4 should not have any vesting yet. - assert_eq!(Vesting::vesting(&4), None); - // Make the schedule for the new transfer. - let new_vesting_schedule = VestingInfo::new( - 256 * 5, - 64, // Vesting over 20 blocks - 10, - ); - assert_ok!(Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule)); - // Now account 4 should have vesting. - assert_eq!(Vesting::vesting(&4).unwrap(), vec![new_vesting_schedule]); - // Ensure the transfer happened correctly. - let user3_free_balance_updated = Balances::free_balance(&3); - assert_eq!(user3_free_balance_updated, 256 * 25); - let user4_free_balance_updated = Balances::free_balance(&4); - assert_eq!(user4_free_balance_updated, 256 * 45); - // Account 4 has 5 * 256 locked. - assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5)); - - System::set_block_number(20); - assert_eq!(System::block_number(), 20); - - // Account 4 has 5 * 64 units vested by block 20. - assert_eq!(Vesting::vesting_balance(&4), Some(10 * 64)); - - System::set_block_number(30); - assert_eq!(System::block_number(), 30); - - // Account 4 has fully vested, - assert_eq!(Vesting::vesting_balance(&4), Some(0)); - // and after unlocking its schedules are removed from storage. - vest_and_assert_no_vesting::(4); - }); + ExtBuilder::default().existential_deposit(256).build().execute_with(|| { + let user3_free_balance = Balances::free_balance(&3); + let user4_free_balance = Balances::free_balance(&4); + assert_eq!(user3_free_balance, 256 * 30); + assert_eq!(user4_free_balance, 256 * 40); + // Account 4 should not have any vesting yet. + assert_eq!(Vesting::vesting(&4), None); + // Make the schedule for the new transfer. + let new_vesting_schedule = VestingInfo::new( + 256 * 5, + 64, // Vesting over 20 blocks + 10, + ); + assert_ok!(Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule)); + // Now account 4 should have vesting. + assert_eq!(Vesting::vesting(&4).unwrap(), vec![new_vesting_schedule]); + // Ensure the transfer happened correctly. + let user3_free_balance_updated = Balances::free_balance(&3); + assert_eq!(user3_free_balance_updated, 256 * 25); + let user4_free_balance_updated = Balances::free_balance(&4); + assert_eq!(user4_free_balance_updated, 256 * 45); + // Account 4 has 5 * 256 locked. + assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5)); + + System::set_block_number(20); + assert_eq!(System::block_number(), 20); + + // Account 4 has 5 * 64 units vested by block 20. + assert_eq!(Vesting::vesting_balance(&4), Some(10 * 64)); + + System::set_block_number(30); + assert_eq!(System::block_number(), 30); + + // Account 4 has fully vested, + assert_eq!(Vesting::vesting_balance(&4), Some(0)); + // and after unlocking its schedules are removed from storage. + vest_and_assert_no_vesting::(4); + }); } #[test] fn vested_transfer_correctly_fails() { - ExtBuilder::default() - .existential_deposit(ED) - .build() - .execute_with(|| { - let user2_free_balance = Balances::free_balance(&2); - let user4_free_balance = Balances::free_balance(&4); - assert_eq!(user2_free_balance, ED * 20); - assert_eq!(user4_free_balance, ED * 40); - - // Account 2 should already have a vesting schedule. - let user2_vesting_schedule = VestingInfo::new( - ED * 20, - ED, // Vesting over 20 blocks - 10, - ); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); - - // Fails due to too low transfer amount. - let new_vesting_schedule_too_low = - VestingInfo::new(::MinVestedTransfer::get() - 1, 64, 10); - assert_noop!( - Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule_too_low), - Error::::AmountLow, - ); + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let user2_free_balance = Balances::free_balance(&2); + let user4_free_balance = Balances::free_balance(&4); + assert_eq!(user2_free_balance, ED * 20); + assert_eq!(user4_free_balance, ED * 40); - // `per_block` is 0, which would result in a schedule with infinite duration. - let schedule_per_block_0 = VestingInfo::new(::MinVestedTransfer::get(), 0, 10); - assert_noop!( - Vesting::vested_transfer(Some(13).into(), 4, schedule_per_block_0), - Error::::InvalidScheduleParams, - ); + // Account 2 should already have a vesting schedule. + let user2_vesting_schedule = VestingInfo::new( + ED * 20, + ED, // Vesting over 20 blocks + 10, + ); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); - // `locked` is 0. - let schedule_locked_0 = VestingInfo::new(0, 1, 10); - assert_noop!( - Vesting::vested_transfer(Some(3).into(), 4, schedule_locked_0), - Error::::AmountLow, - ); + // Fails due to too low transfer amount. + let new_vesting_schedule_too_low = + VestingInfo::new(::MinVestedTransfer::get() - 1, 64, 10); + assert_noop!( + Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule_too_low), + Error::::AmountLow, + ); - // Free balance has not changed. - assert_eq!(user2_free_balance, Balances::free_balance(&2)); - assert_eq!(user4_free_balance, Balances::free_balance(&4)); - // Account 4 has no schedules. - vest_and_assert_no_vesting::(4); + // `per_block` is 0, which would result in a schedule with infinite duration. + let schedule_per_block_0 = + VestingInfo::new(::MinVestedTransfer::get(), 0, 10); + assert_noop!( + Vesting::vested_transfer(Some(13).into(), 4, schedule_per_block_0), + Error::::InvalidScheduleParams, + ); - }); + // `locked` is 0. + let schedule_locked_0 = VestingInfo::new(0, 1, 10); + assert_noop!( + Vesting::vested_transfer(Some(3).into(), 4, schedule_locked_0), + Error::::AmountLow, + ); + + // Free balance has not changed. + assert_eq!(user2_free_balance, Balances::free_balance(&2)); + assert_eq!(user4_free_balance, Balances::free_balance(&4)); + // Account 4 has no schedules. + vest_and_assert_no_vesting::(4); + }); } #[test] @@ -425,7 +410,7 @@ fn vested_transfer_allows_max_schedules() { ); // Add max amount schedules to user 4. - for _ in 0 .. max_schedules { + for _ in 0..max_schedules { assert_ok!(Vesting::vested_transfer(Some(13).into(), 4, sched)); } @@ -445,7 +430,9 @@ fn vested_transfer_allows_max_schedules() { assert_eq!(Balances::free_balance(&4), user_4_free_balance); // Account 4 has fully vested when all the schedules end, - System::set_block_number(::MinVestedTransfer::get() + sched.starting_block()); + System::set_block_number( + ::MinVestedTransfer::get() + sched.starting_block(), + ); assert_eq!(Vesting::vesting_balance(&4), Some(0)); // and after unlocking its schedules are removed from storage. vest_and_assert_no_vesting::(4); @@ -454,112 +441,106 @@ fn vested_transfer_allows_max_schedules() { #[test] fn force_vested_transfer_works() { - ExtBuilder::default() - .existential_deposit(ED) - .build() - .execute_with(|| { - let user3_free_balance = Balances::free_balance(&3); - let user4_free_balance = Balances::free_balance(&4); - assert_eq!(user3_free_balance, ED * 30); - assert_eq!(user4_free_balance, ED * 40); - // Account 4 should not have any vesting yet. - assert_eq!(Vesting::vesting(&4), None); - // Make the schedule for the new transfer. - let new_vesting_schedule = VestingInfo::new( - ED * 5, - 64, // Vesting over 20 blocks - 10, - ); + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let user3_free_balance = Balances::free_balance(&3); + let user4_free_balance = Balances::free_balance(&4); + assert_eq!(user3_free_balance, ED * 30); + assert_eq!(user4_free_balance, ED * 40); + // Account 4 should not have any vesting yet. + assert_eq!(Vesting::vesting(&4), None); + // Make the schedule for the new transfer. + let new_vesting_schedule = VestingInfo::new( + ED * 5, + 64, // Vesting over 20 blocks + 10, + ); - assert_noop!( - Vesting::force_vested_transfer(Some(4).into(), 3, 4, new_vesting_schedule), - BadOrigin - ); - assert_ok!(Vesting::force_vested_transfer( - RawOrigin::Root.into(), - 3, - 4, - new_vesting_schedule - )); - // Now account 4 should have vesting. - assert_eq!(Vesting::vesting(&4).unwrap()[0], new_vesting_schedule); - assert_eq!(Vesting::vesting(&4).unwrap().len(), 1); - // Ensure the transfer happened correctly. - let user3_free_balance_updated = Balances::free_balance(&3); - assert_eq!(user3_free_balance_updated, ED * 25); - let user4_free_balance_updated = Balances::free_balance(&4); - assert_eq!(user4_free_balance_updated, ED * 45); - // Account 4 has 5 * ED locked. - assert_eq!(Vesting::vesting_balance(&4), Some(ED * 5)); - - System::set_block_number(20); - assert_eq!(System::block_number(), 20); - - // Account 4 has 5 * 64 units vested by block 20. - assert_eq!(Vesting::vesting_balance(&4), Some(10 * 64)); - - System::set_block_number(30); - assert_eq!(System::block_number(), 30); - - // Account 4 has fully vested, - assert_eq!(Vesting::vesting_balance(&4), Some(0)); - // and after unlocking its schedules are removed from storage. - vest_and_assert_no_vesting::(4); - }); + assert_noop!( + Vesting::force_vested_transfer(Some(4).into(), 3, 4, new_vesting_schedule), + BadOrigin + ); + assert_ok!(Vesting::force_vested_transfer( + RawOrigin::Root.into(), + 3, + 4, + new_vesting_schedule + )); + // Now account 4 should have vesting. + assert_eq!(Vesting::vesting(&4).unwrap()[0], new_vesting_schedule); + assert_eq!(Vesting::vesting(&4).unwrap().len(), 1); + // Ensure the transfer happened correctly. + let user3_free_balance_updated = Balances::free_balance(&3); + assert_eq!(user3_free_balance_updated, ED * 25); + let user4_free_balance_updated = Balances::free_balance(&4); + assert_eq!(user4_free_balance_updated, ED * 45); + // Account 4 has 5 * ED locked. + assert_eq!(Vesting::vesting_balance(&4), Some(ED * 5)); + + System::set_block_number(20); + assert_eq!(System::block_number(), 20); + + // Account 4 has 5 * 64 units vested by block 20. + assert_eq!(Vesting::vesting_balance(&4), Some(10 * 64)); + + System::set_block_number(30); + assert_eq!(System::block_number(), 30); + + // Account 4 has fully vested, + assert_eq!(Vesting::vesting_balance(&4), Some(0)); + // and after unlocking its schedules are removed from storage. + vest_and_assert_no_vesting::(4); + }); } #[test] fn force_vested_transfer_correctly_fails() { - ExtBuilder::default() - .existential_deposit(ED) - .build() - .execute_with(|| { - let user2_free_balance = Balances::free_balance(&2); - let user4_free_balance = Balances::free_balance(&4); - assert_eq!(user2_free_balance, ED * 20); - assert_eq!(user4_free_balance, ED * 40); - // Account 2 should already have a vesting schedule. - let user2_vesting_schedule = VestingInfo::new( - ED * 20, - ED, // Vesting over 20 blocks - 10, - ); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); - - // Too low transfer amount. - let new_vesting_schedule_too_low = - VestingInfo::new(::MinVestedTransfer::get() - 1, 64, 10); - assert_noop!( - Vesting::force_vested_transfer( - RawOrigin::Root.into(), - 3, - 4, - new_vesting_schedule_too_low - ), - Error::::AmountLow, - ); + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let user2_free_balance = Balances::free_balance(&2); + let user4_free_balance = Balances::free_balance(&4); + assert_eq!(user2_free_balance, ED * 20); + assert_eq!(user4_free_balance, ED * 40); + // Account 2 should already have a vesting schedule. + let user2_vesting_schedule = VestingInfo::new( + ED * 20, + ED, // Vesting over 20 blocks + 10, + ); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![user2_vesting_schedule]); - // `per_block` is 0. - let schedule_per_block_0 = - VestingInfo::new(::MinVestedTransfer::get(), 0, 10); - assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 13, 4, schedule_per_block_0), - Error::::InvalidScheduleParams, - ); + // Too low transfer amount. + let new_vesting_schedule_too_low = + VestingInfo::new(::MinVestedTransfer::get() - 1, 64, 10); + assert_noop!( + Vesting::force_vested_transfer( + RawOrigin::Root.into(), + 3, + 4, + new_vesting_schedule_too_low + ), + Error::::AmountLow, + ); - // `locked` is 0. - let schedule_locked_0 = VestingInfo::new(0, 1, 10); - assert_noop!( - Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), - Error::::AmountLow, - ); + // `per_block` is 0. + let schedule_per_block_0 = + VestingInfo::new(::MinVestedTransfer::get(), 0, 10); + assert_noop!( + Vesting::force_vested_transfer(RawOrigin::Root.into(), 13, 4, schedule_per_block_0), + Error::::InvalidScheduleParams, + ); - // Verify no currency transfer happened. - assert_eq!(user2_free_balance, Balances::free_balance(&2)); - assert_eq!(user4_free_balance, Balances::free_balance(&4)); - // Account 4 has no schedules. - vest_and_assert_no_vesting::(4); - }); + // `locked` is 0. + let schedule_locked_0 = VestingInfo::new(0, 1, 10); + assert_noop!( + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0), + Error::::AmountLow, + ); + + // Verify no currency transfer happened. + assert_eq!(user2_free_balance, Balances::free_balance(&2)); + assert_eq!(user4_free_balance, Balances::free_balance(&4)); + // Account 4 has no schedules. + vest_and_assert_no_vesting::(4); + }); } #[test] @@ -574,7 +555,7 @@ fn force_vested_transfer_allows_max_schedules() { ); // Add max amount schedules to user 4. - for _ in 0 .. max_schedules { + for _ in 0..max_schedules { assert_ok!(Vesting::force_vested_transfer(RawOrigin::Root.into(), 13, 4, sched)); } @@ -767,7 +748,8 @@ fn merge_ongoing_and_yet_to_be_started_schedules() { assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0]); // Fast forward to half way through the life of sched1. - let mut cur_block = (sched0.starting_block() + sched0.ending_block_as_balance::()) / 2; + let mut cur_block = + (sched0.starting_block() + sched0.ending_block_as_balance::()) / 2; assert_eq!(cur_block, 20); System::set_block_number(cur_block); @@ -808,7 +790,8 @@ fn merge_ongoing_and_yet_to_be_started_schedules() { let sched2_locked = sched0.locked_at::(cur_block) + sched1.locked_at::(cur_block); // and will end at the max possible block. - let sched2_end = sched0.ending_block_as_balance::() + let sched2_end = sched0 + .ending_block_as_balance::() .max(sched1.ending_block_as_balance::()); let sched2_duration = sched2_end - sched2_start; let sched2_per_block = sched2_locked / sched2_duration; @@ -899,10 +882,11 @@ fn merge_finishing_schedules_does_not_create_a_new_one() { ED, // 30 block duration. 10, ); - assert_ok!(Vesting::vested_transfer(Some(3).into(), 2, sched1)); - assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); + assert_ok!(Vesting::vested_transfer(Some(3).into(), 2, sched1)); + assert_eq!(Vesting::vesting(&2).unwrap(), vec![sched0, sched1]); - let all_scheds_end = sched0.ending_block_as_balance::() + let all_scheds_end = sched0 + .ending_block_as_balance::() .max(sched1.ending_block_as_balance::()); assert_eq!(all_scheds_end, 40); @@ -1069,10 +1053,7 @@ fn merge_vesting_handles_per_block_0() { ED, 0, // Vesting over 256 blocks. 1, ); - assert_eq!( - sched0.ending_block_as_balance::(), - 257 - ); + assert_eq!(sched0.ending_block_as_balance::(), 257); let sched1 = VestingInfo::new( ED * 2, 0, // Vesting over 512 blocks. From a4b8061059f3e55ee730b39a18c874985f625113 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Wed, 21 Jul 2021 14:02:13 -0700 Subject: [PATCH 129/144] Revert MinVestedTransfer & MaxVestingSchedules getters; Add integrity test --- frame/vesting/src/benchmarking.rs | 30 +++++++++--------- frame/vesting/src/lib.rs | 45 +++++++++++--------------- frame/vesting/src/migrations.rs | 2 +- frame/vesting/src/tests.rs | 52 ------------------------------- 4 files changed, 34 insertions(+), 95 deletions(-) diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index 51a55862156bd..a8bbe7bcf7fe5 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -45,7 +45,7 @@ fn add_vesting_schedules( target: ::Source, n: u32, ) -> Result, &'static str> { - let min_transfer = Vesting::::min_vested_transfer(); + let min_transfer = T::MinVestedTransfer::get(); let locked = min_transfer.checked_mul(&20u32.into()).unwrap(); // Schedule has a duration of 20. let per_block = min_transfer; @@ -78,7 +78,7 @@ fn add_vesting_schedules( benchmarks! { vest_locked { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 1 .. MaxVestingSchedulesGetter::::get(); + let s in 1 .. T::MaxVestingSchedules::get(); let caller: T::AccountId = whitelisted_caller(); let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); @@ -106,7 +106,7 @@ benchmarks! { vest_unlocked { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 1 .. MaxVestingSchedulesGetter::::get(); + let s in 1 .. T::MaxVestingSchedules::get(); let caller: T::AccountId = whitelisted_caller(); let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); @@ -134,7 +134,7 @@ benchmarks! { vest_other_locked { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 1 .. MaxVestingSchedulesGetter::::get(); + let s in 1 .. T::MaxVestingSchedules::get(); let other: T::AccountId = account("other", 0, SEED); let other_lookup: ::Source = T::Lookup::unlookup(other.clone()); @@ -163,7 +163,7 @@ benchmarks! { vest_other_unlocked { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 1 .. MaxVestingSchedulesGetter::::get(); + let s in 1 .. T::MaxVestingSchedules::get(); let other: T::AccountId = account("other", 0, SEED); let other_lookup: ::Source = T::Lookup::unlookup(other.clone()); @@ -192,7 +192,7 @@ benchmarks! { vested_transfer { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 0 .. MaxVestingSchedulesGetter::::get() - 1; + let s in 0 .. T::MaxVestingSchedules::get() - 1; let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); @@ -204,7 +204,7 @@ benchmarks! { // Add one less than max vesting schedules. let mut expected_balance = add_vesting_schedules::(target_lookup.clone(), s)?; - let transfer_amount = Vesting::::min_vested_transfer(); + let transfer_amount = T::MinVestedTransfer::get(); let per_block = transfer_amount.checked_div(&20u32.into()).unwrap(); expected_balance += transfer_amount; @@ -229,7 +229,7 @@ benchmarks! { force_vested_transfer { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 0 .. MaxVestingSchedulesGetter::::get() - 1; + let s in 0 .. T::MaxVestingSchedules::get() - 1; let source: T::AccountId = account("source", 0, SEED); let source_lookup: ::Source = T::Lookup::unlookup(source.clone()); @@ -242,7 +242,7 @@ benchmarks! { // Add one less than max vesting schedules let mut expected_balance = add_vesting_schedules::(target_lookup.clone(), s)?; - let transfer_amount = Vesting::::min_vested_transfer(); + let transfer_amount = T::MinVestedTransfer::get(); let per_block = transfer_amount.checked_div(&20u32.into()).unwrap(); expected_balance += transfer_amount; @@ -267,7 +267,7 @@ benchmarks! { not_unlocking_merge_schedules { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 2 .. MaxVestingSchedulesGetter::::get(); + let s in 2 .. T::MaxVestingSchedules::get(); let caller: T::AccountId = account("caller", 0, SEED); let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); @@ -291,8 +291,8 @@ benchmarks! { }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, s - 1) verify { let expected_schedule = VestingInfo::new( - Vesting::::min_vested_transfer() * 20u32.into() * 2u32.into(), - Vesting::::min_vested_transfer() * 2u32.into(), + T::MinVestedTransfer::get() * 20u32.into() * 2u32.into(), + T::MinVestedTransfer::get() * 2u32.into(), 1u32.into(), ); let expected_index = (s - 2) as usize; @@ -314,7 +314,7 @@ benchmarks! { unlocking_merge_schedules { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 2 .. MaxVestingSchedulesGetter::::get(); + let s in 2 .. T::MaxVestingSchedules::get(); // Destination used just for currency transfers in asserts. let test_dest: T::AccountId = account("test_dest", 0, SEED); @@ -345,8 +345,8 @@ benchmarks! { }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, s - 1) verify { let expected_schedule = VestingInfo::new( - Vesting::::min_vested_transfer() * 2u32.into() * 10u32.into(), - Vesting::::min_vested_transfer() * 2u32.into(), + T::MinVestedTransfer::get() * 2u32.into() * 10u32.into(), + T::MinVestedTransfer::get() * 2u32.into(), 11u32.into(), ); let expected_index = (s - 2) as usize; diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index b9068a1eae791..caa74636a5224 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -133,16 +133,6 @@ impl VestingAction { } } -/// MaxVestingSchedules getter. Guarantees a value of at least 1. -pub struct MaxVestingSchedulesGetter(PhantomData); - -impl Get for MaxVestingSchedulesGetter { - /// MaxVestingSchedules getter. Guarantees a value of at least 1. - fn get() -> u32 { - T::MaxVestingSchedules::get().max(One::one()) - } -} - #[frame_support::pallet] pub mod pallet { use super::*; @@ -165,7 +155,8 @@ pub mod pallet { /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; - /// Maximum number of vesting schedules an account may have at a given moment. + /// Maximum number of vesting schedules an account may have at a given moment. This should + /// always be at least 1. If this value is changed the runtime should be reweighed. #[pallet::constant] type MaxVestingSchedules: Get; } @@ -190,6 +181,10 @@ pub mod pallet { fn post_upgrade() -> Result<(), &'static str> { migrations::v1::post_migrate::() } + + fn integrity_test() { + assert!(T::MaxVestingSchedules::get() > 0, "`MaxVestingSchedules` must ge greater than 0"); + } } /// Information regarding the vesting of a given account. @@ -199,7 +194,7 @@ pub mod pallet { _, Blake2_128Concat, T::AccountId, - BoundedVec, T::BlockNumber>, MaxVestingSchedulesGetter>, + BoundedVec, T::BlockNumber>, T::MaxVestingSchedules>, >; /// Storage version of the pallet. @@ -304,8 +299,8 @@ pub mod pallet { /// - Reads: Vesting Storage, Balances Locks, [Sender Account] /// - Writes: Vesting Storage, Balances Locks, [Sender Account] /// # - #[pallet::weight(T::WeightInfo::vest_locked(MaxLocksOf::::get(), MaxVestingSchedulesGetter::::get()) - .max(T::WeightInfo::vest_unlocked(MaxLocksOf::::get(), MaxVestingSchedulesGetter::::get())) + #[pallet::weight(T::WeightInfo::vest_locked(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) + .max(T::WeightInfo::vest_unlocked(MaxLocksOf::::get(), T::MaxVestingSchedules::get())) )] pub fn vest(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; @@ -327,8 +322,8 @@ pub mod pallet { /// - Reads: Vesting Storage, Balances Locks, Target Account /// - Writes: Vesting Storage, Balances Locks, Target Account /// # - #[pallet::weight(T::WeightInfo::vest_other_locked(MaxLocksOf::::get(), MaxVestingSchedulesGetter::::get()) - .max(T::WeightInfo::vest_other_unlocked(MaxLocksOf::::get(), MaxVestingSchedulesGetter::::get())) + #[pallet::weight(T::WeightInfo::vest_other_locked(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) + .max(T::WeightInfo::vest_other_unlocked(MaxLocksOf::::get(), T::MaxVestingSchedules::get())) )] pub fn vest_other( origin: OriginFor, @@ -357,7 +352,7 @@ pub mod pallet { /// - Writes: Vesting Storage, Balances Locks, Target Account, [Sender Account] /// # #[pallet::weight( - T::WeightInfo::vested_transfer(MaxLocksOf::::get(), MaxVestingSchedulesGetter::::get()) + T::WeightInfo::vested_transfer(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) )] pub fn vested_transfer( origin: OriginFor, @@ -388,7 +383,7 @@ pub mod pallet { /// - Writes: Vesting Storage, Balances Locks, Target Account, Source Account /// # #[pallet::weight( - T::WeightInfo::force_vested_transfer(MaxLocksOf::::get(), MaxVestingSchedulesGetter::::get()) + T::WeightInfo::force_vested_transfer(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) )] pub fn force_vested_transfer( origin: OriginFor, @@ -420,8 +415,8 @@ pub mod pallet { /// - `schedule1_index`: index of the first schedule to merge. /// - `schedule2_index`: index of the second schedule to merge. #[pallet::weight( - T::WeightInfo::not_unlocking_merge_schedules(MaxLocksOf::::get(), MaxVestingSchedulesGetter::::get()) - .max(T::WeightInfo::unlocking_merge_schedules(MaxLocksOf::::get(), MaxVestingSchedulesGetter::::get())) + T::WeightInfo::not_unlocking_merge_schedules(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) + .max(T::WeightInfo::unlocking_merge_schedules(MaxLocksOf::::get(), T::MaxVestingSchedules::get())) )] pub fn merge_schedules( origin: OriginFor, @@ -505,7 +500,7 @@ impl Pallet { schedule: VestingInfo, T::BlockNumber>, ) -> DispatchResult { // Validate user inputs. - ensure!(schedule.locked() >= Self::min_vested_transfer(), Error::::AmountLow); + ensure!(schedule.locked() >= T::MinVestedTransfer::get(), Error::::AmountLow); if !schedule.is_valid() { return Err(Error::::InvalidScheduleParams.into()) }; @@ -591,7 +586,7 @@ impl Pallet { ) -> Result<(), DispatchError> { let schedules: BoundedVec< VestingInfo, T::BlockNumber>, - MaxVestingSchedulesGetter, + T::MaxVestingSchedules, > = schedules.try_into().map_err(|_| Error::::AtMaxVestingSchedules)?; if schedules.len() == 0 { @@ -659,10 +654,6 @@ impl Pallet { Ok((schedules, locked_now)) } - - fn min_vested_transfer() -> BalanceOf { - T::MinVestedTransfer::get().max(T::Currency::minimum_balance()) - } } impl VestingSchedule for Pallet @@ -743,7 +734,7 @@ where ensure!( (Vesting::::decode_len(who).unwrap_or_default() as u32) < - MaxVestingSchedulesGetter::::get(), + T::MaxVestingSchedules::get(), Error::::AtMaxVestingSchedules ); diff --git a/frame/vesting/src/migrations.rs b/frame/vesting/src/migrations.rs index 90614c3be5e0a..5f50befc2503f 100644 --- a/frame/vesting/src/migrations.rs +++ b/frame/vesting/src/migrations.rs @@ -46,7 +46,7 @@ pub(crate) mod v1 { let v: Option< BoundedVec< VestingInfo, T::BlockNumber>, - MaxVestingSchedulesGetter, + T::MaxVestingSchedules, >, > = vec![vesting_info].try_into().ok(); diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index b2291f06f551b..bfbcfee737699 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -1123,55 +1123,3 @@ fn per_block_works() { assert_eq!(per_block_1.per_block(), 1u32); assert_eq!(per_block_1.raw_per_block(), 1u32); } - -// MaxVestingSchedules getter always returns at least 1. -#[test] -fn max_vesting_schedules_getter_works() { - ExtBuilder::default() - .existential_deposit(ED) - .max_vesting_schedules(0) - .build() - .execute_with(|| { - // When MaxVestingSchedules is 0, - assert_eq!(::MaxVestingSchedules::get(), 0); - // MaxVestingSchedulesGetter returns 1. - assert_eq!(MaxVestingSchedulesGetter::::get(), 1); - }); - - ExtBuilder::default() - .existential_deposit(ED) - .max_vesting_schedules(2) - .build() - .execute_with(|| { - // When MaxVestingSchedules is greater than 0, - assert_eq!(::MaxVestingSchedules::get(), 2); - // MaxVestingSchedulesGetter returns the MaxVestingSchedules' value. - assert_eq!(MaxVestingSchedulesGetter::::get(), 2); - }); -} - -// MinVestedTransfer getter always returns at least existential deposit. -#[test] -fn min_vested_transfer_getter_works() { - ExtBuilder::default() - // Set ED to 10 * 256. - .existential_deposit(10 * 256) - .build() - .execute_with(|| { - // When MinVestedTransfer is configured below ED, - assert_eq!(::MinVestedTransfer::get(), 2 * 256); - // min_vested_transfer makes sure it is at least ED. - assert_eq!(Vesting::min_vested_transfer(), 10 * 256); - }); - - ExtBuilder::default() - // Set ED to 256. - .existential_deposit(256) - .build() - .execute_with(|| { - // When MinVestedTransfer is greater than ED, - assert_eq!(::MinVestedTransfer::get(), 2 * 256); - // min_vested_transfer does not adjust. - assert_eq!(Vesting::min_vested_transfer(), ::MinVestedTransfer::get()); - }); -} From 1af17083b1fcb401d9b88459f58adea4f0c08c68 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 22 Jul 2021 12:09:11 -0700 Subject: [PATCH 130/144] Make MAX_VESTING_SCHEDULES a const --- bin/node/runtime/src/lib.rs | 3 +- frame/vesting/src/benchmarking.rs | 16 +++++------ frame/vesting/src/lib.rs | 47 ++++++++++++++++++++----------- frame/vesting/src/migrations.rs | 2 +- frame/vesting/src/mock.rs | 14 ++------- frame/vesting/src/tests.rs | 4 +-- 6 files changed, 45 insertions(+), 41 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 85ea16738a11b..c74d788364a2a 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1053,7 +1053,6 @@ impl pallet_society::Config for Runtime { parameter_types! { pub const MinVestedTransfer: Balance = 100 * DOLLARS; - pub const MaxVestingSchedules: u32 = 28; } impl pallet_vesting::Config for Runtime { @@ -1062,7 +1061,7 @@ impl pallet_vesting::Config for Runtime { type BlockNumberToBalance = ConvertInto; type MinVestedTransfer = MinVestedTransfer; type WeightInfo = pallet_vesting::weights::SubstrateWeight; - type MaxVestingSchedules = MaxVestingSchedules; + const MAX_VESTING_SCHEDULES: u32 = 28; } impl pallet_mmr::Config for Runtime { diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index a8bbe7bcf7fe5..ca631d399bf07 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -78,7 +78,7 @@ fn add_vesting_schedules( benchmarks! { vest_locked { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 1 .. T::MaxVestingSchedules::get(); + let s in 1 .. T::MAX_VESTING_SCHEDULES; let caller: T::AccountId = whitelisted_caller(); let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); @@ -106,7 +106,7 @@ benchmarks! { vest_unlocked { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 1 .. T::MaxVestingSchedules::get(); + let s in 1 .. T::MAX_VESTING_SCHEDULES; let caller: T::AccountId = whitelisted_caller(); let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); @@ -134,7 +134,7 @@ benchmarks! { vest_other_locked { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 1 .. T::MaxVestingSchedules::get(); + let s in 1 .. T::MAX_VESTING_SCHEDULES; let other: T::AccountId = account("other", 0, SEED); let other_lookup: ::Source = T::Lookup::unlookup(other.clone()); @@ -163,7 +163,7 @@ benchmarks! { vest_other_unlocked { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 1 .. T::MaxVestingSchedules::get(); + let s in 1 .. T::MAX_VESTING_SCHEDULES; let other: T::AccountId = account("other", 0, SEED); let other_lookup: ::Source = T::Lookup::unlookup(other.clone()); @@ -192,7 +192,7 @@ benchmarks! { vested_transfer { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 0 .. T::MaxVestingSchedules::get() - 1; + let s in 0 .. T::MAX_VESTING_SCHEDULES - 1; let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); @@ -229,7 +229,7 @@ benchmarks! { force_vested_transfer { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 0 .. T::MaxVestingSchedules::get() - 1; + let s in 0 .. T::MAX_VESTING_SCHEDULES - 1; let source: T::AccountId = account("source", 0, SEED); let source_lookup: ::Source = T::Lookup::unlookup(source.clone()); @@ -267,7 +267,7 @@ benchmarks! { not_unlocking_merge_schedules { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 2 .. T::MaxVestingSchedules::get(); + let s in 2 .. T::MAX_VESTING_SCHEDULES; let caller: T::AccountId = account("caller", 0, SEED); let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); @@ -314,7 +314,7 @@ benchmarks! { unlocking_merge_schedules { let l in 0 .. MaxLocksOf::::get() - 1; - let s in 2 .. T::MaxVestingSchedules::get(); + let s in 2 .. T::MAX_VESTING_SCHEDULES; // Destination used just for currency transfers in asserts. let test_dest: T::AccountId = account("test_dest", 0, SEED); diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index caa74636a5224..04190d8409642 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -133,6 +133,14 @@ impl VestingAction { } } +// Wrapper for `T::MAX_VESTING_SCHEDULES` to satisfy `trait Get`. +pub struct MaxVestingSchedulesGet(PhantomData); +impl Get for MaxVestingSchedulesGet { + fn get() -> u32 { + T::MAX_VESTING_SCHEDULES + } +} + #[frame_support::pallet] pub mod pallet { use super::*; @@ -155,10 +163,17 @@ pub mod pallet { /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; - /// Maximum number of vesting schedules an account may have at a given moment. This should - /// always be at least 1. If this value is changed the runtime should be reweighed. - #[pallet::constant] - type MaxVestingSchedules: Get; + /// Maximum number of vesting schedules an account may have at a given moment. + const MAX_VESTING_SCHEDULES: u32; + } + + #[pallet::extra_constants] + impl Pallet { + // TODO: rename to snake case after https://github.com/paritytech/substrate/issues/8826 fixed. + #[allow(non_snake_case)] + fn MaxVestingSchedules() -> u32 { + T::MAX_VESTING_SCHEDULES + } } #[pallet::hooks] @@ -183,7 +198,7 @@ pub mod pallet { } fn integrity_test() { - assert!(T::MaxVestingSchedules::get() > 0, "`MaxVestingSchedules` must ge greater than 0"); + assert!(T::MAX_VESTING_SCHEDULES > 0, "`MaxVestingSchedules` must ge greater than 0"); } } @@ -194,7 +209,7 @@ pub mod pallet { _, Blake2_128Concat, T::AccountId, - BoundedVec, T::BlockNumber>, T::MaxVestingSchedules>, + BoundedVec, T::BlockNumber>, MaxVestingSchedulesGet>, >; /// Storage version of the pallet. @@ -299,8 +314,8 @@ pub mod pallet { /// - Reads: Vesting Storage, Balances Locks, [Sender Account] /// - Writes: Vesting Storage, Balances Locks, [Sender Account] /// # - #[pallet::weight(T::WeightInfo::vest_locked(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) - .max(T::WeightInfo::vest_unlocked(MaxLocksOf::::get(), T::MaxVestingSchedules::get())) + #[pallet::weight(T::WeightInfo::vest_locked(MaxLocksOf::::get(), T::MAX_VESTING_SCHEDULES) + .max(T::WeightInfo::vest_unlocked(MaxLocksOf::::get(), T::MAX_VESTING_SCHEDULES)) )] pub fn vest(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; @@ -322,8 +337,8 @@ pub mod pallet { /// - Reads: Vesting Storage, Balances Locks, Target Account /// - Writes: Vesting Storage, Balances Locks, Target Account /// # - #[pallet::weight(T::WeightInfo::vest_other_locked(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) - .max(T::WeightInfo::vest_other_unlocked(MaxLocksOf::::get(), T::MaxVestingSchedules::get())) + #[pallet::weight(T::WeightInfo::vest_other_locked(MaxLocksOf::::get(), T::MAX_VESTING_SCHEDULES) + .max(T::WeightInfo::vest_other_unlocked(MaxLocksOf::::get(), T::MAX_VESTING_SCHEDULES)) )] pub fn vest_other( origin: OriginFor, @@ -352,7 +367,7 @@ pub mod pallet { /// - Writes: Vesting Storage, Balances Locks, Target Account, [Sender Account] /// # #[pallet::weight( - T::WeightInfo::vested_transfer(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) + T::WeightInfo::vested_transfer(MaxLocksOf::::get(), T::MAX_VESTING_SCHEDULES) )] pub fn vested_transfer( origin: OriginFor, @@ -383,7 +398,7 @@ pub mod pallet { /// - Writes: Vesting Storage, Balances Locks, Target Account, Source Account /// # #[pallet::weight( - T::WeightInfo::force_vested_transfer(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) + T::WeightInfo::force_vested_transfer(MaxLocksOf::::get(), T::MAX_VESTING_SCHEDULES) )] pub fn force_vested_transfer( origin: OriginFor, @@ -415,8 +430,8 @@ pub mod pallet { /// - `schedule1_index`: index of the first schedule to merge. /// - `schedule2_index`: index of the second schedule to merge. #[pallet::weight( - T::WeightInfo::not_unlocking_merge_schedules(MaxLocksOf::::get(), T::MaxVestingSchedules::get()) - .max(T::WeightInfo::unlocking_merge_schedules(MaxLocksOf::::get(), T::MaxVestingSchedules::get())) + T::WeightInfo::not_unlocking_merge_schedules(MaxLocksOf::::get(), T::MAX_VESTING_SCHEDULES) + .max(T::WeightInfo::unlocking_merge_schedules(MaxLocksOf::::get(), T::MAX_VESTING_SCHEDULES)) )] pub fn merge_schedules( origin: OriginFor, @@ -586,7 +601,7 @@ impl Pallet { ) -> Result<(), DispatchError> { let schedules: BoundedVec< VestingInfo, T::BlockNumber>, - T::MaxVestingSchedules, + MaxVestingSchedulesGet, > = schedules.try_into().map_err(|_| Error::::AtMaxVestingSchedules)?; if schedules.len() == 0 { @@ -734,7 +749,7 @@ where ensure!( (Vesting::::decode_len(who).unwrap_or_default() as u32) < - T::MaxVestingSchedules::get(), + T::MAX_VESTING_SCHEDULES, Error::::AtMaxVestingSchedules ); diff --git a/frame/vesting/src/migrations.rs b/frame/vesting/src/migrations.rs index 5f50befc2503f..2cf424c268d24 100644 --- a/frame/vesting/src/migrations.rs +++ b/frame/vesting/src/migrations.rs @@ -46,7 +46,7 @@ pub(crate) mod v1 { let v: Option< BoundedVec< VestingInfo, T::BlockNumber>, - T::MaxVestingSchedules, + MaxVestingSchedulesGet, >, > = vec![vesting_info].try_into().ok(); diff --git a/frame/vesting/src/mock.rs b/frame/vesting/src/mock.rs index a15ddbeae3be0..4c71b6f23464c 100644 --- a/frame/vesting/src/mock.rs +++ b/frame/vesting/src/mock.rs @@ -87,13 +87,12 @@ impl pallet_balances::Config for Test { parameter_types! { pub const MinVestedTransfer: u64 = 256 * 2; pub static ExistentialDeposit: u64 = 0; - pub static MaxVestingSchedules: u32 = 3; } impl Config for Test { type BlockNumberToBalance = Identity; type Currency = Balances; type Event = Event; - type MaxVestingSchedules = MaxVestingSchedules; + const MAX_VESTING_SCHEDULES: u32 = 3; type MinVestedTransfer = MinVestedTransfer; type WeightInfo = (); } @@ -101,12 +100,11 @@ impl Config for Test { pub struct ExtBuilder { existential_deposit: u64, vesting_genesis_config: Option>, - max_vesting_schedules: Option, } impl Default for ExtBuilder { fn default() -> Self { - Self { existential_deposit: 1, vesting_genesis_config: None, max_vesting_schedules: None } + Self { existential_deposit: 1, vesting_genesis_config: None } } } @@ -121,16 +119,8 @@ impl ExtBuilder { self } - pub fn max_vesting_schedules(mut self, max_vesting_schedules: u32) -> Self { - self.max_vesting_schedules = Some(max_vesting_schedules); - self - } - pub fn build(self) -> sp_io::TestExternalities { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); - if let Some(max_vesting_schedules) = self.max_vesting_schedules { - MAX_VESTING_SCHEDULES.with(|v| *v.borrow_mut() = max_vesting_schedules); - } let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); pallet_balances::GenesisConfig:: { balances: vec![ diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index bfbcfee737699..292f3489446de 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -402,7 +402,7 @@ fn vested_transfer_correctly_fails() { fn vested_transfer_allows_max_schedules() { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { let mut user_4_free_balance = Balances::free_balance(&4); - let max_schedules = ::MaxVestingSchedules::get(); + let max_schedules = ::MAX_VESTING_SCHEDULES; let sched = VestingInfo::new( ::MinVestedTransfer::get(), 1, // Vest over 2 * 256 blocks. @@ -547,7 +547,7 @@ fn force_vested_transfer_correctly_fails() { fn force_vested_transfer_allows_max_schedules() { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { let mut user_4_free_balance = Balances::free_balance(&4); - let max_schedules = ::MaxVestingSchedules::get(); + let max_schedules = ::MAX_VESTING_SCHEDULES; let sched = VestingInfo::new( ::MinVestedTransfer::get(), 1, // Vest over 2 * 256 blocks. From 884c1508312cfe60e4ade5e2bbd9b498ed01b904 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 22 Jul 2021 12:11:19 -0700 Subject: [PATCH 131/144] fmt --- frame/vesting/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 04190d8409642..3480933a070a5 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -748,8 +748,7 @@ where } ensure!( - (Vesting::::decode_len(who).unwrap_or_default() as u32) < - T::MAX_VESTING_SCHEDULES, + (Vesting::::decode_len(who).unwrap_or_default() as u32) < T::MAX_VESTING_SCHEDULES, Error::::AtMaxVestingSchedules ); From 2f6ba5ed0aa6c6036bf7c8ecabd7c302d5dbd197 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 22 Jul 2021 13:06:27 -0700 Subject: [PATCH 132/144] WIP: benchmark improvements --- .../src/traits/tokens/currency/lockable.rs | 3 --- frame/vesting/src/benchmarking.rs | 25 +++++++++++-------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/frame/support/src/traits/tokens/currency/lockable.rs b/frame/support/src/traits/tokens/currency/lockable.rs index d65e8acf5929e..26463864a6471 100644 --- a/frame/support/src/traits/tokens/currency/lockable.rs +++ b/frame/support/src/traits/tokens/currency/lockable.rs @@ -103,9 +103,6 @@ pub trait VestingSchedule { /// Remove a vesting schedule for a given account. /// - /// Parameter `schedule_index` is only applicable for implementations that - /// support multiple schedules. - /// /// NOTE: This doesn't alter the free balance of the account. fn remove_vesting_schedule(who: &AccountId, schedule_index: u32) -> DispatchResult; } diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index ca631d399bf07..99723cef2df25 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -32,6 +32,11 @@ const SEED: u32 = 0; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; +// Minimum vested transfer to create a new account. +fn min_new_account_transfer() { + T::MinVestedTransfer.max(T::Currency::minimum_balance()) +} + fn add_locks(who: &T::AccountId, n: u8) { for id in 0..n { let lock_id = [id; 8]; @@ -45,7 +50,7 @@ fn add_vesting_schedules( target: ::Source, n: u32, ) -> Result, &'static str> { - let min_transfer = T::MinVestedTransfer::get(); + let min_transfer = min_new_account_transfer::(); let locked = min_transfer.checked_mul(&20u32.into()).unwrap(); // Schedule has a duration of 20. let per_block = min_transfer; @@ -115,8 +120,8 @@ benchmarks! { add_locks::(&caller, l as u8); add_vesting_schedules::(caller_lookup, s)?; - // At block 22, everything is guaranteed unlocked. - System::::set_block_number(22u32.into()); + // At block 21, everything is unlocked. + System::::set_block_number(21u32.into()); assert_eq!( Vesting::::vesting_balance(&caller), Some(BalanceOf::::zero()), @@ -201,10 +206,10 @@ benchmarks! { let target_lookup: ::Source = T::Lookup::unlookup(target.clone()); // Give target existing locks add_locks::(&target, l as u8); - // Add one less than max vesting schedules. + // Add one vesting schedules. let mut expected_balance = add_vesting_schedules::(target_lookup.clone(), s)?; - let transfer_amount = T::MinVestedTransfer::get(); + let transfer_amount = min_new_account_transfer::(); let per_block = transfer_amount.checked_div(&20u32.into()).unwrap(); expected_balance += transfer_amount; @@ -242,7 +247,7 @@ benchmarks! { // Add one less than max vesting schedules let mut expected_balance = add_vesting_schedules::(target_lookup.clone(), s)?; - let transfer_amount = T::MinVestedTransfer::get(); + let transfer_amount = min_new_account_transfer::(); let per_block = transfer_amount.checked_div(&20u32.into()).unwrap(); expected_balance += transfer_amount; @@ -291,8 +296,8 @@ benchmarks! { }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, s - 1) verify { let expected_schedule = VestingInfo::new( - T::MinVestedTransfer::get() * 20u32.into() * 2u32.into(), - T::MinVestedTransfer::get() * 2u32.into(), + min_new_account_transfer::() * 20u32.into() * 2u32.into(), + min_new_account_transfer::() * 2u32.into(), 1u32.into(), ); let expected_index = (s - 2) as usize; @@ -345,8 +350,8 @@ benchmarks! { }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, s - 1) verify { let expected_schedule = VestingInfo::new( - T::MinVestedTransfer::get() * 2u32.into() * 10u32.into(), - T::MinVestedTransfer::get() * 2u32.into(), + min_new_account_transfer::() * 2u32.into() * 10u32.into(), + min_new_account_transfer::() * 2u32.into(), 11u32.into(), ); let expected_index = (s - 2) as usize; From a94f0c9d0e249b333a99684e2a651e5cdbaa683b Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 22 Jul 2021 13:32:08 -0700 Subject: [PATCH 133/144] Finish benchmark update --- frame/vesting/src/benchmarking.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index 99723cef2df25..803b552d1666f 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -33,8 +33,8 @@ type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; // Minimum vested transfer to create a new account. -fn min_new_account_transfer() { - T::MinVestedTransfer.max(T::Currency::minimum_balance()) +fn min_new_account_transfer() -> BalanceOf { + T::MinVestedTransfer::get().max(T::Currency::minimum_balance()) } fn add_locks(who: &T::AccountId, n: u8) { From dca713223da0730b78b6ad158ae16a6acef86232 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 22 Jul 2021 14:37:33 -0700 Subject: [PATCH 134/144] Add test for transfer to account with less than ed --- frame/vesting/src/lib.rs | 14 +++++++------- frame/vesting/src/migrations.rs | 1 - frame/vesting/src/tests.rs | 31 +++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 3480933a070a5..affe5dd205f42 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -97,7 +97,7 @@ impl Default for Releases { } } -/// Actions to take on a user's `Vesting` storage entry. +/// Actions to take against a user's `Vesting` storage entry. #[derive(Clone, Copy)] enum VestingAction { /// Do not actively remove any schedules. @@ -186,7 +186,7 @@ pub mod pallet { fn on_runtime_upgrade() -> Weight { if StorageVersion::::get() == Releases::V0 { StorageVersion::::put(Releases::V1); - migrations::v1::migrate::() + migrations::v1::migrate::().saturating_add(T::DbWeight::get().reads_writes(1, 1)) } else { T::DbWeight::get().reads(1) } @@ -295,7 +295,7 @@ pub mod pallet { AmountLow, /// An index was out of bounds of the vesting schedules. ScheduleIndexOutOfBounds, - /// Failed to create a new schedule because some parameter was invalid. e.g. `locked` was 0. + /// Failed to create a new schedule because some parameter was invalid. InvalidScheduleParams, } @@ -553,10 +553,10 @@ impl Pallet { /// filter out completed and specified schedules. /// /// Returns a tuple that consists of: - /// - vec of vesting schedules, where completed schedules and those specified + /// - Vec of vesting schedules, where completed schedules and those specified /// by filter are removed. (Note the vec is not checked for respecting /// bounded length.) - /// - the amount locked at the current block number based on the given schedules. + /// - The amount locked at the current block number based on the given schedules. /// /// NOTE: the amount locked does not include any schedules that are filtered out via `action`. fn report_schedule_updates( @@ -634,8 +634,8 @@ impl Pallet { ) -> Result<(Vec, T::BlockNumber>>, BalanceOf), DispatchError> { let (schedules, locked_now) = match action { VestingAction::Merge(idx1, idx2) => { - // The schedule index is based off of the schedule ordering prior to filtering out any - // schedules that may be ending at this block. + // The schedule index is based off of the schedule ordering prior to filtering out + // any schedules that may be ending at this block. let schedule1 = *schedules.get(idx1).ok_or(Error::::ScheduleIndexOutOfBounds)?; let schedule2 = *schedules.get(idx2).ok_or(Error::::ScheduleIndexOutOfBounds)?; diff --git a/frame/vesting/src/migrations.rs b/frame/vesting/src/migrations.rs index 2cf424c268d24..2189e754be716 100644 --- a/frame/vesting/src/migrations.rs +++ b/frame/vesting/src/migrations.rs @@ -69,7 +69,6 @@ pub(crate) mod v1 { assert_eq!(StorageVersion::::get(), Releases::V1); for (_key, schedules) in Vesting::::iter() { - // Assert the new bound vec respects size. assert!( schedules.len() == 1, "A bounded vec with incorrect count of items was created." diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index 292f3489446de..74b5e8c0447f2 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -1123,3 +1123,34 @@ fn per_block_works() { assert_eq!(per_block_1.per_block(), 1u32); assert_eq!(per_block_1.raw_per_block(), 1u32); } + +// When an accounts free balance + schedule.locked is less than ED, the vested transfer will fail. +#[test] +fn vested_transfer_less_than_existential_deposit_fails() { + ExtBuilder::default().existential_deposit(4 * ED).build().execute_with(|| { + // MinVestedTransfer is less the ED. + assert!( + ::Currency::minimum_balance() > + ::MinVestedTransfer::get() + ); + + let sched = + VestingInfo::new(::MinVestedTransfer::get() as u64, 1u64, 10u64); + // The new account balance with the schedule's locked amount would be less than ED. + assert!( + Balances::free_balance(&99) + sched.locked() < + ::Currency::minimum_balance() + ); + + // vested_transfer fails. + assert_noop!( + Vesting::vested_transfer(Some(3).into(), 99, sched), + pallet_balances::Error::::ExistentialDeposit, + ); + // force_vested_transfer fails. + assert_noop!( + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 99, sched), + pallet_balances::Error::::ExistentialDeposit, + ); + }); +} From fd12e179ebac4f9100fee54be68d61aad8d3e904 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 22 Jul 2021 17:47:59 -0700 Subject: [PATCH 135/144] Rm min_new_account_transfer; move sp-io to dev-dep --- frame/vesting/Cargo.toml | 2 +- frame/vesting/src/benchmarking.rs | 19 +++++++------------ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/frame/vesting/Cargo.toml b/frame/vesting/Cargo.toml index f38a75481c8cc..5144c7f27866e 100644 --- a/frame/vesting/Cargo.toml +++ b/frame/vesting/Cargo.toml @@ -21,12 +21,12 @@ frame-support = { version = "4.0.0-dev", default-features = false, path = "../su frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../benchmarking", optional = true } log = { version = "0.4.0", default-features = false } -sp-io = { version = "4.0.0-dev", default-features = false, path = "../../primitives/io" } [dev-dependencies] sp-core = { version = "4.0.0-dev", path = "../../primitives/core" } pallet-balances = { version = "4.0.0-dev", path = "../balances" } sp-storage = { version = "4.0.0-dev", path = "../../primitives/storage" } +sp-io = { version = "4.0.0-dev", default-features = false, path = "../../primitives/io" } hex-literal = "0.3.1" diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index 803b552d1666f..5cdc14c8fdaca 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -32,11 +32,6 @@ const SEED: u32 = 0; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; -// Minimum vested transfer to create a new account. -fn min_new_account_transfer() -> BalanceOf { - T::MinVestedTransfer::get().max(T::Currency::minimum_balance()) -} - fn add_locks(who: &T::AccountId, n: u8) { for id in 0..n { let lock_id = [id; 8]; @@ -50,7 +45,7 @@ fn add_vesting_schedules( target: ::Source, n: u32, ) -> Result, &'static str> { - let min_transfer = min_new_account_transfer::(); + let min_transfer = T::MinVestedTransfer::get(); let locked = min_transfer.checked_mul(&20u32.into()).unwrap(); // Schedule has a duration of 20. let per_block = min_transfer; @@ -209,7 +204,7 @@ benchmarks! { // Add one vesting schedules. let mut expected_balance = add_vesting_schedules::(target_lookup.clone(), s)?; - let transfer_amount = min_new_account_transfer::(); + let transfer_amount = T::MinVestedTransfer::get(); let per_block = transfer_amount.checked_div(&20u32.into()).unwrap(); expected_balance += transfer_amount; @@ -247,7 +242,7 @@ benchmarks! { // Add one less than max vesting schedules let mut expected_balance = add_vesting_schedules::(target_lookup.clone(), s)?; - let transfer_amount = min_new_account_transfer::(); + let transfer_amount = T::MinVestedTransfer::get(); let per_block = transfer_amount.checked_div(&20u32.into()).unwrap(); expected_balance += transfer_amount; @@ -296,8 +291,8 @@ benchmarks! { }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, s - 1) verify { let expected_schedule = VestingInfo::new( - min_new_account_transfer::() * 20u32.into() * 2u32.into(), - min_new_account_transfer::() * 2u32.into(), + T::MinVestedTransfer::get() * 20u32.into() * 2u32.into(), + T::MinVestedTransfer::get() * 2u32.into(), 1u32.into(), ); let expected_index = (s - 2) as usize; @@ -350,8 +345,8 @@ benchmarks! { }: merge_schedules(RawOrigin::Signed(caller.clone()), 0, s - 1) verify { let expected_schedule = VestingInfo::new( - min_new_account_transfer::() * 2u32.into() * 10u32.into(), - min_new_account_transfer::() * 2u32.into(), + T::MinVestedTransfer::get() * 2u32.into() * 10u32.into(), + T::MinVestedTransfer::get() * 2u32.into(), 11u32.into(), ); let expected_index = (s - 2) as usize; From c471f9f6e7bf0cf8b2a2ea3267393a61fd2be417 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 22 Jul 2021 17:58:23 -0700 Subject: [PATCH 136/144] Reduce cargo.toml diff --- frame/vesting/Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frame/vesting/Cargo.toml b/frame/vesting/Cargo.toml index 5144c7f27866e..297ef39c674f7 100644 --- a/frame/vesting/Cargo.toml +++ b/frame/vesting/Cargo.toml @@ -23,11 +23,10 @@ frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = " log = { version = "0.4.0", default-features = false } [dev-dependencies] +sp-io = { version = "4.0.0-dev", default-features = false, path = "../../primitives/io" } sp-core = { version = "4.0.0-dev", path = "../../primitives/core" } pallet-balances = { version = "4.0.0-dev", path = "../balances" } sp-storage = { version = "4.0.0-dev", path = "../../primitives/storage" } -sp-io = { version = "4.0.0-dev", default-features = false, path = "../../primitives/io" } - hex-literal = "0.3.1" [features] From ed9878fd4accea5f0dd87b09aa7eddfba06d4d4d Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 22 Jul 2021 18:07:19 -0700 Subject: [PATCH 137/144] Explain MAX_VESTING_SCHEDULES choice --- bin/node/runtime/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index c74d788364a2a..1ec00d8c0ccdc 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1061,6 +1061,8 @@ impl pallet_vesting::Config for Runtime { type BlockNumberToBalance = ConvertInto; type MinVestedTransfer = MinVestedTransfer; type WeightInfo = pallet_vesting::weights::SubstrateWeight; + // `VestingInfo` encode length is 36bytes. 28 schedules gets encoded as 1009 bytes, which is the + // highest number of schedules that encodes less than 2^10. const MAX_VESTING_SCHEDULES: u32 = 28; } From 7b8250207789814202f1704c5c883e6f6b95deb3 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 9 Aug 2021 12:17:30 -0700 Subject: [PATCH 138/144] Fix after merge --- frame/vesting/src/weights.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/frame/vesting/src/weights.rs b/frame/vesting/src/weights.rs index 6e5d490c52a43..e402137cecf43 100644 --- a/frame/vesting/src/weights.rs +++ b/frame/vesting/src/weights.rs @@ -57,7 +57,7 @@ pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { // Storage: Vesting Vesting (r:1 w:0) // Storage: Balances Locks (r:1 w:1) - fn vest_locked(l: u32, ) -> Weight { + fn vest_locked(l: u32, s: u32, ) -> Weight { (42_983_000 as Weight) // Standard Error: 9_000 .saturating_add((190_000 as Weight).saturating_mul(l as Weight)) @@ -66,7 +66,7 @@ impl WeightInfo for SubstrateWeight { } // Storage: Vesting Vesting (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - fn vest_unlocked(l: u32, ) -> Weight { + fn vest_unlocked(l: u32, s: u32,) -> Weight { (46_213_000 as Weight) // Standard Error: 5_000 .saturating_add((158_000 as Weight).saturating_mul(l as Weight)) @@ -76,7 +76,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Vesting Vesting (r:1 w:0) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) - fn vest_other_locked(l: u32, ) -> Weight { + fn vest_other_locked(l: u32, s: u32,) -> Weight { (42_644_000 as Weight) // Standard Error: 11_000 .saturating_add((202_000 as Weight).saturating_mul(l as Weight)) @@ -86,7 +86,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Vesting Vesting (r:1 w:1) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) - fn vest_other_unlocked(l: u32, ) -> Weight { + fn vest_other_unlocked(l: u32, s: u32,) -> Weight { (45_765_000 as Weight) // Standard Error: 5_000 .saturating_add((159_000 as Weight).saturating_mul(l as Weight)) @@ -96,7 +96,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Vesting Vesting (r:1 w:1) // Storage: System Account (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - fn vested_transfer(l: u32, ) -> Weight { + fn vested_transfer(l: u32, s: u32,) -> Weight { (97_417_000 as Weight) // Standard Error: 11_000 .saturating_add((235_000 as Weight).saturating_mul(l as Weight)) @@ -106,7 +106,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Vesting Vesting (r:1 w:1) // Storage: System Account (r:2 w:2) // Storage: Balances Locks (r:1 w:1) - fn force_vested_transfer(l: u32, ) -> Weight { + fn force_vested_transfer(l: u32, s: u32,) -> Weight { (97_661_000 as Weight) // Standard Error: 16_000 .saturating_add((239_000 as Weight).saturating_mul(l as Weight)) @@ -137,7 +137,7 @@ impl WeightInfo for SubstrateWeight { impl WeightInfo for () { // Storage: Vesting Vesting (r:1 w:0) // Storage: Balances Locks (r:1 w:1) - fn vest_locked(l: u32, ) -> Weight { + fn vest_locked(l: u32, s: u32,) -> Weight { (42_983_000 as Weight) // Standard Error: 9_000 .saturating_add((190_000 as Weight).saturating_mul(l as Weight)) @@ -146,7 +146,7 @@ impl WeightInfo for () { } // Storage: Vesting Vesting (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - fn vest_unlocked(l: u32, ) -> Weight { + fn vest_unlocked(l: u32, s: u32,) -> Weight { (46_213_000 as Weight) // Standard Error: 5_000 .saturating_add((158_000 as Weight).saturating_mul(l as Weight)) @@ -156,7 +156,7 @@ impl WeightInfo for () { // Storage: Vesting Vesting (r:1 w:0) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) - fn vest_other_locked(l: u32, ) -> Weight { + fn vest_other_locked(l: u32, s: u32,) -> Weight { (42_644_000 as Weight) // Standard Error: 11_000 .saturating_add((202_000 as Weight).saturating_mul(l as Weight)) @@ -166,7 +166,7 @@ impl WeightInfo for () { // Storage: Vesting Vesting (r:1 w:1) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) - fn vest_other_unlocked(l: u32, ) -> Weight { + fn vest_other_unlocked(l: u32, s: u32,) -> Weight { (45_765_000 as Weight) // Standard Error: 5_000 .saturating_add((159_000 as Weight).saturating_mul(l as Weight)) @@ -176,7 +176,7 @@ impl WeightInfo for () { // Storage: Vesting Vesting (r:1 w:1) // Storage: System Account (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - fn vested_transfer(l: u32, ) -> Weight { + fn vested_transfer(l: u32, s: u32,) -> Weight { (97_417_000 as Weight) // Standard Error: 11_000 .saturating_add((235_000 as Weight).saturating_mul(l as Weight)) @@ -186,7 +186,7 @@ impl WeightInfo for () { // Storage: Vesting Vesting (r:1 w:1) // Storage: System Account (r:2 w:2) // Storage: Balances Locks (r:1 w:1) - fn force_vested_transfer(l: u32, ) -> Weight { + fn force_vested_transfer(l: u32, s: u32,) -> Weight { (97_661_000 as Weight) // Standard Error: 16_000 .saturating_add((239_000 as Weight).saturating_mul(l as Weight)) From ca99a292560c09cb0cf574ce45bae1b432f6ba74 Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Mon, 9 Aug 2021 13:24:32 -0700 Subject: [PATCH 139/144] Try fix CI complaints --- frame/vesting/src/weights.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/frame/vesting/src/weights.rs b/frame/vesting/src/weights.rs index e402137cecf43..3ef2d9a9775f2 100644 --- a/frame/vesting/src/weights.rs +++ b/frame/vesting/src/weights.rs @@ -57,7 +57,7 @@ pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { // Storage: Vesting Vesting (r:1 w:0) // Storage: Balances Locks (r:1 w:1) - fn vest_locked(l: u32, s: u32, ) -> Weight { + fn vest_locked(l: u32, _s: u32, ) -> Weight { (42_983_000 as Weight) // Standard Error: 9_000 .saturating_add((190_000 as Weight).saturating_mul(l as Weight)) @@ -66,7 +66,7 @@ impl WeightInfo for SubstrateWeight { } // Storage: Vesting Vesting (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - fn vest_unlocked(l: u32, s: u32,) -> Weight { + fn vest_unlocked(l: u32, _s: u32,) -> Weight { (46_213_000 as Weight) // Standard Error: 5_000 .saturating_add((158_000 as Weight).saturating_mul(l as Weight)) @@ -76,7 +76,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Vesting Vesting (r:1 w:0) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) - fn vest_other_locked(l: u32, s: u32,) -> Weight { + fn vest_other_locked(l: u32, _s: u32,) -> Weight { (42_644_000 as Weight) // Standard Error: 11_000 .saturating_add((202_000 as Weight).saturating_mul(l as Weight)) @@ -86,7 +86,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Vesting Vesting (r:1 w:1) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) - fn vest_other_unlocked(l: u32, s: u32,) -> Weight { + fn vest_other_unlocked(l: u32, _s: u32,) -> Weight { (45_765_000 as Weight) // Standard Error: 5_000 .saturating_add((159_000 as Weight).saturating_mul(l as Weight)) @@ -96,7 +96,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Vesting Vesting (r:1 w:1) // Storage: System Account (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - fn vested_transfer(l: u32, s: u32,) -> Weight { + fn vested_transfer(l: u32, _s: u32,) -> Weight { (97_417_000 as Weight) // Standard Error: 11_000 .saturating_add((235_000 as Weight).saturating_mul(l as Weight)) @@ -106,7 +106,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Vesting Vesting (r:1 w:1) // Storage: System Account (r:2 w:2) // Storage: Balances Locks (r:1 w:1) - fn force_vested_transfer(l: u32, s: u32,) -> Weight { + fn force_vested_transfer(l: u32, _s: u32,) -> Weight { (97_661_000 as Weight) // Standard Error: 16_000 .saturating_add((239_000 as Weight).saturating_mul(l as Weight)) @@ -137,7 +137,7 @@ impl WeightInfo for SubstrateWeight { impl WeightInfo for () { // Storage: Vesting Vesting (r:1 w:0) // Storage: Balances Locks (r:1 w:1) - fn vest_locked(l: u32, s: u32,) -> Weight { + fn vest_locked(l: u32, _s: u32,) -> Weight { (42_983_000 as Weight) // Standard Error: 9_000 .saturating_add((190_000 as Weight).saturating_mul(l as Weight)) @@ -146,7 +146,7 @@ impl WeightInfo for () { } // Storage: Vesting Vesting (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - fn vest_unlocked(l: u32, s: u32,) -> Weight { + fn vest_unlocked(l: u32, _s: u32,) -> Weight { (46_213_000 as Weight) // Standard Error: 5_000 .saturating_add((158_000 as Weight).saturating_mul(l as Weight)) @@ -156,7 +156,7 @@ impl WeightInfo for () { // Storage: Vesting Vesting (r:1 w:0) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) - fn vest_other_locked(l: u32, s: u32,) -> Weight { + fn vest_other_locked(l: u32, _s: u32,) -> Weight { (42_644_000 as Weight) // Standard Error: 11_000 .saturating_add((202_000 as Weight).saturating_mul(l as Weight)) @@ -166,7 +166,7 @@ impl WeightInfo for () { // Storage: Vesting Vesting (r:1 w:1) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) - fn vest_other_unlocked(l: u32, s: u32,) -> Weight { + fn vest_other_unlocked(l: u32, _s: u32,) -> Weight { (45_765_000 as Weight) // Standard Error: 5_000 .saturating_add((159_000 as Weight).saturating_mul(l as Weight)) @@ -176,7 +176,7 @@ impl WeightInfo for () { // Storage: Vesting Vesting (r:1 w:1) // Storage: System Account (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - fn vested_transfer(l: u32, s: u32,) -> Weight { + fn vested_transfer(l: u32, _s: u32,) -> Weight { (97_417_000 as Weight) // Standard Error: 11_000 .saturating_add((235_000 as Weight).saturating_mul(l as Weight)) @@ -186,7 +186,7 @@ impl WeightInfo for () { // Storage: Vesting Vesting (r:1 w:1) // Storage: System Account (r:2 w:2) // Storage: Balances Locks (r:1 w:1) - fn force_vested_transfer(l: u32, s: u32,) -> Weight { + fn force_vested_transfer(l: u32, _s: u32,) -> Weight { (97_661_000 as Weight) // Standard Error: 16_000 .saturating_add((239_000 as Weight).saturating_mul(l as Weight)) From bb3b591835cee4abbe7f4f11ee3e57fd87aa0c9c Mon Sep 17 00:00:00 2001 From: Parity Benchmarking Bot Date: Mon, 9 Aug 2021 20:34:55 +0000 Subject: [PATCH 140/144] cargo run --quiet --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_vesting --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/vesting/src/weights.rs --template=./.maintain/frame-weight-template.hbs --- frame/vesting/src/weights.rs | 207 +++++++++++++++++++++-------------- 1 file changed, 123 insertions(+), 84 deletions(-) diff --git a/frame/vesting/src/weights.rs b/frame/vesting/src/weights.rs index 3ef2d9a9775f2..27728ddc247f3 100644 --- a/frame/vesting/src/weights.rs +++ b/frame/vesting/src/weights.rs @@ -18,26 +18,29 @@ //! Autogenerated weights for pallet_vesting //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2021-08-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2021-08-09, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: -// target/debug/substrate +// target/release/substrate // benchmark -// --pallet=pallet-vesting +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_vesting // --extrinsic=* +// --execution=wasm // --wasm-execution=compiled +// --heap-pages=4096 // --output=./frame/vesting/src/weights.rs -// --template=.maintain/frame-weight-template.hbs +// --template=./.maintain/frame-weight-template.hbs + #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] -use frame_support::{ - traits::Get, - weights::{constants::RocksDbWeight, Weight}, -}; +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use sp_std::marker::PhantomData; /// Weight functions needed for pallet_vesting. @@ -55,79 +58,97 @@ pub trait WeightInfo { /// Weights for pallet_vesting using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Vesting Vesting (r:1 w:0) + // Storage: Vesting Vesting (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - fn vest_locked(l: u32, _s: u32, ) -> Weight { - (42_983_000 as Weight) - // Standard Error: 9_000 - .saturating_add((190_000 as Weight).saturating_mul(l as Weight)) + fn vest_locked(l: u32, s: u32, ) -> Weight { + (50_701_000 as Weight) + // Standard Error: 1_000 + .saturating_add((137_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 3_000 + .saturating_add((179_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } // Storage: Vesting Vesting (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - fn vest_unlocked(l: u32, _s: u32,) -> Weight { - (46_213_000 as Weight) - // Standard Error: 5_000 - .saturating_add((158_000 as Weight).saturating_mul(l as Weight)) + fn vest_unlocked(l: u32, s: u32, ) -> Weight { + (50_775_000 as Weight) + // Standard Error: 1_000 + .saturating_add((112_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 3_000 + .saturating_add((111_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } - // Storage: Vesting Vesting (r:1 w:0) + // Storage: Vesting Vesting (r:1 w:1) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) - fn vest_other_locked(l: u32, _s: u32,) -> Weight { - (42_644_000 as Weight) - // Standard Error: 11_000 - .saturating_add((202_000 as Weight).saturating_mul(l as Weight)) + fn vest_other_locked(l: u32, s: u32, ) -> Weight { + (51_968_000 as Weight) + // Standard Error: 2_000 + .saturating_add((134_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 5_000 + .saturating_add((153_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } // Storage: Vesting Vesting (r:1 w:1) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) - fn vest_other_unlocked(l: u32, _s: u32,) -> Weight { - (45_765_000 as Weight) - // Standard Error: 5_000 - .saturating_add((159_000 as Weight).saturating_mul(l as Weight)) + fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { + (51_737_000 as Weight) + // Standard Error: 2_000 + .saturating_add((119_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 4_000 + .saturating_add((92_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } // Storage: Vesting Vesting (r:1 w:1) // Storage: System Account (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - fn vested_transfer(l: u32, _s: u32,) -> Weight { - (97_417_000 as Weight) - // Standard Error: 11_000 - .saturating_add((235_000 as Weight).saturating_mul(l as Weight)) + fn vested_transfer(l: u32, s: u32, ) -> Weight { + (88_567_000 as Weight) + // Standard Error: 6_000 + .saturating_add((126_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 13_000 + .saturating_add((41_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } // Storage: Vesting Vesting (r:1 w:1) // Storage: System Account (r:2 w:2) // Storage: Balances Locks (r:1 w:1) - fn force_vested_transfer(l: u32, _s: u32,) -> Weight { - (97_661_000 as Weight) - // Standard Error: 16_000 - .saturating_add((239_000 as Weight).saturating_mul(l as Weight)) + fn force_vested_transfer(l: u32, s: u32, ) -> Weight { + (88_133_000 as Weight) + // Standard Error: 6_000 + .saturating_add((115_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 12_000 + .saturating_add((54_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } - - fn not_unlocking_merge_schedules(l: u32, s: u32) -> Weight { - (670_637_000 as Weight) - // Standard Error: 2_507_000 - .saturating_add((4_049_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 4_675_000 - .saturating_add((784_000 as Weight).saturating_mul(s as Weight)) + // Storage: Vesting Vesting (r:1 w:1) + // Storage: Balances Locks (r:1 w:1) + // Storage: System Account (r:1 w:1) + fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { + (54_275_000 as Weight) + // Standard Error: 2_000 + .saturating_add((135_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 4_000 + .saturating_add((152_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - - fn unlocking_merge_schedules(l: u32, _s: u32) -> Weight { - (728_296_000 as Weight) - // Standard Error: 3_129_000 - .saturating_add((2_908_000 as Weight).saturating_mul(l as Weight)) + // Storage: Vesting Vesting (r:1 w:1) + // Storage: Balances Locks (r:1 w:1) + // Storage: System Account (r:1 w:1) + fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { + (53_366_000 as Weight) + // Standard Error: 2_000 + .saturating_add((148_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 4_000 + .saturating_add((154_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } @@ -135,79 +156,97 @@ impl WeightInfo for SubstrateWeight { // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Vesting Vesting (r:1 w:0) + // Storage: Vesting Vesting (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - fn vest_locked(l: u32, _s: u32,) -> Weight { - (42_983_000 as Weight) - // Standard Error: 9_000 - .saturating_add((190_000 as Weight).saturating_mul(l as Weight)) + fn vest_locked(l: u32, s: u32, ) -> Weight { + (50_701_000 as Weight) + // Standard Error: 1_000 + .saturating_add((137_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 3_000 + .saturating_add((179_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } // Storage: Vesting Vesting (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - fn vest_unlocked(l: u32, _s: u32,) -> Weight { - (46_213_000 as Weight) - // Standard Error: 5_000 - .saturating_add((158_000 as Weight).saturating_mul(l as Weight)) + fn vest_unlocked(l: u32, s: u32, ) -> Weight { + (50_775_000 as Weight) + // Standard Error: 1_000 + .saturating_add((112_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 3_000 + .saturating_add((111_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } - // Storage: Vesting Vesting (r:1 w:0) + // Storage: Vesting Vesting (r:1 w:1) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) - fn vest_other_locked(l: u32, _s: u32,) -> Weight { - (42_644_000 as Weight) - // Standard Error: 11_000 - .saturating_add((202_000 as Weight).saturating_mul(l as Weight)) + fn vest_other_locked(l: u32, s: u32, ) -> Weight { + (51_968_000 as Weight) + // Standard Error: 2_000 + .saturating_add((134_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 5_000 + .saturating_add((153_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } // Storage: Vesting Vesting (r:1 w:1) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) - fn vest_other_unlocked(l: u32, _s: u32,) -> Weight { - (45_765_000 as Weight) - // Standard Error: 5_000 - .saturating_add((159_000 as Weight).saturating_mul(l as Weight)) + fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { + (51_737_000 as Weight) + // Standard Error: 2_000 + .saturating_add((119_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 4_000 + .saturating_add((92_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } // Storage: Vesting Vesting (r:1 w:1) // Storage: System Account (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - fn vested_transfer(l: u32, _s: u32,) -> Weight { - (97_417_000 as Weight) - // Standard Error: 11_000 - .saturating_add((235_000 as Weight).saturating_mul(l as Weight)) + fn vested_transfer(l: u32, s: u32, ) -> Weight { + (88_567_000 as Weight) + // Standard Error: 6_000 + .saturating_add((126_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 13_000 + .saturating_add((41_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } // Storage: Vesting Vesting (r:1 w:1) // Storage: System Account (r:2 w:2) // Storage: Balances Locks (r:1 w:1) - fn force_vested_transfer(l: u32, _s: u32,) -> Weight { - (97_661_000 as Weight) - // Standard Error: 16_000 - .saturating_add((239_000 as Weight).saturating_mul(l as Weight)) + fn force_vested_transfer(l: u32, s: u32, ) -> Weight { + (88_133_000 as Weight) + // Standard Error: 6_000 + .saturating_add((115_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 12_000 + .saturating_add((54_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } - - fn not_unlocking_merge_schedules(l: u32, s: u32) -> Weight { - (670_637_000 as Weight) - // Standard Error: 2_507_000 - .saturating_add((4_049_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 4_675_000 - .saturating_add((784_000 as Weight).saturating_mul(s as Weight)) + // Storage: Vesting Vesting (r:1 w:1) + // Storage: Balances Locks (r:1 w:1) + // Storage: System Account (r:1 w:1) + fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { + (54_275_000 as Weight) + // Standard Error: 2_000 + .saturating_add((135_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 4_000 + .saturating_add((152_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - - fn unlocking_merge_schedules(l: u32, _s: u32) -> Weight { - (728_296_000 as Weight) - // Standard Error: 3_129_000 - .saturating_add((2_908_000 as Weight).saturating_mul(l as Weight)) + // Storage: Vesting Vesting (r:1 w:1) + // Storage: Balances Locks (r:1 w:1) + // Storage: System Account (r:1 w:1) + fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { + (53_366_000 as Weight) + // Standard Error: 2_000 + .saturating_add((148_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 4_000 + .saturating_add((154_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } From 23e15d9e21e3ba5988dd92e743ccaf53aac41f74 Mon Sep 17 00:00:00 2001 From: Parity Benchmarking Bot Date: Tue, 10 Aug 2021 10:34:20 +0000 Subject: [PATCH 141/144] cargo run --quiet --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_vesting --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/vesting/src/weights.rs --template=./.maintain/frame-weight-template.hbs --- frame/vesting/src/weights.rs | 126 +++++++++++++++++------------------ 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/frame/vesting/src/weights.rs b/frame/vesting/src/weights.rs index 27728ddc247f3..3ccc1a5bda362 100644 --- a/frame/vesting/src/weights.rs +++ b/frame/vesting/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_vesting //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2021-08-09, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2021-08-10, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: @@ -61,22 +61,22 @@ impl WeightInfo for SubstrateWeight { // Storage: Vesting Vesting (r:1 w:1) // Storage: Balances Locks (r:1 w:1) fn vest_locked(l: u32, s: u32, ) -> Weight { - (50_701_000 as Weight) + (50_642_000 as Weight) // Standard Error: 1_000 - .saturating_add((137_000 as Weight).saturating_mul(l as Weight)) + .saturating_add((144_000 as Weight).saturating_mul(l as Weight)) // Standard Error: 3_000 - .saturating_add((179_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((177_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } // Storage: Vesting Vesting (r:1 w:1) // Storage: Balances Locks (r:1 w:1) fn vest_unlocked(l: u32, s: u32, ) -> Weight { - (50_775_000 as Weight) + (50_830_000 as Weight) // Standard Error: 1_000 - .saturating_add((112_000 as Weight).saturating_mul(l as Weight)) + .saturating_add((115_000 as Weight).saturating_mul(l as Weight)) // Standard Error: 3_000 - .saturating_add((111_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((112_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } @@ -84,11 +84,11 @@ impl WeightInfo for SubstrateWeight { // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) fn vest_other_locked(l: u32, s: u32, ) -> Weight { - (51_968_000 as Weight) - // Standard Error: 2_000 - .saturating_add((134_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 5_000 - .saturating_add((153_000 as Weight).saturating_mul(s as Weight)) + (52_151_000 as Weight) + // Standard Error: 1_000 + .saturating_add((130_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 3_000 + .saturating_add((162_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } @@ -96,11 +96,11 @@ impl WeightInfo for SubstrateWeight { // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { - (51_737_000 as Weight) - // Standard Error: 2_000 - .saturating_add((119_000 as Weight).saturating_mul(l as Weight)) + (51_009_000 as Weight) // Standard Error: 4_000 - .saturating_add((92_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((123_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 9_000 + .saturating_add((118_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } @@ -108,11 +108,11 @@ impl WeightInfo for SubstrateWeight { // Storage: System Account (r:1 w:1) // Storage: Balances Locks (r:1 w:1) fn vested_transfer(l: u32, s: u32, ) -> Weight { - (88_567_000 as Weight) - // Standard Error: 6_000 - .saturating_add((126_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 13_000 - .saturating_add((41_000 as Weight).saturating_mul(s as Weight)) + (89_517_000 as Weight) + // Standard Error: 5_000 + .saturating_add((114_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 10_000 + .saturating_add((23_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } @@ -120,11 +120,11 @@ impl WeightInfo for SubstrateWeight { // Storage: System Account (r:2 w:2) // Storage: Balances Locks (r:1 w:1) fn force_vested_transfer(l: u32, s: u32, ) -> Weight { - (88_133_000 as Weight) + (87_903_000 as Weight) // Standard Error: 6_000 - .saturating_add((115_000 as Weight).saturating_mul(l as Weight)) + .saturating_add((121_000 as Weight).saturating_mul(l as Weight)) // Standard Error: 12_000 - .saturating_add((54_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((56_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } @@ -132,11 +132,11 @@ impl WeightInfo for SubstrateWeight { // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - (54_275_000 as Weight) + (54_463_000 as Weight) // Standard Error: 2_000 - .saturating_add((135_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 4_000 - .saturating_add((152_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((123_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 5_000 + .saturating_add((149_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } @@ -144,11 +144,11 @@ impl WeightInfo for SubstrateWeight { // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - (53_366_000 as Weight) - // Standard Error: 2_000 - .saturating_add((148_000 as Weight).saturating_mul(l as Weight)) + (53_674_000 as Weight) + // Standard Error: 1_000 + .saturating_add((137_000 as Weight).saturating_mul(l as Weight)) // Standard Error: 4_000 - .saturating_add((154_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((152_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } @@ -159,22 +159,22 @@ impl WeightInfo for () { // Storage: Vesting Vesting (r:1 w:1) // Storage: Balances Locks (r:1 w:1) fn vest_locked(l: u32, s: u32, ) -> Weight { - (50_701_000 as Weight) + (50_642_000 as Weight) // Standard Error: 1_000 - .saturating_add((137_000 as Weight).saturating_mul(l as Weight)) + .saturating_add((144_000 as Weight).saturating_mul(l as Weight)) // Standard Error: 3_000 - .saturating_add((179_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((177_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } // Storage: Vesting Vesting (r:1 w:1) // Storage: Balances Locks (r:1 w:1) fn vest_unlocked(l: u32, s: u32, ) -> Weight { - (50_775_000 as Weight) + (50_830_000 as Weight) // Standard Error: 1_000 - .saturating_add((112_000 as Weight).saturating_mul(l as Weight)) + .saturating_add((115_000 as Weight).saturating_mul(l as Weight)) // Standard Error: 3_000 - .saturating_add((111_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((112_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -182,11 +182,11 @@ impl WeightInfo for () { // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) fn vest_other_locked(l: u32, s: u32, ) -> Weight { - (51_968_000 as Weight) - // Standard Error: 2_000 - .saturating_add((134_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 5_000 - .saturating_add((153_000 as Weight).saturating_mul(s as Weight)) + (52_151_000 as Weight) + // Standard Error: 1_000 + .saturating_add((130_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 3_000 + .saturating_add((162_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } @@ -194,11 +194,11 @@ impl WeightInfo for () { // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { - (51_737_000 as Weight) - // Standard Error: 2_000 - .saturating_add((119_000 as Weight).saturating_mul(l as Weight)) + (51_009_000 as Weight) // Standard Error: 4_000 - .saturating_add((92_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((123_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 9_000 + .saturating_add((118_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } @@ -206,11 +206,11 @@ impl WeightInfo for () { // Storage: System Account (r:1 w:1) // Storage: Balances Locks (r:1 w:1) fn vested_transfer(l: u32, s: u32, ) -> Weight { - (88_567_000 as Weight) - // Standard Error: 6_000 - .saturating_add((126_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 13_000 - .saturating_add((41_000 as Weight).saturating_mul(s as Weight)) + (89_517_000 as Weight) + // Standard Error: 5_000 + .saturating_add((114_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 10_000 + .saturating_add((23_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } @@ -218,11 +218,11 @@ impl WeightInfo for () { // Storage: System Account (r:2 w:2) // Storage: Balances Locks (r:1 w:1) fn force_vested_transfer(l: u32, s: u32, ) -> Weight { - (88_133_000 as Weight) + (87_903_000 as Weight) // Standard Error: 6_000 - .saturating_add((115_000 as Weight).saturating_mul(l as Weight)) + .saturating_add((121_000 as Weight).saturating_mul(l as Weight)) // Standard Error: 12_000 - .saturating_add((54_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((56_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } @@ -230,11 +230,11 @@ impl WeightInfo for () { // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - (54_275_000 as Weight) + (54_463_000 as Weight) // Standard Error: 2_000 - .saturating_add((135_000 as Weight).saturating_mul(l as Weight)) - // Standard Error: 4_000 - .saturating_add((152_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((123_000 as Weight).saturating_mul(l as Weight)) + // Standard Error: 5_000 + .saturating_add((149_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } @@ -242,11 +242,11 @@ impl WeightInfo for () { // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { - (53_366_000 as Weight) - // Standard Error: 2_000 - .saturating_add((148_000 as Weight).saturating_mul(l as Weight)) + (53_674_000 as Weight) + // Standard Error: 1_000 + .saturating_add((137_000 as Weight).saturating_mul(l as Weight)) // Standard Error: 4_000 - .saturating_add((154_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((152_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } From 0a11ab1bed6c95be9b3d92e143d7c5f59e2283a5 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 16 Aug 2021 13:10:07 +0200 Subject: [PATCH 142/144] fmt --- Cargo.lock | 1 + frame/vesting/src/lib.rs | 20 +++++++++++--------- frame/vesting/src/migrations.rs | 2 +- frame/vesting/src/tests.rs | 7 ++++--- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f410b288e9188..c85d939e99376 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5681,6 +5681,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "log 0.4.14", "pallet-balances", "parity-scale-codec", "sp-core", diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index affe5dd205f42..7e4a11fbd5c36 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -275,8 +275,8 @@ pub mod pallet { T::AccountId = "AccountId", BalanceOf = "Balance", T::BlockNumber = "BlockNumber" )] pub enum Event { - /// The amount vested has been updated. This could indicate a change in funds available. The - /// balance given is the amount which is left unvested (and thus locked). + /// The amount vested has been updated. This could indicate a change in funds available. + /// The balance given is the amount which is left unvested (and thus locked). /// \[account, unvested\] VestingUpdated(T::AccountId, BalanceOf), /// An \[account\] has become fully vested. @@ -410,10 +410,11 @@ pub mod pallet { Self::do_vested_transfer(source, target, schedule) } - /// Merge two vesting schedules together, creating a new vesting schedule that unlocks over the - /// highest possible start and end blocks. If both schedules have already started the current - /// block will be used as the schedule start; with the caveat that if one schedule is finished - /// by the current block, the other will be treated as the new merged schedule, unmodified. + /// Merge two vesting schedules together, creating a new vesting schedule that unlocks over + /// the highest possible start and end blocks. If both schedules have already started the + /// current block will be used as the schedule start; with the caveat that if one schedule + /// is finished by the current block, the other will be treated as the new merged schedule, + /// unmodified. /// /// NOTE: If `schedule1_index == schedule2_index` this is a no-op. /// NOTE: This will unlock all schedules through the current block prior to merging. @@ -421,7 +422,8 @@ pub mod pallet { /// and both will be removed. /// /// Merged schedule attributes: - /// - `starting_block`: `MAX(schedule1.starting_block, scheduled2.starting_block, current_block)`. + /// - `starting_block`: `MAX(schedule1.starting_block, scheduled2.starting_block, + /// current_block)`. /// - `ending_block`: `MAX(schedule1.ending_block, schedule2.ending_block)`. /// - `locked`: `schedule1.locked_at(current_block) + schedule2.locked_at(current_block)`. /// @@ -640,8 +642,8 @@ impl Pallet { let schedule2 = *schedules.get(idx2).ok_or(Error::::ScheduleIndexOutOfBounds)?; // The length of `schedules` decreases by 2 here since we filter out 2 schedules. - // Thus we know below that we can push the new merged schedule without error (assuming - // initial state was valid). + // Thus we know below that we can push the new merged schedule without error + // (assuming initial state was valid). let (mut schedules, mut locked_now) = Self::report_schedule_updates(schedules.to_vec(), action); diff --git a/frame/vesting/src/migrations.rs b/frame/vesting/src/migrations.rs index 2189e754be716..086257d285ea0 100644 --- a/frame/vesting/src/migrations.rs +++ b/frame/vesting/src/migrations.rs @@ -88,7 +88,7 @@ pub(crate) mod v1 { log::debug!( target: "runtime::vesting", - "migration: Vesting storage version v1 POST migration checks succesful!" + "migration: Vesting storage version v1 POST migration checks successful!" ); Ok(()) } diff --git a/frame/vesting/src/tests.rs b/frame/vesting/src/tests.rs index 74b5e8c0447f2..2a6dd0520c3b0 100644 --- a/frame/vesting/src/tests.rs +++ b/frame/vesting/src/tests.rs @@ -643,8 +643,8 @@ fn merge_ongoing_schedules() { assert_ok!(Vesting::merge_schedules(Some(2).into(), 0, 1)); - // Merging schedules un-vests all pre-existing schedules prior to merging, which is reflected - // in account 2's updated usable balance. + // Merging schedules un-vests all pre-existing schedules prior to merging, which is + // reflected in account 2's updated usable balance. let sched0_vested_now = sched0.per_block() * (cur_block - sched0.starting_block()); let sched1_vested_now = sched1.per_block() * (cur_block - sched1.starting_block()); assert_eq!(Balances::usable_balance(&2), sched0_vested_now + sched1_vested_now); @@ -937,7 +937,8 @@ fn merge_finished_and_yet_to_be_started_schedules() { System::set_block_number(30); - // At block 30, sched0 has finished unlocking while sched1 and sched2 are still fully locked, + // At block 30, sched0 has finished unlocking while sched1 and sched2 are still fully + // locked, assert_eq!(Vesting::vesting_balance(&2), Some(sched1.locked() + sched2.locked())); // but since we have not vested usable balance is still 0. assert_eq!(Balances::usable_balance(&2), 0); From 81c064cb1c9386e1916279f9d898b900e2289a9b Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 19 Aug 2021 16:23:09 -0700 Subject: [PATCH 143/144] trigger --- frame/vesting/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 7e4a11fbd5c36..7cdf55fb54c16 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -769,3 +769,4 @@ where Ok(()) } } + From 1b3c0c589dd23b51f3fb0de10c86de0b09015efc Mon Sep 17 00:00:00 2001 From: emostov <32168567+emostov@users.noreply.github.com> Date: Thu, 19 Aug 2021 16:49:03 -0700 Subject: [PATCH 144/144] fmt --- frame/vesting/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 7cdf55fb54c16..7e4a11fbd5c36 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -769,4 +769,3 @@ where Ok(()) } } -