Skip to content

Commit

Permalink
add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
salman01zp committed Nov 6, 2023
1 parent f759fae commit 6403364
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 35 deletions.
35 changes: 15 additions & 20 deletions pallets/roles/src/impls.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
use super::*;
use frame_support::{
pallet_prelude::DispatchResult,
traits::{ExistenceRequirement, WithdrawReasons},
};
use sp_runtime::{traits::AccountIdConversion, Saturating};
use frame_support::{pallet_prelude::DispatchResult, traits::WithdrawReasons};
use sp_runtime::Saturating;
use tangle_primitives::{roles::RoleType, traits::roles::RolesHandler};

/// Implements RolesHandler for the pallet.
Expand All @@ -24,11 +21,8 @@ impl<T: Config> RolesHandler<T::AccountId> for Pallet<T> {
address: T::AccountId,
_offence: tangle_primitives::jobs::ValidatorOffence,
) -> sp_runtime::DispatchResult {
let mut ledger = Self::ledger(&address).ok_or(Error::<T>::InvalidStashController)?;
let slash_amount = 1000u64;
let (_imbalance, _missing) = T::Currency::slash(&address, slash_amount.into());
ledger.total_locked = ledger.total_locked.saturating_sub(slash_amount.into());
Self::update_ledger(&address, &ledger);
Self::slash(address, slash_amount.into())?;
Ok(())
}
}
Expand All @@ -41,9 +35,17 @@ impl<T: Config> Pallet<T> {
Self::ledger(&stash).map(|l| l.total_locked).unwrap_or_default()
}

