From c106dc6588a76ca4607068ae80b5cb7da890ea80 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Sun, 6 Dec 2020 21:41:19 +0800 Subject: [PATCH 1/6] Add Balances Locks --- src/frame/balances.rs | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/frame/balances.rs b/src/frame/balances.rs index 598421c8f1..05c6eb9254 100644 --- a/src/frame/balances.rs +++ b/src/frame/balances.rs @@ -25,7 +25,7 @@ use codec::{ Encode, }; use core::marker::PhantomData; -use frame_support::Parameter; +use frame_support::{Parameter, traits::LockIdentifier}; use sp_runtime::traits::{ AtLeast32Bit, MaybeSerialize, @@ -80,6 +80,47 @@ pub struct TotalIssuanceStore { pub _runtime: PhantomData, } +/// The locks of the balances module. +#[derive(Clone, Debug, Eq, PartialEq, Store, Encode, Decode)] +pub struct LocksStore<'a, T: Balances> { + #[store(returns = Vec>)] + /// Account to retrieve the balance locks for. + pub account_id: &'a T::AccountId, +} + +/// A single lock on a balance. There can be many of these on an account and they "overlap", so the +/// same balance is frozen by multiple locks. +#[derive(Clone, PartialEq, Eq, Encode, Decode)] +pub struct BalanceLock { + /// An identifier for this lock. Only one lock may be in existence for each identifier. + pub id: LockIdentifier, + /// The amount which the free balance may not drop below when this lock is in effect. + pub amount: Balance, + /// If true, then the lock remains in effect even for payment of transaction fees. + pub reasons: Reasons, +} + +impl Debug for BalanceLock { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_struct("BalanceLock") + .field("id", &String::from_utf8_lossy(&self.id)) + .field("amount", &self.amount) + .field("reasons", &self.reasons) + .finish() + } +} + +/// Simplified reasons for withdrawing balance. +#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, Debug)] +pub enum Reasons { + /// Paying system transaction fees. + Fee, + /// Any reason other than paying system transaction fees. + Misc, + /// Any reason at all. + All, +} + /// Transfer some liquid free balance to another account. /// /// `transfer` will set the `FreeBalance` of the sender and receiver. From fb0b79fd42cc61d6716e8f1be487c3726116e7f2 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Sun, 6 Dec 2020 21:57:09 +0800 Subject: [PATCH 2/6] Pass fmt --- src/frame/balances.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/frame/balances.rs b/src/frame/balances.rs index 05c6eb9254..7df135296c 100644 --- a/src/frame/balances.rs +++ b/src/frame/balances.rs @@ -25,7 +25,10 @@ use codec::{ Encode, }; use core::marker::PhantomData; -use frame_support::{Parameter, traits::LockIdentifier}; +use frame_support::{ + traits::LockIdentifier, + Parameter, +}; use sp_runtime::traits::{ AtLeast32Bit, MaybeSerialize, From c652eece283e67fd69785b6b0ce3a70f84a8c25f Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Tue, 12 Jan 2021 22:20:03 +0800 Subject: [PATCH 3/6] Add tests for Balances Locks In order to write this test, I just added the new staking::BondCall :). --- src/extrinsic/signer.rs | 2 +- src/frame/balances.rs | 41 +++++++++++++++++++++++++++++++++ src/frame/staking.rs | 50 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) diff --git a/src/extrinsic/signer.rs b/src/extrinsic/signer.rs index 7898ecf07d..1a00c817b3 100644 --- a/src/extrinsic/signer.rs +++ b/src/extrinsic/signer.rs @@ -87,7 +87,7 @@ where /// Increment the nonce. pub fn increment_nonce(&mut self) { - self.nonce = self.nonce.map(|nonce| nonce + 1.into()); + self.nonce = self.nonce.map(|nonce| nonce + 1u32.into()); } /// Returns the signer. diff --git a/src/frame/balances.rs b/src/frame/balances.rs index 7df135296c..564b5f2310 100644 --- a/src/frame/balances.rs +++ b/src/frame/balances.rs @@ -225,6 +225,47 @@ mod tests { assert_ne!(info.data.free, 0); } + #[async_std::test] + #[cfg(feature = "integration-tests")] + async fn test_state_balance_lock() { + use crate::{ + frame::staking::{ + BondCallExt, + RewardDestination, + }, + runtimes::KusamaRuntime as RT, + ClientBuilder, + }; + + env_logger::try_init().ok(); + let alice = PairSigner::::new(AccountKeyring::Alice.pair()); + let client = ClientBuilder::::new().build().await.unwrap(); + + client + .bond_and_watch( + &alice, + AccountKeyring::Bob.to_account_id(), + 100_000_000_000, + RewardDestination::Stash, + ) + .await + .unwrap(); + + let alice_locks = client + .locks(&AccountKeyring::Alice.to_account_id(), None) + .await + .unwrap(); + + assert_eq!( + alice_locks, + vec![BalanceLock { + id: *b"staking ", + amount: 100_000_000_000, + reasons: Reasons::All, + }] + ); + } + #[async_std::test] async fn test_transfer_error() { env_logger::try_init().ok(); diff --git a/src/frame/staking.rs b/src/frame/staking.rs index 06aa598143..c00a1318e0 100644 --- a/src/frame/staking.rs +++ b/src/frame/staking.rs @@ -196,6 +196,19 @@ pub struct NominateCall { pub targets: Vec, } +/// Take the origin account as a stash and lock up `value` of its balance. +/// `controller` will be the account that controls it. +#[derive(Call, Encode, Debug)] +pub struct BondCall { + /// Tٗhe contrller account + pub contrller: T::AccountId, + /// Lock up `value` of its balance. + #[codec(compact)] + pub value: T::Balance, + /// Destination of Staking reward. + pub payee: RewardDestination, +} + #[cfg(test)] #[cfg(feature = "integration-tests")] mod tests { @@ -324,6 +337,43 @@ mod tests { Ok(()) } + #[async_std::test] + async fn test_bond() -> Result<(), Error> { + env_logger::try_init().ok(); + let alice = PairSigner::::new(AccountKeyring::Alice.pair()); + let client = ClientBuilder::::new().build().await.unwrap(); + + let bond = client + .bond_and_watch( + &alice, + AccountKeyring::Bob.to_account_id(), + 100_000_000_000, + RewardDestination::Stash, + ) + .await; + + assert_matches!(bond, Ok(ExtrinsicSuccess {block: _, extrinsic: _, events}) => { + // TOOD: this is unsatisfying – can we do better? + assert_eq!(events.len(), 3); + }); + + let bond_again = client + .bond_and_watch( + &alice, + AccountKeyring::Bob.to_account_id(), + 100_000_000_000, + RewardDestination::Stash, + ) + .await; + + assert_matches!(bond_again, Err(Error::Runtime(RuntimeError::Module(module_err))) => { + assert_eq!(module_err.module, "Staking"); + assert_eq!(module_err.error, "AlreadyBonded"); + }); + + Ok(()) + } + #[async_std::test] async fn test_total_issuance_is_okay() -> Result<(), Error> { env_logger::try_init().ok(); From a875e146f7c8934ded8ab96399aad7c4f1beca04 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Tue, 12 Jan 2021 22:26:45 +0800 Subject: [PATCH 4/6] . --- src/frame/staking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frame/staking.rs b/src/frame/staking.rs index c00a1318e0..361d77df26 100644 --- a/src/frame/staking.rs +++ b/src/frame/staking.rs @@ -200,7 +200,7 @@ pub struct NominateCall { /// `controller` will be the account that controls it. #[derive(Call, Encode, Debug)] pub struct BondCall { - /// Tٗhe contrller account + /// Tٗhe controller account pub contrller: T::AccountId, /// Lock up `value` of its balance. #[codec(compact)] From 976a8f1faab0134fcc1a9ce8ce83d0190499e472 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Tue, 12 Jan 2021 22:51:18 +0800 Subject: [PATCH 5/6] In case you want to run multiple tests at the same time --- src/frame/balances.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/frame/balances.rs b/src/frame/balances.rs index 564b5f2310..0c447c8d69 100644 --- a/src/frame/balances.rs +++ b/src/frame/balances.rs @@ -238,26 +238,26 @@ mod tests { }; env_logger::try_init().ok(); - let alice = PairSigner::::new(AccountKeyring::Alice.pair()); + let bob = PairSigner::::new(AccountKeyring::Bob.pair()); let client = ClientBuilder::::new().build().await.unwrap(); client .bond_and_watch( - &alice, - AccountKeyring::Bob.to_account_id(), + &bob, + AccountKeyring::Charlie.to_account_id(), 100_000_000_000, RewardDestination::Stash, ) .await .unwrap(); - let alice_locks = client - .locks(&AccountKeyring::Alice.to_account_id(), None) + let locks = client + .locks(&AccountKeyring::Bob.to_account_id(), None) .await .unwrap(); assert_eq!( - alice_locks, + locks, vec![BalanceLock { id: *b"staking ", amount: 100_000_000_000, From 3c71a0d031dc85374bd996a61c78e0f29096ca37 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Fri, 15 Jan 2021 10:15:51 +0800 Subject: [PATCH 6/6] Return Result in integration test --- src/frame/balances.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/frame/balances.rs b/src/frame/balances.rs index 0c447c8d69..4d9a331bf8 100644 --- a/src/frame/balances.rs +++ b/src/frame/balances.rs @@ -227,7 +227,7 @@ mod tests { #[async_std::test] #[cfg(feature = "integration-tests")] - async fn test_state_balance_lock() { + async fn test_state_balance_lock() -> Result<(), crate::Error> { use crate::{ frame::staking::{ BondCallExt, @@ -239,7 +239,7 @@ mod tests { env_logger::try_init().ok(); let bob = PairSigner::::new(AccountKeyring::Bob.pair()); - let client = ClientBuilder::::new().build().await.unwrap(); + let client = ClientBuilder::::new().build().await?; client .bond_and_watch( @@ -248,13 +248,11 @@ mod tests { 100_000_000_000, RewardDestination::Stash, ) - .await - .unwrap(); + .await?; let locks = client .locks(&AccountKeyring::Bob.to_account_id(), None) - .await - .unwrap(); + .await?; assert_eq!( locks, @@ -264,6 +262,8 @@ mod tests { reasons: Reasons::All, }] ); + + Ok(()) } #[async_std::test]