Skip to content

Commit

Permalink
Add Balances Locks (#197)
Browse files Browse the repository at this point in the history
* Add Balances Locks

* Pass fmt

* Add tests for Balances Locks

In order to write this test, I just added the new staking::BondCall :).

* .

* In case you want to run multiple tests at the same time

* Return Result in integration test
  • Loading branch information
liuchengxu authored Jan 15, 2021
1 parent 754b184 commit 5a0201c
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 1 deletion.
87 changes: 86 additions & 1 deletion src/frame/balances.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ use codec::{
Encode,
};
use core::marker::PhantomData;
use frame_support::Parameter;
use frame_support::{
traits::LockIdentifier,
Parameter,
};
use sp_runtime::traits::{
AtLeast32Bit,
MaybeSerialize,
Expand Down Expand Up @@ -80,6 +83,47 @@ pub struct TotalIssuanceStore<T: Balances> {
pub _runtime: PhantomData<T>,
}

/// The locks of the balances module.
#[derive(Clone, Debug, Eq, PartialEq, Store, Encode, Decode)]
pub struct LocksStore<'a, T: Balances> {
#[store(returns = Vec<BalanceLock<T::Balance>>)]
/// 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<Balance> {
/// 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<Balance: Debug> Debug for BalanceLock<Balance> {
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.
Expand Down Expand Up @@ -181,6 +225,47 @@ mod tests {
assert_ne!(info.data.free, 0);
}

#[async_std::test]
#[cfg(feature = "integration-tests")]
async fn test_state_balance_lock() -> Result<(), crate::Error> {
use crate::{
frame::staking::{
BondCallExt,
RewardDestination,
},
runtimes::KusamaRuntime as RT,
ClientBuilder,
};

env_logger::try_init().ok();
let bob = PairSigner::<RT, _>::new(AccountKeyring::Bob.pair());
let client = ClientBuilder::<RT>::new().build().await?;

client
.bond_and_watch(
&bob,
AccountKeyring::Charlie.to_account_id(),
100_000_000_000,
RewardDestination::Stash,
)
.await?;

let locks = client
.locks(&AccountKeyring::Bob.to_account_id(), None)
.await?;

assert_eq!(
locks,
vec![BalanceLock {
id: *b"staking ",
amount: 100_000_000_000,
reasons: Reasons::All,
}]
);

Ok(())
}

#[async_std::test]
async fn test_transfer_error() {
env_logger::try_init().ok();
Expand Down
50 changes: 50 additions & 0 deletions src/frame/staking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,19 @@ pub struct NominateCall<T: Staking> {
pub targets: Vec<T::Address>,
}

/// 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: Staking> {
/// Tٗhe controller account
pub contrller: T::AccountId,
/// Lock up `value` of its balance.
#[codec(compact)]
pub value: T::Balance,
/// Destination of Staking reward.
pub payee: RewardDestination<T::AccountId>,
}

#[cfg(test)]
#[cfg(feature = "integration-tests")]
mod tests {
Expand Down Expand Up @@ -324,6 +337,43 @@ mod tests {
Ok(())
}

#[async_std::test]
async fn test_bond() -> Result<(), Error> {
env_logger::try_init().ok();
let alice = PairSigner::<RT, _>::new(AccountKeyring::Alice.pair());
let client = ClientBuilder::<RT>::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();
Expand Down

0 comments on commit 5a0201c

Please sign in to comment.