applause based on the external expousre
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
This commit is contained in:
parent
0bb46482b2
commit
6a2b5a34d2
@ -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
|
||||
|
||||
@ -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,33 +605,23 @@ 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) {
|
||||
(true, true) => tree_of_claps
|
||||
.remove(&clap.authority_index)
|
||||
.then(|| number_of_claps.saturating_sub(1))
|
||||
.ok_or(Error::<T>::UnregisteredClapRemove),
|
||||
(true, false) => Err(Error::<T>::AlreadyClapped),
|
||||
(false, true) => Err(Error::<T>::UnregisteredClapRemove),
|
||||
(false, false) => tree_of_claps
|
||||
.try_insert(clap.authority_index)
|
||||
.map(|_| number_of_claps.saturating_add(1))
|
||||
.map_err(|_| Error::<T>::TooMuchAuthorities),
|
||||
}
|
||||
})?;
|
||||
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) {
|
||||
(true, true) => tree_of_claps
|
||||
.remove(&clap.authority_index)
|
||||
.then(|| number_of_claps.saturating_sub(1))
|
||||
.ok_or(Error::<T>::UnregisteredClapRemove),
|
||||
(true, false) => Err(Error::<T>::AlreadyClapped),
|
||||
(false, true) => Err(Error::<T>::UnregisteredClapRemove),
|
||||
(false, false) => tree_of_claps
|
||||
.try_insert(clap.authority_index)
|
||||
.map(|_| number_of_claps.saturating_add(1))
|
||||
.map_err(|_| Error::<T>::TooMuchAuthorities),
|
||||
}
|
||||
})?;
|
||||
|
||||
claps_in_session
|
||||
.entry(clap.authority_index)
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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>;
|
||||
|
||||
@ -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());
|
||||
|
||||
Loading…
Reference in New Issue
Block a user