Compare commits
5 Commits
573e57dfb4
...
e73f3855fd
Author | SHA1 | Date | |
---|---|---|---|
e73f3855fd | |||
a00eec9bb9 | |||
2c2df5a607 | |||
186fb58367 | |||
1e4abbfe69 |
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -3648,7 +3648,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ghost-networks"
|
name = "ghost-networks"
|
||||||
version = "0.1.4"
|
version = "0.1.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"frame-benchmarking",
|
"frame-benchmarking",
|
||||||
"frame-support",
|
"frame-support",
|
||||||
@ -3834,7 +3834,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ghost-slow-clap"
|
name = "ghost-slow-clap"
|
||||||
version = "0.3.19"
|
version = "0.3.23"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"frame-benchmarking",
|
"frame-benchmarking",
|
||||||
"frame-support",
|
"frame-support",
|
||||||
@ -3870,7 +3870,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ghost-traits"
|
name = "ghost-traits"
|
||||||
version = "0.3.20"
|
version = "0.3.21"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"frame-support",
|
"frame-support",
|
||||||
"sp-runtime 31.0.1",
|
"sp-runtime 31.0.1",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ghost-networks"
|
name = "ghost-networks"
|
||||||
version = "0.1.4"
|
version = "0.1.5"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
@ -697,4 +697,13 @@ impl<T: Config> NetworkDataMutateHandler<NetworkData, BalanceOf<T>> for Pallet<T
|
|||||||
fn nullify_commission() {
|
fn nullify_commission() {
|
||||||
AccumulatedCommission::<T>::set(Default::default());
|
AccumulatedCommission::<T>::set(Default::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn trigger_nullification() {
|
||||||
|
if NullifyNeeded::<T>::get() {
|
||||||
|
Self::nullify_commission();
|
||||||
|
NullifyNeeded::<T>::put(false);
|
||||||
|
} else {
|
||||||
|
NullifyNeeded::<T>::put(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -935,3 +935,15 @@ fn bridged_inflation_era_payout_triggers_need_of_nullification() {
|
|||||||
assert_eq!(NullifyNeeded::<Test>::get(), false);
|
assert_eq!(NullifyNeeded::<Test>::get(), false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn trigger_nullification_works_as_expected() {
|
||||||
|
ExtBuilder::build()
|
||||||
|
.execute_with(|| {
|
||||||
|
assert_eq!(NullifyNeeded::<Test>::get(), false);
|
||||||
|
GhostNetworks::trigger_nullification();
|
||||||
|
assert_eq!(NullifyNeeded::<Test>::get(), true);
|
||||||
|
GhostNetworks::trigger_nullification();
|
||||||
|
assert_eq!(NullifyNeeded::<Test>::get(), false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ghost-slow-clap"
|
name = "ghost-slow-clap"
|
||||||
version = "0.3.19"
|
version = "0.3.23"
|
||||||
description = "Applause protocol for the EVM bridge"
|
description = "Applause protocol for the EVM bridge"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
|
@ -18,12 +18,12 @@ benchmarks! {
|
|||||||
let receiver = create_account::<T>();
|
let receiver = create_account::<T>();
|
||||||
let amount = minimum_balance + minimum_balance;
|
let amount = minimum_balance + minimum_balance;
|
||||||
let network_id = NetworkIdOf::<T>::default();
|
let network_id = NetworkIdOf::<T>::default();
|
||||||
|
let session_index = T::ValidatorSet::session_index();
|
||||||
|
|
||||||
let authorities = vec![T::AuthorityId::generate_pair(None)];
|
let authorities = vec![T::AuthorityId::generate_pair(None)];
|
||||||
let bounded_authorities =
|
let bounded_authorities = WeakBoundedVec::<_, T::MaxAuthorities>::try_from(authorities.clone())
|
||||||
WeakBoundedVec::<_, T::MaxAuthorities>::try_from(authorities.clone())
|
|
||||||
.map_err(|()| "more than the maximum number of keys provided")?;
|
.map_err(|()| "more than the maximum number of keys provided")?;
|
||||||
Authorities::<T>::put(bounded_authorities);
|
Authorities::<T>::set(&session_index, bounded_authorities);
|
||||||
|
|
||||||
let clap = Clap {
|
let clap = Clap {
|
||||||
session_index: 0,
|
session_index: 0,
|
||||||
@ -48,6 +48,52 @@ benchmarks! {
|
|||||||
assert_eq!(<<T as pallet::Config>::Currency>::total_balance(&receiver), amount);
|
assert_eq!(<<T as pallet::Config>::Currency>::total_balance(&receiver), amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self_applause {
|
||||||
|
let session_index = T::ValidatorSet::session_index();
|
||||||
|
let authority = T::AuthorityId::generate_pair(Some(vec![69u8]));
|
||||||
|
let bounded_authorities = WeakBoundedVec::<_, T::MaxAuthorities>::try_from(vec![authority.clone()])
|
||||||
|
.map_err(|()| "more than the maximum number of keys provided")?;
|
||||||
|
Authorities::<T>::set(&session_index, bounded_authorities);
|
||||||
|
|
||||||
|
let minimum_balance = <<T as pallet::Config>::Currency>::minimum_balance();
|
||||||
|
let receiver = create_account::<T>();
|
||||||
|
let receiver_clone = receiver.clone();
|
||||||
|
let amount = minimum_balance + minimum_balance;
|
||||||
|
let network_id = NetworkIdOf::<T>::default();
|
||||||
|
let transaction_hash = H256::repeat_byte(1u8);
|
||||||
|
|
||||||
|
let unique_transaction_hash = <Pallet<T>>::generate_unique_hash(
|
||||||
|
&receiver,
|
||||||
|
&amount,
|
||||||
|
&network_id,
|
||||||
|
);
|
||||||
|
let storage_key = (session_index, &transaction_hash, &unique_transaction_hash);
|
||||||
|
|
||||||
|
<Pallet::<T>>::trigger_nullification_for_benchmark();
|
||||||
|
let clap = Clap {
|
||||||
|
session_index,
|
||||||
|
authority_index: 0,
|
||||||
|
transaction_hash,
|
||||||
|
block_number: 69,
|
||||||
|
removed: false,
|
||||||
|
network_id,
|
||||||
|
receiver: receiver.clone(),
|
||||||
|
amount,
|
||||||
|
};
|
||||||
|
|
||||||
|
let encoded_clap = clap.encode();
|
||||||
|
let signature = authority.sign(&encoded_clap).unwrap();
|
||||||
|
<Pallet<T>>::slow_clap(RawOrigin::None.into(), clap, signature)?;
|
||||||
|
<Pallet::<T>>::trigger_nullification_for_benchmark();
|
||||||
|
|
||||||
|
assert_eq!(<<T as pallet::Config>::Currency>::total_balance(&receiver), Default::default());
|
||||||
|
assert_eq!(ApplausesForTransaction::<T>::get(&storage_key), false);
|
||||||
|
}: _(RawOrigin::Signed(receiver_clone), network_id, session_index, transaction_hash, receiver_clone.clone(), amount)
|
||||||
|
verify {
|
||||||
|
assert_eq!(<<T as pallet::Config>::Currency>::total_balance(&receiver), amount);
|
||||||
|
assert_eq!(ApplausesForTransaction::<T>::get(&storage_key), true);
|
||||||
|
}
|
||||||
|
|
||||||
impl_benchmark_test_suite!(
|
impl_benchmark_test_suite!(
|
||||||
Pallet,
|
Pallet,
|
||||||
crate::mock::new_test_ext(),
|
crate::mock::new_test_ext(),
|
||||||
|
@ -13,7 +13,7 @@ use frame_support::{
|
|||||||
EstimateNextSessionRotation, ValidatorSet, ValidatorSetWithIdentification,
|
EstimateNextSessionRotation, ValidatorSet, ValidatorSetWithIdentification,
|
||||||
OneSessionHandler, Get,
|
OneSessionHandler, Get,
|
||||||
},
|
},
|
||||||
BoundedSlice, WeakBoundedVec,
|
WeakBoundedVec,
|
||||||
|
|
||||||
};
|
};
|
||||||
use frame_system::{
|
use frame_system::{
|
||||||
@ -323,8 +323,8 @@ pub mod pallet {
|
|||||||
|
|
||||||
#[pallet::error]
|
#[pallet::error]
|
||||||
pub enum Error<T> {
|
pub enum Error<T> {
|
||||||
|
NotEnoughClaps,
|
||||||
NotAnAuthority,
|
NotAnAuthority,
|
||||||
ClapForWrongSession,
|
|
||||||
CurrentValidatorIsDisabled,
|
CurrentValidatorIsDisabled,
|
||||||
AlreadyClapped,
|
AlreadyClapped,
|
||||||
UnregisteredClapRemove,
|
UnregisteredClapRemove,
|
||||||
@ -370,20 +370,27 @@ pub mod pallet {
|
|||||||
>;
|
>;
|
||||||
|
|
||||||
#[pallet::storage]
|
#[pallet::storage]
|
||||||
#[pallet::getter(fn keys)]
|
#[pallet::getter(fn authorities)]
|
||||||
pub(super) type Authorities<T: Config> =
|
pub(super) type Authorities<T: Config> = StorageMap<
|
||||||
StorageValue<_, WeakBoundedVec<T::AuthorityId, T::MaxAuthorities>, ValueQuery>;
|
_,
|
||||||
|
Twox64Concat,
|
||||||
|
SessionIndex,
|
||||||
|
WeakBoundedVec<T::AuthorityId, T::MaxAuthorities>,
|
||||||
|
ValueQuery,
|
||||||
|
>;
|
||||||
|
|
||||||
#[pallet::genesis_config]
|
#[pallet::genesis_config]
|
||||||
#[derive(frame_support::DefaultNoBound)]
|
#[derive(frame_support::DefaultNoBound)]
|
||||||
pub struct GenesisConfig<T: Config> {
|
pub struct GenesisConfig<T: Config> {
|
||||||
pub keys: Vec<T::AuthorityId>,
|
pub authorities: Vec<T::AuthorityId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pallet::genesis_build]
|
#[pallet::genesis_build]
|
||||||
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
|
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
|
||||||
fn build(&self) {
|
fn build(&self) {
|
||||||
Pallet::<T>::initialize_authorities(&self.keys);
|
if !self.authorities.is_empty() {
|
||||||
|
Pallet::<T>::initialize_authorities(self.authorities.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,13 +411,23 @@ pub mod pallet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[pallet::call_index(1)]
|
#[pallet::call_index(1)]
|
||||||
#[pallet::weight(T::WeightInfo::applause())]
|
#[pallet::weight(T::WeightInfo::self_applause())]
|
||||||
pub fn applause(
|
pub fn self_applause(
|
||||||
origin: OriginFor<T>,
|
origin: OriginFor<T>,
|
||||||
_clap: Clap<T::AccountId, NetworkIdOf<T>, BalanceOf<T>>,
|
network_id: NetworkIdOf<T>,
|
||||||
|
session_index: SessionIndex,
|
||||||
|
transaction_hash: H256,
|
||||||
|
receiver: T::AccountId,
|
||||||
|
amount: BalanceOf<T>,
|
||||||
) -> DispatchResult {
|
) -> DispatchResult {
|
||||||
let _ = ensure_signed(origin)?;
|
let _ = ensure_signed(origin)?;
|
||||||
Ok(())
|
Self::applause_if_posible(
|
||||||
|
network_id,
|
||||||
|
session_index,
|
||||||
|
transaction_hash,
|
||||||
|
receiver,
|
||||||
|
amount,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -454,7 +471,7 @@ pub mod pallet {
|
|||||||
|
|
||||||
fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity {
|
fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity {
|
||||||
if let Call::slow_clap { clap, signature } = call {
|
if let Call::slow_clap { clap, signature } = call {
|
||||||
let authorities = Authorities::<T>::get();
|
let authorities = Authorities::<T>::get(&clap.session_index);
|
||||||
let authority = match authorities.get(clap.authority_index as usize) {
|
let authority = match authorities.get(clap.authority_index as usize) {
|
||||||
Some(authority) => authority,
|
Some(authority) => authority,
|
||||||
None => return InvalidTransaction::BadSigner.into(),
|
None => return InvalidTransaction::BadSigner.into(),
|
||||||
@ -495,10 +512,7 @@ impl<T: Config> Pallet<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn try_slow_clap(clap: &Clap<T::AccountId, NetworkIdOf<T>, BalanceOf<T>>) -> DispatchResult {
|
fn try_slow_clap(clap: &Clap<T::AccountId, NetworkIdOf<T>, BalanceOf<T>>) -> DispatchResult {
|
||||||
let current_session_index = T::ValidatorSet::session_index();
|
let authorities = Authorities::<T>::get(&clap.session_index);
|
||||||
ensure!(current_session_index == clap.session_index, Error::<T>::ClapForWrongSession);
|
|
||||||
|
|
||||||
let authorities = Authorities::<T>::get();
|
|
||||||
ensure!(authorities.get(clap.authority_index as usize).is_some(), Error::<T>::NotAnAuthority);
|
ensure!(authorities.get(clap.authority_index as usize).is_some(), Error::<T>::NotAnAuthority);
|
||||||
|
|
||||||
let clap_unique_hash = Self::generate_unique_hash(&clap.receiver, &clap.amount, &clap.network_id);
|
let clap_unique_hash = Self::generate_unique_hash(&clap.receiver, &clap.amount, &clap.network_id);
|
||||||
@ -562,11 +576,11 @@ impl<T: Config> Pallet<T> {
|
|||||||
return Ok(())
|
return Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
let incoming_fee = T::NetworkDataHandler::get(&clap.network_id)
|
let commission = T::NetworkDataHandler::get(&clap.network_id)
|
||||||
.map(|network_data| Perbill::from_parts(network_data.incoming_fee))
|
.map(|network_data| Perbill::from_parts(network_data.incoming_fee))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default()
|
||||||
|
.mul_ceil(clap.amount);
|
||||||
|
|
||||||
let commission = incoming_fee.mul_ceil(clap.amount);
|
|
||||||
let final_amount = clap.amount
|
let final_amount = clap.amount
|
||||||
.checked_sub(&commission)
|
.checked_sub(&commission)
|
||||||
.map(|value| T::Currency::minimum_balance()
|
.map(|value| T::Currency::minimum_balance()
|
||||||
@ -598,6 +612,38 @@ impl<T: Config> Pallet<T> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn applause_if_posible(
|
||||||
|
network_id: NetworkIdOf<T>,
|
||||||
|
session_index: SessionIndex,
|
||||||
|
transaction_hash: H256,
|
||||||
|
receiver: T::AccountId,
|
||||||
|
amount: BalanceOf<T>,
|
||||||
|
) -> DispatchResult{
|
||||||
|
let clap_unique_hash = Self::generate_unique_hash(&receiver, &amount, &network_id);
|
||||||
|
let received_claps_key = (session_index, &transaction_hash, &clap_unique_hash);
|
||||||
|
|
||||||
|
let clap = Clap {
|
||||||
|
authority_index: Default::default(),
|
||||||
|
block_number: Default::default(),
|
||||||
|
removed: false,
|
||||||
|
session_index,
|
||||||
|
network_id,
|
||||||
|
receiver,
|
||||||
|
amount,
|
||||||
|
transaction_hash,
|
||||||
|
};
|
||||||
|
|
||||||
|
let enough_authorities = Perbill::from_rational(
|
||||||
|
ReceivedClaps::<T>::get(&received_claps_key).len() as u32,
|
||||||
|
Authorities::<T>::get(session_index).len() as u32,
|
||||||
|
) > Perbill::from_percent(T::ApplauseThreshold::get());
|
||||||
|
|
||||||
|
ensure!(enough_authorities, Error::<T>::NotEnoughClaps);
|
||||||
|
Self::try_applause(&clap, &received_claps_key)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn start_slow_clapping(
|
fn start_slow_clapping(
|
||||||
block_number: BlockNumberFor<T>,
|
block_number: BlockNumberFor<T>,
|
||||||
networks_len: usize,
|
networks_len: usize,
|
||||||
@ -752,7 +798,8 @@ impl<T: Config> Pallet<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn local_authorities() -> impl Iterator<Item = (u32, T::AuthorityId)> {
|
fn local_authorities() -> impl Iterator<Item = (u32, T::AuthorityId)> {
|
||||||
let authorities = Authorities::<T>::get();
|
let session_index = T::ValidatorSet::session_index();
|
||||||
|
let authorities = Authorities::<T>::get(&session_index);
|
||||||
let mut local_authorities = T::AuthorityId::all();
|
let mut local_authorities = T::AuthorityId::all();
|
||||||
local_authorities.sort();
|
local_authorities.sort();
|
||||||
authorities.into_iter().enumerate().filter_map(move |(index, authority)| {
|
authorities.into_iter().enumerate().filter_map(move |(index, authority)| {
|
||||||
@ -877,13 +924,13 @@ impl<T: Config> Pallet<T> {
|
|||||||
authority_deviation < Perbill::from_percent(T::OffenceThreshold::get())
|
authority_deviation < Perbill::from_percent(T::OffenceThreshold::get())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_authorities(authorities: &[T::AuthorityId]) {
|
fn initialize_authorities(authorities: Vec<T::AuthorityId>) {
|
||||||
if !authorities.is_empty() {
|
let session_index = T::ValidatorSet::session_index();
|
||||||
assert!(Authorities::<T>::get().is_empty(), "Authorities are already initilized!");
|
assert!(Authorities::<T>::get(&session_index).is_empty(), "Authorities are already initilized!");
|
||||||
let bounded_authorities = BoundedSlice::<'_, _, T::MaxAuthorities>::try_from(authorities)
|
let bounded_authorities = WeakBoundedVec::<_, T::MaxAuthorities>::try_from(authorities)
|
||||||
.expect("more than the maximum number of authorities");
|
.expect("more than the maximum number of authorities");
|
||||||
Authorities::<T>::put(bounded_authorities);
|
Authorities::<T>::set(&session_index, bounded_authorities);
|
||||||
}
|
ClapsInSession::<T>::set(&session_index, Default::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_median_claps(session_index: &SessionIndex) -> u32 {
|
fn calculate_median_claps(session_index: &SessionIndex) -> u32 {
|
||||||
@ -909,10 +956,15 @@ impl<T: Config> Pallet<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn set_test_authorities(authorities: Vec<T::AuthorityId>) {
|
fn set_test_authorities(session_index: SessionIndex, authorities: Vec<T::AuthorityId>) {
|
||||||
let bounded_authorities = WeakBoundedVec::<_, T::MaxAuthorities>::try_from(authorities)
|
let bounded_authorities = WeakBoundedVec::<_, T::MaxAuthorities>::try_from(authorities)
|
||||||
.expect("more than the maximum number of authorities");
|
.expect("more than the maximum number of authorities");
|
||||||
Authorities::<T>::put(bounded_authorities);
|
Authorities::<T>::set(session_index, bounded_authorities);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
|
fn trigger_nullification_for_benchmark() {
|
||||||
|
T::NetworkDataHandler::trigger_nullification();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -935,23 +987,21 @@ impl<T: Config> OneSessionHandler<T::AccountId> for Pallet<T> {
|
|||||||
I: Iterator<Item = (&'a T::AccountId, T::AuthorityId)>,
|
I: Iterator<Item = (&'a T::AccountId, T::AuthorityId)>,
|
||||||
{
|
{
|
||||||
let authorities = validators.map(|x| x.1).collect::<Vec<_>>();
|
let authorities = validators.map(|x| x.1).collect::<Vec<_>>();
|
||||||
Self::initialize_authorities(&authorities);
|
Self::initialize_authorities(authorities);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_new_session<'a, I: 'a>(_changed: bool, validators: I, _queued_validators: I)
|
fn on_new_session<'a, I: 'a>(_changed: bool, validators: I, _queued_validators: I)
|
||||||
where
|
where
|
||||||
I: Iterator<Item = (&'a T::AccountId, T::AuthorityId)>,
|
I: Iterator<Item = (&'a T::AccountId, T::AuthorityId)>,
|
||||||
{
|
{
|
||||||
let previous_session = T::ValidatorSet::session_index().saturating_sub(1);
|
|
||||||
let authorities = validators.map(|x| x.1).collect::<Vec<_>>();
|
let authorities = validators.map(|x| x.1).collect::<Vec<_>>();
|
||||||
Self::initialize_authorities(&authorities);
|
Self::initialize_authorities(authorities);
|
||||||
ClapsInSession::<T>::set(previous_session, Default::default());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_before_session_ending() {
|
fn on_before_session_ending() {
|
||||||
let session_index = T::ValidatorSet::session_index();
|
let session_index = T::ValidatorSet::session_index();
|
||||||
let validators = T::ValidatorSet::validators();
|
let validators = T::ValidatorSet::validators();
|
||||||
let authorities = Authorities::<T>::get();
|
let authorities = Authorities::<T>::get(&session_index);
|
||||||
|
|
||||||
let median_claps = Self::calculate_median_claps(&session_index);
|
let median_claps = Self::calculate_median_claps(&session_index);
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ use sp_staking::{
|
|||||||
SessionIndex,
|
SessionIndex,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "runtime-benchmarks")]
|
|
||||||
use sp_runtime::BuildStorage;
|
use sp_runtime::BuildStorage;
|
||||||
|
|
||||||
use crate as slow_clap;
|
use crate as slow_clap;
|
||||||
@ -231,12 +230,33 @@ pub fn advance_session() {
|
|||||||
let now = System::block_number().max(1);
|
let now = System::block_number().max(1);
|
||||||
System::set_block_number(now + 1);
|
System::set_block_number(now + 1);
|
||||||
Session::rotate_session();
|
Session::rotate_session();
|
||||||
|
let session_index = Session::current_index();
|
||||||
|
|
||||||
let authorities = Session::validators()
|
let authorities = Session::validators()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(UintAuthorityId)
|
.map(UintAuthorityId)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
SlowClap::set_test_authorities(authorities);
|
SlowClap::set_test_authorities(session_index, authorities);
|
||||||
assert_eq!(Session::current_index(), (now / Period::get()) as u32);
|
assert_eq!(session_index, (now / Period::get()) as u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn advance_session_with_authority(authority: u64) {
|
||||||
|
let now = System::block_number().max(1);
|
||||||
|
System::set_block_number(now + 1);
|
||||||
|
Session::rotate_session();
|
||||||
|
|
||||||
|
let session_index = Session::current_index();
|
||||||
|
|
||||||
|
SlowClap::set_test_authorities(
|
||||||
|
session_index,
|
||||||
|
vec![
|
||||||
|
UintAuthorityId::from(authority),
|
||||||
|
UintAuthorityId::from(69),
|
||||||
|
UintAuthorityId::from(420),
|
||||||
|
UintAuthorityId::from(1337),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
assert_eq!(session_index, (now / Period::get()) as u32);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,18 +2,19 @@
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::mock::*;
|
use crate::mock::*;
|
||||||
|
|
||||||
use frame_support::{assert_err, assert_ok, dispatch};
|
use frame_support::{assert_err, assert_ok, dispatch};
|
||||||
use sp_core::offchain::{
|
use sp_core::offchain::{
|
||||||
testing,
|
testing,
|
||||||
testing::{TestOffchainExt, TestTransactionPoolExt},
|
testing::{TestOffchainExt, TestTransactionPoolExt},
|
||||||
OffchainWorkerExt, OffchainDbExt, TransactionPoolExt,
|
OffchainWorkerExt, OffchainDbExt, TransactionPoolExt,
|
||||||
};
|
};
|
||||||
use sp_runtime::testing::UintAuthorityId;
|
use sp_runtime::{DispatchError, testing::UintAuthorityId};
|
||||||
|
|
||||||
use ghost_networks::BridgedInflationCurve;
|
use ghost_networks::BridgedInflationCurve;
|
||||||
use pallet_staking::EraPayout;
|
use pallet_staking::EraPayout;
|
||||||
|
|
||||||
const MAX_DEVIATION_DEPTH: u32 = 10;
|
const MAX_DEVIATION_DEPTH: u64 = 10;
|
||||||
|
|
||||||
fn prepare_evm_network(
|
fn prepare_evm_network(
|
||||||
maybe_network_id: Option<u32>,
|
maybe_network_id: Option<u32>,
|
||||||
@ -39,6 +40,34 @@ fn prepare_evm_network(
|
|||||||
network_data
|
network_data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn do_clap_from_first_authority(
|
||||||
|
session_index: u32,
|
||||||
|
network_id: u32,
|
||||||
|
authority: u64,
|
||||||
|
) -> dispatch::DispatchResult {
|
||||||
|
let (transaction_hash, receiver, amount) = get_mocked_metadata();
|
||||||
|
let clap = Clap {
|
||||||
|
block_number: 420,
|
||||||
|
removed: false,
|
||||||
|
transaction_hash,
|
||||||
|
session_index,
|
||||||
|
authority_index: 0,
|
||||||
|
network_id,
|
||||||
|
receiver,
|
||||||
|
amount,
|
||||||
|
};
|
||||||
|
let authority = UintAuthorityId::from(authority);
|
||||||
|
let signature = authority.sign(&clap.encode()).unwrap();
|
||||||
|
|
||||||
|
SlowClap::pre_dispatch(&crate::Call::slow_clap {
|
||||||
|
clap: clap.clone(),
|
||||||
|
signature: signature.clone(),
|
||||||
|
})
|
||||||
|
.map_err(|e| <&'static str>::from(e))?;
|
||||||
|
|
||||||
|
SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature)
|
||||||
|
}
|
||||||
|
|
||||||
fn do_clap_from(
|
fn do_clap_from(
|
||||||
session_index: u32,
|
session_index: u32,
|
||||||
network_id: u32,
|
network_id: u32,
|
||||||
@ -325,23 +354,63 @@ fn should_throw_error_if_session_index_is_not_current() {
|
|||||||
let (network_id, transaction_hash, unique_transaction_hash) =
|
let (network_id, transaction_hash, unique_transaction_hash) =
|
||||||
generate_unique_hash(None, None, None, None);
|
generate_unique_hash(None, None, None, None);
|
||||||
|
|
||||||
|
let bad_signer = 777;
|
||||||
|
let bad_signer_id = 5;
|
||||||
|
|
||||||
new_test_ext().execute_with(|| {
|
new_test_ext().execute_with(|| {
|
||||||
let session_index = advance_session_and_get_index();
|
let mut session_and_indexes = Vec::new();
|
||||||
|
|
||||||
for deviation in 1..MAX_DEVIATION_DEPTH {
|
for deviation in 1..MAX_DEVIATION_DEPTH {
|
||||||
let session_index_up = session_index.saturating_add(deviation);
|
advance_session_with_authority(deviation);
|
||||||
let session_index_down = session_index.saturating_sub(deviation);
|
session_and_indexes.push((deviation, Session::current_index()));
|
||||||
let storage_key_up = (session_index_up, transaction_hash, unique_transaction_hash);
|
}
|
||||||
let storage_key_down = (session_index_down, transaction_hash, unique_transaction_hash);
|
|
||||||
|
for chunk in session_and_indexes.chunks(3).into_iter() {
|
||||||
|
let authority_curr = chunk[1].0;
|
||||||
|
let authority_prev = chunk[0].0;
|
||||||
|
let authority_next = chunk[2].0;
|
||||||
|
|
||||||
|
let session_index_curr = chunk[1].1;
|
||||||
|
let session_index_prev = chunk[0].1;
|
||||||
|
let session_index_next = chunk[2].1;
|
||||||
|
|
||||||
|
let storage_key_curr = (session_index_curr, transaction_hash, unique_transaction_hash);
|
||||||
|
let storage_key_prev = (session_index_prev, transaction_hash, unique_transaction_hash);
|
||||||
|
let storage_key_next = (session_index_next, transaction_hash, unique_transaction_hash);
|
||||||
|
|
||||||
|
assert_claps_info_correct(&storage_key_curr, &session_index_curr, 0);
|
||||||
|
assert_claps_info_correct(&storage_key_prev, &session_index_prev, 0);
|
||||||
|
assert_claps_info_correct(&storage_key_next, &session_index_next, 0);
|
||||||
|
|
||||||
|
assert_invalid_signing_address(session_index_curr, network_id, bad_signer_id);
|
||||||
|
assert_invalid_signing_address(session_index_prev, network_id, bad_signer_id);
|
||||||
|
assert_invalid_signing_address(session_index_prev, network_id, bad_signer_id);
|
||||||
|
|
||||||
|
assert_transaction_has_bad_signature(session_index_curr, network_id, bad_signer);
|
||||||
|
assert_transaction_has_bad_signature(session_index_prev, network_id, bad_signer);
|
||||||
|
assert_transaction_has_bad_signature(session_index_prev, network_id, bad_signer);
|
||||||
|
|
||||||
|
assert_transaction_has_bad_signature(session_index_curr, network_id, authority_prev);
|
||||||
|
assert_transaction_has_bad_signature(session_index_curr, network_id, authority_next);
|
||||||
|
|
||||||
|
assert_transaction_has_bad_signature(session_index_prev, network_id, authority_curr);
|
||||||
|
assert_transaction_has_bad_signature(session_index_prev, network_id, authority_next);
|
||||||
|
|
||||||
|
assert_transaction_has_bad_signature(session_index_next, network_id, authority_prev);
|
||||||
|
assert_transaction_has_bad_signature(session_index_next, network_id, authority_curr);
|
||||||
|
|
||||||
|
assert_claps_info_correct(&storage_key_curr, &session_index_curr, 0);
|
||||||
|
assert_claps_info_correct(&storage_key_prev, &session_index_prev, 0);
|
||||||
|
assert_claps_info_correct(&storage_key_next, &session_index_next, 0);
|
||||||
|
|
||||||
|
assert_ok!(do_clap_from_first_authority(session_index_curr, network_id, authority_curr));
|
||||||
|
assert_ok!(do_clap_from_first_authority(session_index_prev, network_id, authority_prev));
|
||||||
|
assert_ok!(do_clap_from_first_authority(session_index_next, network_id, authority_next));
|
||||||
|
|
||||||
|
assert_claps_info_correct(&storage_key_curr, &session_index_curr, 1);
|
||||||
|
assert_claps_info_correct(&storage_key_prev, &session_index_prev, 1);
|
||||||
|
assert_claps_info_correct(&storage_key_next, &session_index_next, 1);
|
||||||
|
|
||||||
assert_claps_info_correct(&storage_key_up, &session_index, 0);
|
|
||||||
assert_claps_info_correct(&storage_key_down, &session_index, 0);
|
|
||||||
assert_err!(do_clap_from(session_index_up, network_id, 0, false),
|
|
||||||
Error::<Runtime>::ClapForWrongSession);
|
|
||||||
assert_err!(do_clap_from(session_index_down, network_id, 0, false),
|
|
||||||
Error::<Runtime>::ClapForWrongSession);
|
|
||||||
assert_claps_info_correct(&storage_key_up, &session_index, 0);
|
|
||||||
assert_claps_info_correct(&storage_key_down, &session_index, 0);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -602,7 +671,128 @@ fn should_avoid_applause_during_nullification_period() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check event
|
#[test]
|
||||||
|
fn should_self_applause_if_enough_received_claps() {
|
||||||
|
let (network_id, transaction_hash, unique_transaction_hash) =
|
||||||
|
generate_unique_hash(None, None, None, None);
|
||||||
|
let (_, receiver, amount) = get_mocked_metadata();
|
||||||
|
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
let _ = prepare_evm_network(Some(network_id), Some(0));
|
||||||
|
let session_index = advance_session_and_get_index();
|
||||||
|
let storage_key = (session_index, transaction_hash, unique_transaction_hash);
|
||||||
|
|
||||||
|
assert_err!(SlowClap::self_applause(
|
||||||
|
RuntimeOrigin::signed(receiver),
|
||||||
|
network_id,
|
||||||
|
session_index,
|
||||||
|
transaction_hash,
|
||||||
|
receiver,
|
||||||
|
amount,
|
||||||
|
), Error::<Runtime>::NotEnoughClaps);
|
||||||
|
|
||||||
|
assert_eq!(pallet::ApplausesForTransaction::<Runtime>::get(&storage_key), false);
|
||||||
|
assert_eq!(Balances::balance(&receiver), 0);
|
||||||
|
assert_eq!(BridgedInflationCurve::<RewardCurve, Runtime>::era_payout(
|
||||||
|
0, 0, 0), (0, 0));
|
||||||
|
|
||||||
|
assert_ok!(do_clap_from(session_index, network_id, 0, false));
|
||||||
|
assert_ok!(do_clap_from(session_index, network_id, 1, false));
|
||||||
|
assert_ok!(do_clap_from(session_index, network_id, 2, false));
|
||||||
|
|
||||||
|
assert_eq!(pallet::ApplausesForTransaction::<Runtime>::get(&storage_key), false);
|
||||||
|
assert_eq!(Balances::balance(&receiver), 0);
|
||||||
|
|
||||||
|
assert_ok!(SlowClap::self_applause(
|
||||||
|
RuntimeOrigin::signed(receiver),
|
||||||
|
network_id,
|
||||||
|
session_index,
|
||||||
|
transaction_hash,
|
||||||
|
receiver,
|
||||||
|
amount,
|
||||||
|
));
|
||||||
|
assert_eq!(pallet::ApplausesForTransaction::<Runtime>::get(&storage_key), false);
|
||||||
|
assert_eq!(Balances::balance(&receiver), 0);
|
||||||
|
|
||||||
|
Networks::on_finalize(System::block_number());
|
||||||
|
|
||||||
|
assert_ok!(SlowClap::self_applause(
|
||||||
|
RuntimeOrigin::signed(receiver),
|
||||||
|
network_id,
|
||||||
|
session_index,
|
||||||
|
transaction_hash,
|
||||||
|
receiver,
|
||||||
|
amount,
|
||||||
|
));
|
||||||
|
assert_eq!(pallet::ApplausesForTransaction::<Runtime>::get(&storage_key), true);
|
||||||
|
assert_eq!(Balances::balance(&receiver), amount);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_emit_event_on_each_clap_and_on_applause() {
|
||||||
|
let (network_id, transaction_hash, unique_transaction_hash) =
|
||||||
|
generate_unique_hash(None, None, None, None);
|
||||||
|
let (_, receiver, amount) = get_mocked_metadata();
|
||||||
|
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
let commission = Perbill::from_parts(100_000_000); // 10%
|
||||||
|
let commission_amount = commission.mul_ceil(amount);
|
||||||
|
let amount_after_commission = amount.saturating_sub(commission_amount);
|
||||||
|
let _ = prepare_evm_network(Some(network_id), Some(100_000_000));
|
||||||
|
let session_index = advance_session_and_get_index();
|
||||||
|
let storage_key = (session_index, transaction_hash, unique_transaction_hash);
|
||||||
|
|
||||||
|
assert_eq!(pallet::ApplausesForTransaction::<Runtime>::get(&storage_key), false);
|
||||||
|
assert_claps_info_correct(&storage_key, &session_index, 0);
|
||||||
|
assert_ok!(do_clap_from(session_index, network_id, 0, false));
|
||||||
|
System::assert_last_event(RuntimeEvent::SlowClap(
|
||||||
|
crate::Event::Clapped {
|
||||||
|
receiver: receiver.clone(),
|
||||||
|
authority_id: 0,
|
||||||
|
network_id,
|
||||||
|
transaction_hash,
|
||||||
|
amount,
|
||||||
|
}));
|
||||||
|
|
||||||
|
assert_eq!(pallet::ApplausesForTransaction::<Runtime>::get(&storage_key), false);
|
||||||
|
assert_claps_info_correct(&storage_key, &session_index, 1);
|
||||||
|
assert_ok!(do_clap_from(session_index, network_id, 1, false));
|
||||||
|
let binding = System::events();
|
||||||
|
let last_two_events = binding
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.take(5)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
assert_eq!(last_two_events[0].event, RuntimeEvent::SlowClap(
|
||||||
|
crate::Event::Applaused {
|
||||||
|
network_id,
|
||||||
|
receiver: receiver.clone(),
|
||||||
|
received_amount: amount_after_commission,
|
||||||
|
}));
|
||||||
|
assert_eq!(last_two_events[4].event, RuntimeEvent::SlowClap(
|
||||||
|
crate::Event::Clapped {
|
||||||
|
receiver: receiver.clone(),
|
||||||
|
authority_id: 1,
|
||||||
|
network_id,
|
||||||
|
transaction_hash,
|
||||||
|
amount,
|
||||||
|
}));
|
||||||
|
|
||||||
|
assert_eq!(pallet::ApplausesForTransaction::<Runtime>::get(&storage_key), true);
|
||||||
|
assert_claps_info_correct(&storage_key, &session_index, 2);
|
||||||
|
assert_ok!(do_clap_from(session_index, network_id, 2, false));
|
||||||
|
System::assert_last_event(RuntimeEvent::SlowClap(
|
||||||
|
crate::Event::Clapped {
|
||||||
|
receiver: receiver.clone(),
|
||||||
|
authority_id: 2,
|
||||||
|
network_id,
|
||||||
|
transaction_hash,
|
||||||
|
amount,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: multiple logs will create multiple records
|
// TODO: multiple logs will create multiple records
|
||||||
// TODO: errors should be checked as much as possible
|
// TODO: errors should be checked as much as possible
|
||||||
// TODO: offences generated as expected
|
// TODO: offences generated as expected
|
||||||
@ -647,6 +837,24 @@ fn assert_claps_info_correct(
|
|||||||
assert_eq!(pallet::ClapsInSession::<Runtime>::get(session_index).len(), index);
|
assert_eq!(pallet::ClapsInSession::<Runtime>::get(session_index).len(), index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn assert_transaction_has_bad_signature(
|
||||||
|
session_index: u32,
|
||||||
|
network_id: u32,
|
||||||
|
authority: u64,
|
||||||
|
) {
|
||||||
|
assert_err!(do_clap_from_first_authority(session_index, network_id, authority),
|
||||||
|
DispatchError::Other("Transaction has a bad signature"));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_invalid_signing_address(
|
||||||
|
session_index: u32,
|
||||||
|
network_id: u32,
|
||||||
|
authority_index: u32,
|
||||||
|
) {
|
||||||
|
assert_err!(do_clap_from(session_index, network_id, authority_index, false),
|
||||||
|
DispatchError::Other("Invalid signing address"));
|
||||||
|
}
|
||||||
|
|
||||||
fn get_rpc_endpoint() -> Vec<u8> {
|
fn get_rpc_endpoint() -> Vec<u8> {
|
||||||
b"https://rpc.endpoint.network.com".to_vec()
|
b"https://rpc.endpoint.network.com".to_vec()
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,10 @@ use frame_support::weights::Weight;
|
|||||||
|
|
||||||
pub trait WeightInfo {
|
pub trait WeightInfo {
|
||||||
fn slow_clap() -> Weight;
|
fn slow_clap() -> Weight;
|
||||||
fn applause()-> Weight;
|
fn self_applause()-> Weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WeightInfo for () {
|
impl WeightInfo for () {
|
||||||
fn slow_clap()-> Weight { Weight::zero() }
|
fn slow_clap()-> Weight { Weight::zero() }
|
||||||
fn applause()-> Weight { Weight::zero() }
|
fn self_applause()-> Weight { Weight::zero() }
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ghost-traits"
|
name = "ghost-traits"
|
||||||
version = "0.3.20"
|
version = "0.3.21"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
@ -38,4 +38,5 @@ pub trait NetworkDataMutateHandler<Network, Balance>: NetworkDataInspectHandler<
|
|||||||
) -> Result<(Balance, Balance), ()>;
|
) -> Result<(Balance, Balance), ()>;
|
||||||
fn accumulate_commission(commission: &Balance) -> Result<Balance, ()>;
|
fn accumulate_commission(commission: &Balance) -> Result<Balance, ()>;
|
||||||
fn nullify_commission();
|
fn nullify_commission();
|
||||||
|
fn trigger_nullification();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user