Add MaxTipAmount for pallet-tips (#1709)

Last week we experienced a governance attack.
Surprisingly, there was no upper limit on the tip amount.

Due to the mechanism of pallet-fragment-election, the council members
will be refreshed immediately. Attacker is easy to control the council
and give a large tip amount.
This commit is contained in:
Xavier Lau
2023-09-28 06:08:05 -05:00
committed by GitHub
parent b50d8e6f7f
commit de71fecc4e
4 changed files with 34 additions and 1 deletions
+13 -1
View File
@@ -154,6 +154,10 @@ pub mod pallet {
#[pallet::constant]
type TipReportDepositBase: Get<BalanceOf<Self, I>>;
/// The maximum amount for a single tip.
#[pallet::constant]
type MaxTipAmount: Get<BalanceOf<Self, I>>;
/// Origin from which tippers must come.
///
/// `ContainsLengthBound::max_len` must be cost free (i.e. no storage read or heavy
@@ -208,6 +212,8 @@ pub mod pallet {
AlreadyKnown,
/// The tip hash is unknown.
UnknownTip,
/// The tip given was too generous.
MaxTipAmountExceeded,
/// The account attempting to retract the tip is not the finder of the tip.
NotFinder,
/// The tip cannot be claimed/closed because there are not enough tippers yet.
@@ -336,10 +342,13 @@ pub mod pallet {
let tipper = ensure_signed(origin)?;
let who = T::Lookup::lookup(who)?;
ensure!(T::Tippers::contains(&tipper), BadOrigin);
ensure!(T::MaxTipAmount::get() >= tip_value, Error::<T, I>::MaxTipAmountExceeded);
let reason_hash = T::Hashing::hash(&reason[..]);
ensure!(!Reasons::<T, I>::contains_key(&reason_hash), Error::<T, I>::AlreadyKnown);
let hash = T::Hashing::hash_of(&(&reason_hash, &who));
let hash = T::Hashing::hash_of(&(&reason_hash, &who));
Reasons::<T, I>::insert(&reason_hash, &reason);
Self::deposit_event(Event::NewTip { tip_hash: hash });
let tips = vec![(tipper.clone(), tip_value)];
@@ -387,7 +396,10 @@ pub mod pallet {
let tipper = ensure_signed(origin)?;
ensure!(T::Tippers::contains(&tipper), BadOrigin);
ensure!(T::MaxTipAmount::get() >= tip_value, Error::<T, I>::MaxTipAmountExceeded);
let mut tip = Tips::<T, I>::get(hash).ok_or(Error::<T, I>::UnknownTip)?;
if Self::insert_tip_and_check_closing(&mut tip, tipper, tip_value) {
Self::deposit_event(Event::TipClosing { tip_hash: hash });
}
+19
View File
@@ -172,6 +172,7 @@ impl Config for Test {
type TipFindersFee = TipFindersFee;
type TipReportDepositBase = ConstU64<1>;
type DataDepositPerByte = ConstU64<1>;
type MaxTipAmount = ConstU64<10_000_000>;
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
}
@@ -183,6 +184,7 @@ impl Config<Instance1> for Test {
type TipFindersFee = TipFindersFee;
type TipReportDepositBase = ConstU64<1>;
type DataDepositPerByte = ConstU64<1>;
type MaxTipAmount = ConstU64<10_000_000>;
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
}
@@ -396,6 +398,23 @@ fn tip_median_calculation_works() {
});
}
#[test]
fn tip_large_should_fail() {
new_test_ext().execute_with(|| {
Balances::make_free_balance_be(&Treasury::account_id(), 101);
assert_ok!(Tips::tip_new(RuntimeOrigin::signed(10), b"awesome.dot".to_vec(), 3, 0));
let h = tip_hash();
assert_noop!(
Tips::tip(
RuntimeOrigin::signed(12),
h,
<<Test as Config>::MaxTipAmount as Get<u64>>::get() + 1
),
Error::<Test>::MaxTipAmountExceeded
);
});
}
#[test]
fn tip_changing_works() {
new_test_ext().execute_with(|| {