Adding try_state hook for Tips pallet (#1871)

Part of https://github.com/paritytech/polkadot-sdk/issues/239.

Invariant

1. The number of entries in `Tips` should be equal to `Reasons`.
2. If `OpenTip.finders_fee` is true, then `OpenTip.deposit` should be
greater than zero.
3. Reasons exists for each Tip[`OpenTip.reason`], implying equal length
of storage.

polkadot address: 12zsKEDVcHpKEWb99iFt3xrTCQQXZMu477nJQsTBBrof5k2h

---------

Co-authored-by: Gonçalo Pestana <g6pestana@gmail.com>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
This commit is contained in:
Doordashcon
2024-01-20 10:51:25 +01:00
committed by GitHub
parent 84ff0a974d
commit 3b7a8c75bb
3 changed files with 178 additions and 15 deletions
+58 -1
View File
@@ -68,6 +68,7 @@ use sp_std::prelude::*;
use codec::{Decode, Encode};
use frame_support::{
ensure,
traits::{
ContainsLengthBound, Currency, EnsureOrigin, ExistenceRequirement::KeepAlive, Get,
OnUnbalanced, ReservableCurrency, SortedMembers,
@@ -76,6 +77,9 @@ use frame_support::{
};
use frame_system::pallet_prelude::BlockNumberFor;
#[cfg(any(feature = "try-runtime", test))]
use sp_runtime::TryRuntimeError;
pub use pallet::*;
pub use weights::WeightInfo;
@@ -150,7 +154,7 @@ pub mod pallet {
#[pallet::constant]
type TipFindersFee: Get<Percent>;
/// The amount held on deposit for placing a tip report.
/// The non-zero amount held on deposit for placing a tip report.
#[pallet::constant]
type TipReportDepositBase: Get<BalanceOf<Self, I>>;
@@ -465,6 +469,21 @@ pub mod pallet {
Ok(())
}
}
#[pallet::hooks]
impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
fn integrity_test() {
assert!(
!T::TipReportDepositBase::get().is_zero(),
"`TipReportDepositBase` should not be zero",
);
}
#[cfg(feature = "try-runtime")]
fn try_state(_n: BlockNumberFor<T>) -> Result<(), TryRuntimeError> {
Self::do_try_state()
}
}
}
impl<T: Config<I>, I: 'static> Pallet<T, I> {
@@ -611,4 +630,42 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
Tips::<T, I>::insert(hash, new_tip)
}
}
/// Ensure the correctness of the state of this pallet.
///
/// This should be valid before and after each state transition of this pallet.
///
/// ## Invariants:
/// 1. The number of entries in `Tips` should be equal to `Reasons`.
/// 2. Reasons exists for each Tip[`OpenTip.reason`].
/// 3. If `OpenTip.finders_fee` is true, then OpenTip.deposit should be greater than zero.
#[cfg(any(feature = "try-runtime", test))]
pub fn do_try_state() -> Result<(), TryRuntimeError> {
let reasons = Reasons::<T, I>::iter_keys().collect::<Vec<_>>();
let tips = Tips::<T, I>::iter_keys().collect::<Vec<_>>();
ensure!(
reasons.len() == tips.len(),
TryRuntimeError::Other("Equal length of entries in `Tips` and `Reasons` Storage")
);
for tip in Tips::<T, I>::iter_keys() {
let open_tip = Tips::<T, I>::get(&tip).expect("All map keys are valid; qed");
if open_tip.finders_fee {
ensure!(
!open_tip.deposit.is_zero(),
TryRuntimeError::Other(
"Tips with `finders_fee` should have non-zero `deposit`."
)
)
}
ensure!(
reasons.contains(&open_tip.reason),
TryRuntimeError::Other("no reason for this tip")
);
}
Ok(())
}
}