Skip to content

Commit

Permalink
Fix slowmist vulnerabilities (#707)
Browse files Browse the repository at this point in the history
* fix auditlist boundery check and upadte ccrate dependencies

* add some unit tests

* fixes

* fix integration tests
  • Loading branch information
herryho authored Aug 15, 2022
1 parent 63959e2 commit 7900f49
Show file tree
Hide file tree
Showing 9 changed files with 368 additions and 21 deletions.
2 changes: 2 additions & 0 deletions integration-tests/src/slp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ fn register_subaccount_index_0() {
delegator_active_staking_maximum: 200_000_000_000_000,
validators_reward_maximum: 0,
delegation_amount_minimum: 0,
delegators_maximum: 100,
validators_maximum: 300,
};

// Set minimums and maximums
Expand Down
26 changes: 22 additions & 4 deletions pallets/slp/src/agents/moonbeam_agent/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use core::marker::PhantomData;
use xcm_interface::traits::parachains;

use super::types::{
MoonbeamCall, MoonbeamCurrencyId, MoonbeamParachainStakingCall, MoonbeamUtilityCall,
MoonbeamXtokensCall,
Expand All @@ -31,9 +28,14 @@ use crate::{
DelegationsOccupied, FeeSources,
};
use codec::{alloc::collections::BTreeMap, Encode};
use core::marker::PhantomData;
use cumulus_primitives_core::relay_chain::HashT;
pub use cumulus_primitives_core::ParaId;
use frame_support::{ensure, traits::Get, weights::Weight};
use frame_support::{
ensure,
traits::{Get, Len},
weights::Weight,
};
use frame_system::pallet_prelude::BlockNumberFor;
use node_primitives::{CurrencyId, TokenSymbol, VtokenMintingOperator};
use orml_traits::MultiCurrency;
Expand All @@ -54,6 +56,7 @@ use xcm::{
},
VersionedMultiLocation,
};
use xcm_interface::traits::parachains;

use crate::{
agents::SystemCall,
Expand Down Expand Up @@ -910,6 +913,11 @@ impl<T: Config>
Error::<T>::AlreadyExist
);

// Ensure delegators count is not greater than maximum.
let delegators_count = DelegatorNextIndex::<T>::get(currency_id);
let mins_maxs = MinimumsAndMaximums::<T>::get(currency_id).ok_or(Error::<T>::NotExist)?;
ensure!(delegators_count < mins_maxs.delegators_maximum, Error::<T>::GreaterThanMaximum);

// Revise two delegator storages.
DelegatorsIndex2Multilocation::<T>::insert(currency_id, index, who);
DelegatorsMultilocation2Index::<T>::insert(currency_id, who, index);
Expand Down Expand Up @@ -951,6 +959,14 @@ impl<T: Config>
let multi_hash = T::Hashing::hash(&who.encode());
// Check if the validator already exists.
let validators_set = Validators::<T>::get(currency_id);

// Ensure validator candidates in the whitelist is not greater than maximum.
let mins_maxs = MinimumsAndMaximums::<T>::get(currency_id).ok_or(Error::<T>::NotExist)?;
ensure!(
validators_set.len() as u16 <= mins_maxs.validators_maximum,
Error::<T>::GreaterThanMaximum
);

if validators_set.is_none() {
Validators::<T>::insert(currency_id, vec![(who, multi_hash)]);
} else {
Expand Down Expand Up @@ -1016,6 +1032,8 @@ impl<T: Config>
to: &MultiLocation,
currency_id: CurrencyId,
) -> DispatchResult {
ensure!(amount > Zero::zero(), Error::<T>::AmountZero);

// Get current VKSM/KSM exchange rate.
let vtoken = match currency_id {
MOVR => Ok(CurrencyId::VToken(TokenSymbol::MOVR)),
Expand Down
21 changes: 20 additions & 1 deletion pallets/slp/src/agents/polkadot_agent/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ use codec::Encode;
use core::marker::PhantomData;
use cumulus_primitives_core::relay_chain::HashT;
pub use cumulus_primitives_core::ParaId;
use frame_support::{ensure, traits::Get, weights::Weight};
use frame_support::{
ensure,
traits::{Get, Len},
weights::Weight,
};
use frame_system::pallet_prelude::BlockNumberFor;
use node_primitives::{CurrencyId, TokenSymbol, VtokenMintingOperator};
use orml_traits::MultiCurrency;
Expand Down Expand Up @@ -884,6 +888,11 @@ impl<T: Config>
Error::<T>::AlreadyExist
);

// Ensure delegators count is not greater than maximum.
let delegators_count = DelegatorNextIndex::<T>::get(currency_id);
let mins_maxs = MinimumsAndMaximums::<T>::get(currency_id).ok_or(Error::<T>::NotExist)?;
ensure!(delegators_count < mins_maxs.delegators_maximum, Error::<T>::GreaterThanMaximum);

// Revise two delegator storages.
DelegatorsIndex2Multilocation::<T>::insert(currency_id, index, who);
DelegatorsMultilocation2Index::<T>::insert(currency_id, who, index);
Expand Down Expand Up @@ -923,6 +932,14 @@ impl<T: Config>
let multi_hash = T::Hashing::hash(&who.encode());
// Check if the validator already exists.
let validators_set = Validators::<T>::get(currency_id);

// Ensure validator candidates in the whitelist is not greater than maximum.
let mins_maxs = MinimumsAndMaximums::<T>::get(currency_id).ok_or(Error::<T>::NotExist)?;
ensure!(
validators_set.len() as u16 <= mins_maxs.validators_maximum,
Error::<T>::GreaterThanMaximum
);

if validators_set.is_none() {
Validators::<T>::insert(currency_id, vec![(who, multi_hash)]);
} else {
Expand Down Expand Up @@ -982,6 +999,8 @@ impl<T: Config>
to: &MultiLocation,
currency_id: CurrencyId,
) -> DispatchResult {
ensure!(amount > Zero::zero(), Error::<T>::AmountZero);

// Get current VKSM/KSM or VDOT/DOT exchange rate.
let vtoken = match currency_id {
KSM => Ok(CurrencyId::VToken(TokenSymbol::KSM)),
Expand Down
7 changes: 6 additions & 1 deletion pallets/slp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ use sp_io::hashing::blake2_256;
use sp_runtime::traits::TrailingZeroInput;

mod agents;
mod migration;
mod mock;
pub mod primitives;
mod tests;
Expand Down Expand Up @@ -654,6 +655,10 @@ pub mod pallet {
// Calculate weight
BASE_WEIGHT.saturating_mul(counter.into())
}

fn on_runtime_upgrade() -> Weight {
migration::update_minimums_maximums::<T>()
}
}

#[pallet::call]
Expand Down Expand Up @@ -1318,7 +1323,7 @@ pub mod pallet {
.ok_or(Error::<T>::TimeUnitNotExist)?;
// If this is the first time.
if !CurrencyLatestTuneRecord::<T>::contains_key(currency_id) {
// Insert an empty record into DelegatorLatestTuneRecord storage.
// Insert an empty record into CurrencyLatestTuneRecord storage.
CurrencyLatestTuneRecord::<T>::insert(currency_id, (current_time_unit.clone(), 0));
}

Expand Down
83 changes: 83 additions & 0 deletions pallets/slp/src/migration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// This file is part of Bifrost.

// Copyright (C) 2019-2022 Liebi Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

#![cfg_attr(not(feature = "std"), no_std)]

use super::{Config, MinimumsAndMaximums, Weight};
use crate::{BalanceOf, Decode, Encode, MinimumsMaximums, RuntimeDebug, TimeUnit, TypeInfo};
use frame_support::traits::Get;

#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
pub struct DeprecatedMinimumsMaximums<Balance> {
/// The minimum bonded amount for a delegator at any time.
#[codec(compact)]
pub delegator_bonded_minimum: Balance,
/// The minimum amount each time a delegator needs to bond for extra
#[codec(compact)]
pub bond_extra_minimum: Balance,
/// The minimum unbond amount each time a delegator to unbond.
#[codec(compact)]
pub unbond_minimum: Balance,
/// The minimum amount each time a delegator needs to rebond
#[codec(compact)]
pub rebond_minimum: Balance,
/// The maximum number of unbond records at the same time.
#[codec(compact)]
pub unbond_record_maximum: u32,
/// The maximum number of validators for a delegator to support at the same time.
#[codec(compact)]
pub validators_back_maximum: u32,
/// The maximum amount of active staking for a delegator. It is used to control ROI.
#[codec(compact)]
pub delegator_active_staking_maximum: Balance,
/// The maximum number of delegators for a validator to reward.
#[codec(compact)]
pub validators_reward_maximum: u32,
/// The minimum amount for a delegation.
#[codec(compact)]
pub delegation_amount_minimum: Balance,
}

#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
pub struct DeprecatedDelays {
/// The unlock delay for the unlocking amount to be able to be liquidized.
pub unlock_delay: TimeUnit,
}

pub fn update_minimums_maximums<T: Config>() -> Weight {
MinimumsAndMaximums::<T>::translate::<DeprecatedMinimumsMaximums<BalanceOf<T>>, _>(
|_currency_id, mins_maxs| {
let new_entry = MinimumsMaximums::<BalanceOf<T>> {
delegator_bonded_minimum: mins_maxs.delegator_bonded_minimum,
bond_extra_minimum: mins_maxs.bond_extra_minimum,
unbond_minimum: mins_maxs.unbond_minimum,
rebond_minimum: mins_maxs.rebond_minimum,
unbond_record_maximum: mins_maxs.unbond_record_maximum,
validators_back_maximum: mins_maxs.validators_back_maximum,
delegator_active_staking_maximum: mins_maxs.delegator_active_staking_maximum,
validators_reward_maximum: mins_maxs.validators_reward_maximum,
delegation_amount_minimum: mins_maxs.delegation_amount_minimum,
delegators_maximum: 100u16,
validators_maximum: 300u16,
};
Some(new_entry)
},
);

T::DbWeight::get().reads(1) + T::DbWeight::get().writes(1)
}
1 change: 1 addition & 0 deletions pallets/slp/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ pub const EDDIE: AccountId = AccountId32::new([5u8; 32]);
pub const BNC: CurrencyId = CurrencyId::Native(TokenSymbol::BNC);
pub const KSM: CurrencyId = CurrencyId::Token(TokenSymbol::KSM);
pub const VKSM: CurrencyId = CurrencyId::VToken(TokenSymbol::KSM);
pub const VMOVR: CurrencyId = CurrencyId::VToken(TokenSymbol::MOVR);

construct_runtime!(
pub enum Runtime where
Expand Down
6 changes: 6 additions & 0 deletions pallets/slp/src/primitives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ pub struct MinimumsMaximums<Balance> {
/// The minimum amount for a delegation.
#[codec(compact)]
pub delegation_amount_minimum: Balance,
// Maximum delegators count.
#[codec(compact)]
pub delegators_maximum: u16,
// Maximum validators candidates in the whitelist(Validators<T>)
#[codec(compact)]
pub validators_maximum: u16,
}

/// Different delay params for different chain
Expand Down
4 changes: 4 additions & 0 deletions pallets/slp/src/tests/kusama_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ fn remove_delegator_works() {
delegator_active_staking_maximum: 200_000_000_000_000,
validators_reward_maximum: 0,
delegation_amount_minimum: 0,
delegators_maximum: 100,
validators_maximum: 300,
};

// Set minimums and maximums
Expand Down Expand Up @@ -445,6 +447,8 @@ fn charge_host_fee_and_tune_vtoken_exchange_rate_works() {
delegator_active_staking_maximum: 200_000_000_000_000,
validators_reward_maximum: 0,
delegation_amount_minimum: 0,
delegators_maximum: 100,
validators_maximum: 300,
};

// Set minimums and maximums
Expand Down
Loading

0 comments on commit 7900f49

Please # to comment.