applause based on the external expousre

Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
This commit is contained in:
Uncle Stinky 2025-11-21 13:46:22 +03:00
parent 0bb46482b2
commit 6a2b5a34d2
Signed by: st1nky
GPG Key ID: 016064BD97603B40
4 changed files with 144 additions and 57 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "ghost-slow-clap"
version = "0.3.55"
version = "0.3.56"
description = "Applause protocol for the EVM bridge"
license.workspace = true
authors.workspace = true

View File

@ -8,8 +8,8 @@ use serde::{Deserialize, Deserializer};
use frame_support::{
pallet_prelude::*,
traits::{
tokens::fungible::{Inspect, Mutate},
DisabledValidators, Get, OneSessionHandler, ValidatorSet, ValidatorSetWithIdentification,
Currency, DisabledValidators, Get, OneSessionHandler, ValidatorSet,
ValidatorSetWithIdentification,
},
WeakBoundedVec,
};
@ -17,6 +17,7 @@ use frame_system::{
offchain::{SendTransactionTypes, SubmitTransaction},
pallet_prelude::*,
};
pub use pallet::*;
use sp_core::H256;
@ -27,12 +28,12 @@ use sp_runtime::{
storage_lock::{StorageLock, Time},
HttpError,
},
traits::{BlockNumberProvider, Convert, Saturating, TrailingZeroInput},
traits::{AtLeast32BitUnsigned, BlockNumberProvider, Convert, Saturating, TrailingZeroInput},
Perbill, RuntimeAppPublic, RuntimeDebug,
};
use sp_staking::{
offence::{Kind, Offence, ReportOffence},
SessionIndex,
EraIndex, SessionIndex,
};
use sp_std::{collections::btree_map::BTreeMap, prelude::*, vec::Vec};
@ -202,7 +203,7 @@ impl<NetworkId: core::fmt::Debug> core::fmt::Debug for OffchainErr<NetworkId> {
pub type NetworkIdOf<T> = <<T as Config>::NetworkDataHandler as NetworkDataBasicHandler>::NetworkId;
pub type BalanceOf<T> =
<<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
pub type ValidatorId<T> = <<T as Config>::ValidatorSet as ValidatorSet<
<T as frame_system::Config>::AccountId,
@ -217,6 +218,12 @@ pub type IdentificationTuple<T> = (
type OffchainResult<T, A> = Result<A, OffchainErr<NetworkIdOf<T>>>;
pub trait ApplauseListener<Balance: AtLeast32BitUnsigned> {
fn get_current_era() -> EraIndex;
fn get_threshold_amount(era: EraIndex) -> Balance;
fn get_validator_total_exposure(era: EraIndex, index: usize) -> Balance;
}
#[frame_support::pallet]
pub mod pallet {
use super::*;
@ -239,7 +246,7 @@ pub mod pallet {
+ MaxEncodedLen;
type ValidatorSet: ValidatorSetWithIdentification<Self::AccountId>;
type Currency: Inspect<Self::AccountId> + Mutate<Self::AccountId>;
type Currency: Currency<Self::AccountId>;
type NetworkDataHandler: NetworkDataBasicHandler
+ NetworkDataInspectHandler<NetworkData>
+ NetworkDataMutateHandler<NetworkData, BalanceOf<Self>>;
@ -250,6 +257,7 @@ pub mod pallet {
ThrottlingOffence<IdentificationTuple<Self>>,
>;
type DisabledValidators: DisabledValidators;
type ApplauseListener: ApplauseListener<BalanceOf<Self>>;
#[pallet::constant]
type MaxAuthorities: Get<u32>;
@ -311,6 +319,19 @@ pub mod pallet {
TimeWentBackwards,
}
#[pallet::storage]
#[pallet::getter(fn clapped_amount)]
pub(super) type ClappedAmount<T: Config> = StorageNMap<
_,
(
NMapKey<Twox64Concat, SessionIndex>,
NMapKey<Twox64Concat, H256>,
NMapKey<Twox64Concat, H256>,
),
BalanceOf<T>,
ValueQuery,
>;
#[pallet::storage]
#[pallet::getter(fn block_commitments)]
pub(super) type BlockCommitments<T: Config> = StorageMap<
@ -584,18 +605,8 @@ impl<T: Config> Pallet<T> {
let (session_index, clap_unique_hash) = Self::mended_session_index(&clap);
let mut claps_in_session = ClapsInSession::<T>::get(&session_index);
let disabled_authorities = claps_in_session
.values()
.filter(|info| info.disabled)
.count();
let active_authorities = Authorities::<T>::get(&session_index)
.len()
.saturating_sub(disabled_authorities);
let received_claps_key = (session_index, &clap.transaction_hash, &clap_unique_hash);
let number_of_received_claps =
ReceivedClaps::<T>::try_mutate(&received_claps_key, |tree_of_claps| {
let number_of_claps = tree_of_claps.len();
match (tree_of_claps.contains(&clap.authority_index), clap.removed) {
@ -631,8 +642,7 @@ impl<T: Config> Pallet<T> {
});
let enough_authorities =
Perbill::from_rational(number_of_received_claps as u32, active_authorities as u32)
> Perbill::from_percent(T::ApplauseThreshold::get());
Self::validator_clap_by_amount(clap.authority_index, &received_claps_key);
if enough_authorities {
let _ = Self::try_applause(&clap, &received_claps_key).inspect_err(|error_msg| {
@ -647,6 +657,24 @@ impl<T: Config> Pallet<T> {
Ok(())
}
fn validator_clap_by_amount(
authority_index: AuthIndex,
received_claps_key: &(SessionIndex, &H256, &H256),
) -> bool {
let era = T::ApplauseListener::get_current_era();
let threshold_amount = T::ApplauseListener::get_threshold_amount(era);
let new_clapped_amount =
T::ApplauseListener::get_validator_total_exposure(era, authority_index as usize);
let total_clapped = ClappedAmount::<T>::mutate(received_claps_key, |clapped_amount| {
let total_clapped = clapped_amount.saturating_add(new_clapped_amount);
*clapped_amount = total_clapped;
total_clapped
});
total_clapped >= threshold_amount
}
fn try_applause(
clap: &Clap<T::AccountId, NetworkIdOf<T>, BalanceOf<T>>,
received_claps_key: &(SessionIndex, &H256, &H256),
@ -670,9 +698,7 @@ impl<T: Config> Pallet<T> {
let _ = T::NetworkDataHandler::accumulate_commission(&commission)
.map_err(|_| Error::<T>::CouldNotAccumulateCommission)?;
if final_amount > T::Currency::minimum_balance() {
T::Currency::mint_into(&clap.receiver, final_amount)?;
}
let _ = T::Currency::deposit_creating(&clap.receiver, final_amount);
*is_applaused = true;

View File

@ -9,7 +9,7 @@ use pallet_session::historical as pallet_session_historical;
use sp_runtime::{
curve::PiecewiseLinear,
testing::{TestXt, UintAuthorityId},
traits::ConvertInto,
traits::{AtLeast32BitUnsigned, ConvertInto},
Permill,
};
use sp_staking::{
@ -20,7 +20,7 @@ use sp_staking::{
use sp_runtime::BuildStorage;
use crate as slow_clap;
use crate::Config;
use crate::{ApplauseListener, Config, EraIndex};
type Block = frame_system::mocking::MockBlock<Runtime>;
@ -174,6 +174,32 @@ impl pallet_balances::Config for Runtime {
type WeightInfo = ();
}
type Balance = u64;
pub struct TestSomeCoolTrait;
impl ApplauseListener<Balance> for TestSomeCoolTrait
where
Balance: AtLeast32BitUnsigned + From<u64>,
{
fn get_current_era() -> EraIndex {
1
}
fn get_threshold_amount(_era: EraIndex) -> Balance {
666_666_667u64.into()
}
fn get_validator_total_exposure(_era: EraIndex, index: usize) -> Balance {
match index {
0 => 500_000_000u64,
1 => 300_000_000u64,
2 => 200_000_000u64,
_ => 0,
}
.into()
}
}
impl Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type AuthorityId = UintAuthorityId;
@ -184,6 +210,7 @@ impl Config for Runtime {
type BlockNumberProvider = System;
type ReportUnresponsiveness = OffenceHandler;
type DisabledValidators = Session;
type ApplauseListener = TestSomeCoolTrait;
type MaxAuthorities = ConstU32<5>;
type ApplauseThreshold = ConstU32<50>;

View File

@ -562,25 +562,25 @@ fn should_applause_and_take_next_claps() {
pallet::ApplausesForTransaction::<Runtime>::get(&storage_key),
false
);
assert_eq!(Balances::balance(&receiver), 0);
assert_eq!(Balances::total_balance(&receiver), 0);
assert_ok!(do_clap_from(session_index, network_id, 0, false));
assert_eq!(
pallet::ApplausesForTransaction::<Runtime>::get(&storage_key),
false
);
assert_eq!(Balances::balance(&receiver), 0);
assert_eq!(Balances::total_balance(&receiver), 0);
assert_ok!(do_clap_from(session_index, network_id, 1, false));
assert_eq!(
pallet::ApplausesForTransaction::<Runtime>::get(&storage_key),
true
);
assert_eq!(Balances::balance(&receiver), amount);
assert_eq!(Balances::total_balance(&receiver), amount);
assert_ok!(do_clap_from(session_index, network_id, 2, false));
assert_eq!(
pallet::ApplausesForTransaction::<Runtime>::get(&storage_key),
true
);
assert_eq!(Balances::balance(&receiver), amount);
assert_eq!(Balances::total_balance(&receiver), amount);
});
}
@ -812,8 +812,8 @@ fn should_clap_without_applause_on_gatekeeper_amount_overflow() {
pallet::ApplausesForTransaction::<Runtime>::get(&storage_key_first),
true
);
assert_eq!(Balances::balance(&first_receiver), big_amount);
assert_eq!(Balances::balance(&second_receiver), 0);
assert_eq!(Balances::total_balance(&first_receiver), big_amount);
assert_eq!(Balances::total_balance(&second_receiver), 0);
for authority_index in 0..=2 {
let clap = Clap {
@ -831,8 +831,8 @@ fn should_clap_without_applause_on_gatekeeper_amount_overflow() {
assert_ok!(SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature));
}
assert_eq!(Balances::balance(&first_receiver), big_amount);
assert_eq!(Balances::balance(&second_receiver), 0);
assert_eq!(Balances::total_balance(&first_receiver), big_amount);
assert_eq!(Balances::total_balance(&second_receiver), 0);
assert_eq!(Networks::gatekeeper_amount(network_id), big_amount);
assert_eq!(Networks::bridged_imbalance().bridged_in, big_amount);
@ -876,8 +876,8 @@ fn should_clap_without_applause_on_commission_overflow() {
pallet::ApplausesForTransaction::<Runtime>::get(&storage_key_first),
true
);
assert_eq!(Balances::balance(&first_receiver), big_amount);
assert_eq!(Balances::balance(&second_receiver), 0);
assert_eq!(Balances::total_balance(&first_receiver), big_amount);
assert_eq!(Balances::total_balance(&second_receiver), 0);
for authority_index in 0..=2 {
let clap = Clap {
@ -895,8 +895,8 @@ fn should_clap_without_applause_on_commission_overflow() {
assert_ok!(SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature));
}
assert_eq!(Balances::balance(&first_receiver), big_amount);
assert_eq!(Balances::balance(&second_receiver), 0);
assert_eq!(Balances::total_balance(&first_receiver), big_amount);
assert_eq!(Balances::total_balance(&second_receiver), 0);
assert_eq!(Networks::gatekeeper_amount(network_id), big_amount);
assert_eq!(Networks::gatekeeper_amount(network_id_other), big_amount);
@ -967,13 +967,13 @@ fn should_avoid_applause_during_nullification_period() {
assert_ok!(do_clap_from(session_index, network_id, 0, false));
assert_ok!(do_clap_from(session_index, network_id, 1, false));
assert_eq!(Balances::balance(&receiver), 0);
assert_eq!(Balances::total_balance(&receiver), 0);
Networks::on_finalize(System::block_number());
assert_eq!(Networks::is_nullification_period(), false);
assert_ok!(do_clap_from(session_index, network_id, 2, false));
assert_eq!(Balances::balance(&receiver), amount);
assert_eq!(Balances::total_balance(&receiver), amount);
});
}
@ -995,7 +995,7 @@ fn should_avoid_session_overlap_on_mended_session_index() {
pallet::ApplausesForTransaction::<Runtime>::get(&storage_key),
false
);
assert_eq!(Balances::balance(&receiver), 0u64);
assert_eq!(Balances::total_balance(&receiver), 0u64);
assert_ok!(do_clap_from(session_index, network_id, 1, false));
assert_ok!(do_clap_from(session_index, network_id, 2, false));
@ -1004,7 +1004,7 @@ fn should_avoid_session_overlap_on_mended_session_index() {
pallet::ApplausesForTransaction::<Runtime>::get(&storage_key),
true
);
assert_eq!(Balances::balance(&receiver), amount);
assert_eq!(Balances::total_balance(&receiver), amount);
});
}
@ -1094,7 +1094,7 @@ fn should_not_fail_on_sub_existential_balance() {
assert_eq!(Networks::gatekeeper_amount(network_id), 0);
assert_eq!(Networks::bridged_imbalance().bridged_in, 0);
assert_eq!(Networks::bridged_imbalance().bridged_out, 0);
assert_eq!(Balances::balance(&receiver), 0);
assert_eq!(Balances::total_balance(&receiver), 0);
assert_eq!(
SlowClap::applauses_for_transaction(&received_claps_key),
false
@ -1108,7 +1108,7 @@ fn should_not_fail_on_sub_existential_balance() {
assert_eq!(Networks::gatekeeper_amount(network_id), amount);
assert_eq!(Networks::bridged_imbalance().bridged_in, 0);
assert_eq!(Networks::bridged_imbalance().bridged_out, 0);
assert_eq!(Balances::balance(&receiver), 0);
assert_eq!(Balances::total_balance(&receiver), 0);
assert_eq!(
SlowClap::applauses_for_transaction(&received_claps_key),
true
@ -1175,6 +1175,40 @@ fn should_register_block_commitments() {
});
}
#[test]
fn should_accumulate_clapped_amount() {
let (network_id, transaction_hash, unique_transaction_hash) =
generate_unique_hash(None, None, None, None);
new_test_ext().execute_with(|| {
let session_index = advance_session_and_get_index();
let storage_key = (session_index, transaction_hash, unique_transaction_hash);
let era = <Runtime as Config>::ApplauseListener::get_current_era();
let threshold_amount = <Runtime as Config>::ApplauseListener::get_threshold_amount(era);
assert_eq!(
ClappedAmount::<Runtime>::get(&storage_key) < threshold_amount,
true
);
assert_ok!(do_clap_from(session_index, network_id, 2, false));
assert_eq!(
ClappedAmount::<Runtime>::get(&storage_key) < threshold_amount,
true
);
assert_ok!(do_clap_from(session_index, network_id, 1, false));
assert_eq!(
ClappedAmount::<Runtime>::get(&storage_key) < threshold_amount,
true
);
assert_ok!(do_clap_from(session_index, network_id, 0, false));
assert_eq!(
ClappedAmount::<Runtime>::get(&storage_key) > threshold_amount,
true
);
});
}
fn advance_session_and_get_index() -> u32 {
advance_session();
assert_eq!(Session::validators(), Vec::<u64>::new());