/// Get AccountId assigned to the pallet.
pub(crate) fn account_id() -> T::AccountId {
T::PalletId::get().into_account_truncating()
/// Slash staker's balance by the given amount.
pub(crate) fn slash(
address: T::AccountId,
slash_amount: T::CurrencyBalance,
) -> sp_runtime::DispatchResult {
let mut ledger = Self::ledger(&address).ok_or(Error::<T>::InvalidStashController)?;
let (_imbalance, _missing) = T::Currency::slash(&address, slash_amount.into());
ledger.total_locked = ledger.total_locked.saturating_sub(slash_amount.into());
Self::update_ledger(&address, &ledger);
Self::deposit_event(Event::Slashed { account: address, amount: slash_amount });
Ok(())
}

/// Update the ledger for the staker.
Expand All @@ -65,18 +67,11 @@ impl<T: Config> Pallet<T> {
}

/// Unbond the full amount of the stash.
pub(super) fn unbond_and_withdraw(ledger: &RoleStakingLedger<T>) -> DispatchResult {
pub(super) fn unbond(ledger: &RoleStakingLedger<T>) -> DispatchResult {
let stash = ledger.stash.clone();
if ledger.total_locked > T::Currency::minimum_balance() {
// Remove the lock.
T::Currency::remove_lock(ROLES_STAKING_ID, &stash);
// Withdraw amount held in storage.
T::Currency::withdraw(
&Self::account_id(),
ledger.total_locked,
WithdrawReasons::TRANSFER,
ExistenceRequirement::AllowDeath,
)?;
// Kill the stash and related information
Self::kill_stash(&stash)?;
}
Expand Down
29 changes: 20 additions & 9 deletions pallets/roles/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
use codec::MaxEncodedLen;
use frame_support::{
ensure,
traits::{Currency, Get, LockIdentifier, LockableCurrency},
traits::{Currency, Get, LockIdentifier, LockableCurrency, OnUnbalanced},
CloneNoBound, EqNoBound, PalletId, PartialEqNoBound, RuntimeDebugNoBound,
};
pub use pallet::*;
Expand Down Expand Up @@ -73,6 +73,10 @@ impl<T: Config> RoleStakingLedger<T> {
pub type CurrencyOf<T> = <T as pallet::Config>::Currency;
pub type BalanceOf<T> =
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
type NegativeImbalanceOf<T> = <<T as Config>::Currency as Currency<
<T as frame_system::Config>::AccountId,
>>::NegativeImbalance;

const ROLES_STAKING_ID: LockIdentifier = *b"rstaking";

#[frame_support::pallet]
Expand Down Expand Up @@ -108,6 +112,9 @@ pub mod pallet {
+ TypeInfo
+ MaxEncodedLen;

/// Handler for the unbalanced reduction when slashing a staker.
type Slash: OnUnbalanced<NegativeImbalanceOf<Self>>;

type WeightInfo: WeightInfo;

#[pallet::constant]
Expand All @@ -125,6 +132,8 @@ pub mod pallet {
Bonded { account: T::AccountId, amount: BalanceOf<T> },
/// Funds unbonded to stop being a validator.
Unbonded { account: T::AccountId, amount: BalanceOf<T> },
/// Slashed validator.
Slashed { account: T::AccountId, amount: BalanceOf<T> },
}

#[pallet::error]
Expand All @@ -148,7 +157,7 @@ pub mod pallet {
StorageMap<_, Blake2_128Concat, T::AccountId, RoleStakingLedger<T>>;

#[pallet::storage]
#[pallet::getter(fn resource_to_bridge_index)]
#[pallet::getter(fn account_role)]
/// Mapping of resource to bridge index
pub type AccountRolesMapping<T: Config> = StorageMap<_, Blake2_256, T::AccountId, RoleType>;

Expand Down Expand Up @@ -181,18 +190,19 @@ pub mod pallet {
if bond_value < min_active_bond.into() {
return Err(Error::<T>::InsufficientBond.into())
}
// Bond the stash account.
// Bond with stash account.
let stash_balance = T::Currency::free_balance(&stash_account);
let value = bond_value.min(stash_balance);
Self::deposit_event(Event::<T>::Bonded {
account: stash_account.clone(),
amount: value,
});

// update ledger.
let item = RoleStakingLedger { stash: stash_account.clone(), total_locked: value };
Self::update_ledger(&stash_account, &item);

Self::deposit_event(Event::<T>::Bonded {
account: stash_account.clone(),
amount: value,
});

// Add role mapping for the stash account.
AccountRolesMapping::<T>::insert(&stash_account, role);
Self::deposit_event(Event::<T>::RoleAssigned { account: stash_account.clone(), role });
Expand All @@ -211,16 +221,17 @@ pub mod pallet {
// TODO: Call jobs manager to remove the services.

// On successful removal of services, remove the role from the mapping.
// unbound and withdraw.
// unbound locked funds.
let ledger = Self::ledger(&stash_account).ok_or(Error::<T>::InvalidStashController)?;
Self::unbond_and_withdraw(&ledger)?;
Self::unbond(&ledger)?;
Self::deposit_event(Event::<T>::Unbonded {
account: ledger.stash,
amount: ledger.total_locked,
});

// Remove role from the mapping.
AccountRolesMapping::<T>::remove(&stash_account);
Self::deposit_event(Event::<T>::RoleRemoved { account: stash_account, role });

Ok(())
}
Expand Down
21 changes: 18 additions & 3 deletions pallets/roles/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ use sp_core::H256;
use sp_runtime::{traits::IdentityLookup, BuildStorage};
pub type AccountId = u128;
pub type Balance = u128;
pub type BlockNumber = u64;

impl frame_system::Config for Runtime {
type RuntimeOrigin = RuntimeOrigin;
Expand Down Expand Up @@ -78,6 +77,7 @@ impl Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Currency = Balances;
type CurrencyBalance = <Self as pallet_balances::Config>::Balance;
type Slash = ();
type PalletId = RolesPalletId;
type WeightInfo = ();
}
Expand All @@ -101,14 +101,29 @@ impl Default for ExtBuilder {
}
}

// Checks events against the latest. A contiguous set of events must be
// provided. They must include the most recent RuntimeEvent, but do not have to include
// every past RuntimeEvent.
pub fn assert_events(mut expected: Vec<RuntimeEvent>) {
let mut actual: Vec<RuntimeEvent> = System::events().iter().map(|e| e.event.clone()).collect();

expected.reverse();
for evt in expected {
let next = actual.pop().expect("RuntimeEvent expected");
assert_eq!(next, evt, "Events don't match (actual,expected)");
}
}

// This function basically just builds a genesis storage key/value store according to
// our desired mockup.
pub fn new_test_ext() -> sp_io::TestExternalities {
let mut t = frame_system::GenesisConfig::<Runtime>::default().build_storage().unwrap();
// We use default for brevity, but you can configure as desired if needed.
pallet_balances::GenesisConfig::<Runtime> { balances: vec![(10, 100), (20, 100)] }
pallet_balances::GenesisConfig::<Runtime> { balances: vec![(10, 10000), (20, 10000)] }
.assimilate_storage(&mut t)
.unwrap();

t.into()
let mut ext = sp_io::TestExternalities::new(t);
ext.execute_with(|| System::set_block_number(1));
ext
}
72 changes: 69 additions & 3 deletions pallets/roles/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,78 @@
// along with Tangle. If not, see <http://www.gnu.org/licenses/>.
#![cfg(test)]
use super::*;
use frame_support::{assert_noop, assert_ok};
use frame_support::assert_ok;
use mock::*;
use tangle_primitives::jobs::ValidatorOffence;

#[test]
fn jobs_submission_works_for_dkg() {
fn test_assign_role() {
new_test_ext().execute_with(|| {
System::set_block_number(1);
// Initially account if funded with 10000 tokens and we are trying to bond 5000 tokens
assert_ok!(Roles::assign_role(RuntimeOrigin::signed(10), 5000, RoleType::Tss));

assert_events(vec![
RuntimeEvent::Roles(crate::Event::Bonded { account: 10, amount: 5000 }),
RuntimeEvent::Roles(crate::Event::RoleAssigned { account: 10, role: RoleType::Tss }),
]);

// Lets verify role assigned to account.
assert_eq!(Roles::account_role(10), Some(RoleType::Tss));
// Verify ledger mapping
assert_eq!(Roles::ledger(10), Some(RoleStakingLedger { stash: 10, total_locked: 5000 }));
// Verify total usable balance of the account. Since we have bonded 5000 tokens, we should
// have 5000 tokens usable.
assert_eq!(Balances::usable_balance(10), 5000);
});
}

#[test]
fn test_clear_role() {
new_test_ext().execute_with(|| {
// Initially account if funded with 10000 tokens and we are trying to bond 5000 tokens
assert_ok!(Roles::assign_role(RuntimeOrigin::signed(10), 5000, RoleType::Tss));
// Verify total usable balance of the account. Since we have bonded 5000 tokens, we should
// have 5000 tokens usable.
assert_eq!(Balances::usable_balance(10), 5000);

// Now lets clear the role
assert_ok!(Roles::clear_role(RuntimeOrigin::signed(10), RoleType::Tss));

assert_events(vec![
RuntimeEvent::Roles(crate::Event::Unbonded { account: 10, amount: 5000 }),
RuntimeEvent::Roles(crate::Event::RoleRemoved { account: 10, role: RoleType::Tss }),
]);

// Role should be removed from account role mappings.
assert_eq!(Roles::account_role(10), None);

// Ledger should be removed from ledger mappings.
assert_eq!(Roles::ledger(10), None);

// Verify total usable balance of the account. Since we have cleared the role, we should
// have 10000 tokens usable.
assert_eq!(Balances::usable_balance(10), 10000);
});
}

// test slashing
#[test]
fn test_slash_validator() {
new_test_ext().execute_with(|| {
// Initially account if funded with 10000 tokens and we are trying to bond 5000 tokens
assert_ok!(Roles::assign_role(RuntimeOrigin::signed(10), 5000, RoleType::Tss));
// Verify total usable balance of the account. Since we have bonded 5000 tokens, we should
// have 5000 tokens usable.
assert_eq!(Balances::usable_balance(10), 5000);

// Now lets slash the account for being Inactive.
assert_ok!(Roles::slash_validator(10, ValidatorOffence::Inactivity));

assert_events(vec![RuntimeEvent::Roles(crate::Event::Slashed {
account: 10,
amount: 1000,
})]);
// should be updated in ledger
assert_eq!(Roles::ledger(10), Some(RoleStakingLedger { stash: 10, total_locked: 4000 }));
});
}

0 comments on commit 6403364

Please sign in to comment.