diff --git a/Cargo.lock b/Cargo.lock index a6a58d8..5b5602c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3504,7 +3504,7 @@ dependencies = [ [[package]] name = "ghost-claims" -version = "0.2.2" +version = "0.2.3" dependencies = [ "frame-benchmarking", "frame-support", @@ -3834,7 +3834,7 @@ dependencies = [ [[package]] name = "ghost-slow-clap" -version = "0.3.16" +version = "0.3.17" dependencies = [ "frame-benchmarking", "frame-support", diff --git a/pallets/slow-clap/Cargo.toml b/pallets/slow-clap/Cargo.toml index 302d83d..40cb7d8 100644 --- a/pallets/slow-clap/Cargo.toml +++ b/pallets/slow-clap/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ghost-slow-clap" -version = "0.3.16" +version = "0.3.17" description = "Applause protocol for the EVM bridge" license.workspace = true authors.workspace = true diff --git a/pallets/slow-clap/src/benchmarking.rs b/pallets/slow-clap/src/benchmarking.rs index 581fd74..15e04a7 100644 --- a/pallets/slow-clap/src/benchmarking.rs +++ b/pallets/slow-clap/src/benchmarking.rs @@ -4,12 +4,7 @@ use super::*; use frame_benchmarking::v1::*; use frame_system::RawOrigin; -use frame_support::traits::fungible::{Inspect, Mutate}; - -use crate::Pallet as SlowClap; - -const MAX_CLAPS: u32 = 100; -const MAX_COMPANIONS: u32 = 20; +use frame_support::traits::fungible::Inspect; pub fn create_account() -> T::AccountId { let account_bytes = Vec::from([1u8; 32]); @@ -17,184 +12,40 @@ pub fn create_account() -> T::AccountId { .expect("32 bytes always construct an AccountId32") } -pub fn create_companions( - total: usize, - network_id: NetworkIdOf, - user_account: T::AccountId, - fee: H256, - receiver: H160, -) -> Result { - T::NetworkDataHandler::register(network_id.into(), NetworkData { - chain_name: "Ethereum".into(), - default_endpoint: - "https://base-mainnet.core.chainstack.com/2fc1de7f08c0465f6a28e3c355e0cb14/".into(), - finality_delay: Some(0), - release_delay: Some(0), - network_type: Default::default(), - gatekeeper: b"0x1234567891234567891234567891234567891234".to_vec(), - topic_name: b"0x12345678912345678912345678912345678912345678912345678912345678".to_vec(), - incoming_fee: 0, - outgoing_fee: 0, - }).map_err(|_| BenchmarkError::Weightless)?; - - let mut last_companion_id = 0; - for _ in 0..total { - let minimum_balance = <::Currency>::minimum_balance(); - let balance = minimum_balance + minimum_balance; - let companion = Companion::, BalanceOf::> { - network_id: network_id.into(), receiver, - fee, amount: balance, - }; - - let _ = <::Currency>::mint_into(&user_account, balance); - let companion_id = SlowClap::::current_companion_id(); - let _ = SlowClap::::propose_companion( - RawOrigin::Signed(user_account.clone()).into(), - network_id, - companion, - )?; - last_companion_id = companion_id; - } - Ok(last_companion_id) -} - -pub fn create_claps(i: u32, j: u32) -> Result< - ( - Vec, BalanceOf>>, - ::Signature, - ), - &'static str, -> { - let minimum_balance = <::Currency>::minimum_balance(); - let amount = minimum_balance + minimum_balance; - let total_amount = amount.saturating_mul(j.into()); - let network_id = NetworkIdOf::::default(); - - let mut claps = Vec::new(); - let mut companions = BTreeMap::new(); - - let authorities = vec![T::AuthorityId::generate_pair(None)]; - let bounded_authorities = - WeakBoundedVec::<_, T::MaxAuthorities>::try_from(authorities.clone()) - .map_err(|()| "more than the maximum number of keys provided")?; - Authorities::::put(bounded_authorities); - - for index in 0..j { - companions.insert( - index.into(), - amount, - ); - } - - for _ in 0..i { - claps.push(Clap { - session_index: 1, - authority_index: 0, - network_id, - transaction_hash: H256::repeat_byte(1u8), - block_number: 69, - removed: true, - receiver: create_account::(), - amount: total_amount, - companions: companions.clone(), - }); - } - - let authority_id = authorities - .get(0usize) - .expect("first authority should exist"); - let encoded_claps = claps.encode(); - let signature = authority_id.sign(&encoded_claps) - .ok_or("couldn't make signature")?; - - Ok((claps, signature)) -} - benchmarks! { slow_clap { - let k in 1 .. MAX_CLAPS; - let j in 1 .. MAX_COMPANIONS; - - let receiver = H160::repeat_byte(69u8); - let fee = H256::repeat_byte(0u8); - let user_account: T::AccountId = whitelisted_caller(); - let network_id = <::NetworkDataHandler as NetworkDataBasicHandler>::NetworkId::default(); - - let (claps, signature) = create_claps::(k, j)?; - let _ = create_companions::(j as usize, network_id, user_account, fee, receiver)?; - }: _(RawOrigin::None, claps, signature) - verify { let minimum_balance = <::Currency>::minimum_balance(); - let total_amount = (minimum_balance + minimum_balance).saturating_mul(j.into()); - } + let receiver = create_account::(); + let amount = minimum_balance + minimum_balance; + let network_id = NetworkIdOf::::default(); - propose_companion { - let receiver = H160::repeat_byte(69u8); - let fee = H256::repeat_byte(0u8); - let user_account: T::AccountId = whitelisted_caller(); - let network_id = <::NetworkDataHandler as NetworkDataBasicHandler>::NetworkId::default(); - // T::NetworkDataHandler::register(network_id.into(), NetworkData { - // chain_name: "Ethereum".into(), - // // https://nd-422-757-666.p2pify.com/0a9d79d93fb2f4a4b1e04695da2b77a7/ - // default_endpoint: - // "https://base-mainnet.core.chainstack.com/2fc1de7f08c0465f6a28e3c355e0cb14/".into(), - // finality_delay: Some(50), - // release_delay: Some(100), - // network_type: Default::default(), - // gatekeeper: b"0x1234567891234567891234567891234567891234".to_vec(), - // topic_name: b"0x12345678912345678912345678912345678912345678912345678912345678".to_vec(), - // incoming_fee: 0, - // outgoing_fee: 0, - // }).map_err(|_| BenchmarkError::Weightless)?; - let companion_id = create_companions::(1, network_id, user_account.clone(), fee, receiver)?; - let companion_id = SlowClap::::current_companion_id(); - let minimum_balance = <::Currency>::minimum_balance(); - let balance = minimum_balance + minimum_balance; - let companion = Companion::, BalanceOf::> { - network_id: network_id.into(), receiver, - fee, amount: balance, + let authorities = vec![T::AuthorityId::generate_pair(None)]; + let bounded_authorities = + WeakBoundedVec::<_, T::MaxAuthorities>::try_from(authorities.clone()) + .map_err(|()| "more than the maximum number of keys provided")?; + Authorities::::put(bounded_authorities); + + let clap = Clap { + session_index: 0, + authority_index: 0, + transaction_hash: H256::repeat_byte(1u8), + block_number: 69, + removed: false, + network_id, + receiver: receiver.clone(), + amount, }; - let _ = <::Currency>::mint_into(&user_account, balance); - assert_eq!(SlowClap::::current_companion_id(), companion_id); - }: _(RawOrigin::Signed(user_account), network_id.into(), companion) - verify { - assert_eq!(SlowClap::::current_companion_id(), companion_id + 1); - } - release_companion { - let receiver = H160::repeat_byte(69u8); - let fee = H256::repeat_byte(0u8); - let user_account: T::AccountId = whitelisted_caller(); - let network_id = <::NetworkDataHandler as NetworkDataBasicHandler>::NetworkId::default(); - let companion_id = create_companions::(1, network_id, user_account.clone(), fee, receiver)?; - assert_eq!(SlowClap::::release_blocks(companion_id), BlockNumberFor::::default()); - }: _(RawOrigin::Signed(user_account), network_id.into(), companion_id) - verify { - assert_ne!(SlowClap::::release_blocks(companion_id), BlockNumberFor::::default()); - } + let authority_id = authorities + .get(0usize) + .expect("first authority should exist"); + let encoded_clap = clap.encode(); + let signature = authority_id.sign(&encoded_clap) + .ok_or("couldn't make signature")?; - kill_companion { - let receiver = H160::repeat_byte(69u8); - let fee = H256::repeat_byte(0u8); - let user_account: T::AccountId = whitelisted_caller(); - let network_id = <::NetworkDataHandler as NetworkDataBasicHandler>::NetworkId::default(); - let companion_id = create_companions::(1, network_id, user_account.clone(), fee, receiver)?; - SlowClap::::release_companion( - RawOrigin::Signed(user_account.clone()).into(), - network_id, - companion_id, - )?; - let block_shift = - <::NetworkDataHandler as NetworkDataInspectHandler>::get(&network_id) - .unwrap() - .release_delay - .unwrap(); - frame_system::Pallet::::set_block_number((block_shift + 1).saturated_into()); - }: _(RawOrigin::Signed(user_account), network_id.into(), companion_id) + }: _(RawOrigin::None, clap, signature) verify { - assert_eq!(SlowClap::::companions(network_id, companion_id), None); - assert_eq!(SlowClap::::companion_details(companion_id), None); - assert_eq!(SlowClap::::current_companion_id(), companion_id + 1); + assert_eq!(<::Currency>::total_balance(&receiver), amount); } impl_benchmark_test_suite!( diff --git a/pallets/slow-clap/src/lib.rs b/pallets/slow-clap/src/lib.rs index 0f72707..8fea071 100644 --- a/pallets/slow-clap/src/lib.rs +++ b/pallets/slow-clap/src/lib.rs @@ -13,7 +13,7 @@ use frame_support::{ EstimateNextSessionRotation, ValidatorSet, ValidatorSetWithIdentification, OneSessionHandler, Get, }, - PalletId, BoundedSlice, WeakBoundedVec, + BoundedSlice, WeakBoundedVec, }; use frame_system::{ @@ -188,12 +188,6 @@ pub struct Clap { pub amount: Balance, } -#[derive(Default, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] -pub struct AuthorityClapsInfo { - pub total: u32, - pub individual: BTreeMap, -} - #[derive(Default, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct SessionAuthorityInfo { pub claps: u32, @@ -305,9 +299,6 @@ pub mod pallet { #[pallet::constant] type UnsignedPriority: Get; - #[pallet::constant] - type TreasuryPalletId: Get; - type WeightInfo: WeightInfo; } @@ -333,12 +324,11 @@ pub mod pallet { #[pallet::error] pub enum Error { NotAnAuthority, - ClapForThePastSession, + ClapForWrongSession, CurrentValidatorIsDisabled, AlreadyClapped, UnregisteredClapRemove, TooMuchAuthorities, - NotEnoughToApplause, CouldNotAccumulateCommission, CouldNotIncreaseGatekeeperAmount, } @@ -369,25 +359,13 @@ pub mod pallet { ValueQuery >; - #[pallet::storage] - #[pallet::getter(fn authorities_claps)] - pub(super) type AuthoritiesClaps = StorageDoubleMap< - _, - Twox64Concat, - T::AuthorityId, - Twox64Concat, - H256, - bool, - ValueQuery, - >; - #[pallet::storage] #[pallet::getter(fn claps_in_session)] pub(super) type ClapsInSession = StorageMap< _, Twox64Concat, SessionIndex, - AuthorityClapsInfo, + BTreeMap, ValueQuery, >; @@ -479,7 +457,7 @@ pub mod pallet { let authorities = Authorities::::get(); let authority = match authorities.get(clap.authority_index as usize) { Some(authority) => authority, - None => return InvalidTransaction::BadProof.into(), + None => return InvalidTransaction::BadSigner.into(), }; let signature_valid = clap.using_encoded(|encoded_clap| { @@ -518,7 +496,7 @@ impl Pallet { fn try_slow_clap(clap: &Clap, BalanceOf>) -> DispatchResult { let current_session_index = T::ValidatorSet::session_index(); - ensure!(current_session_index == clap.session_index, Error::::ClapForThePastSession); + ensure!(current_session_index == clap.session_index, Error::::ClapForWrongSession); let authorities = Authorities::::get(); ensure!(authorities.get(clap.authority_index as usize).is_some(), Error::::NotAnAuthority); @@ -543,15 +521,14 @@ impl Pallet { })?; ClapsInSession::::try_mutate(&clap.session_index, |claps_details| { - if claps_details.individual.get(&clap.authority_index).map(|x| x.disabled).unwrap_or_default() { + if claps_details.get(&clap.authority_index).map(|x| x.disabled).unwrap_or_default() { return Err(Error::::CurrentValidatorIsDisabled); } - (*claps_details).total.saturating_inc(); - (*claps_details).individual + (*claps_details) .entry(clap.authority_index) .and_modify(|individual| (*individual).claps.saturating_inc()) - .or_default(); + .or_insert(SessionAuthorityInfo { claps: 1u32, disabled: false }); Ok(()) })?; @@ -888,7 +865,6 @@ impl Pallet { } let number_of_claps = ClapsInSession::::get(session_index) - .individual .entry(authority_index as AuthIndex) .or_default() .claps; @@ -905,10 +881,17 @@ impl Pallet { if !authorities.is_empty() { assert!(Authorities::::get().is_empty(), "Authorities are already initilized!"); let bounded_authorities = BoundedSlice::<'_, _, T::MaxAuthorities>::try_from(authorities) - .expect("more than the maximum number of clappers"); + .expect("more than the maximum number of authorities"); Authorities::::put(bounded_authorities); } } + + #[cfg(test)] + fn set_test_authorities(authorities: Vec) { + let bounded_authorities = WeakBoundedVec::<_, T::MaxAuthorities>::try_from(authorities) + .expect("more than the maximum number of authorities"); + Authorities::::put(bounded_authorities); + } } impl sp_runtime::BoundToRuntimeAppPublic for Pallet { @@ -948,17 +931,16 @@ impl OneSessionHandler for Pallet { let validators = T::ValidatorSet::validators(); let authorities = Authorities::::get(); - let claps_details = ClapsInSession::::get(session_index); - let total_claps_in_session = claps_details.total; - let average_claps = claps_details - .individual + let (sum_claps, total_claps) = ClapsInSession::::get(&session_index) .iter() .filter(|(_, value)| !value.disabled) - .fold(0u32, |acc, (_, value)| acc.saturating_add(value.claps)) - .checked_div(total_claps_in_session) - .unwrap_or_default(); + .fold((0u32, 0u32), |(sum, total), (_, value)| + (sum.saturating_add(value.claps), total.saturating_add(1)) + ); - // TODO: seems like it's not working + let average_claps = sum_claps + .checked_div(total_claps) + .unwrap_or_default(); let offenders = validators .into_iter() @@ -988,7 +970,6 @@ impl OneSessionHandler for Pallet { let session_index = T::ValidatorSet::session_index(); ClapsInSession::::mutate(&session_index, |claps_details| { (*claps_details) - .individual .entry(validator_index as AuthIndex) .and_modify(|individual| (*individual).disabled = true) .or_insert(SessionAuthorityInfo { claps: 0u32, disabled: true }); diff --git a/pallets/slow-clap/src/mock.rs b/pallets/slow-clap/src/mock.rs index d9a99bc..5e35901 100644 --- a/pallets/slow-clap/src/mock.rs +++ b/pallets/slow-clap/src/mock.rs @@ -11,13 +11,16 @@ use pallet_session::historical as pallet_session_historical; use sp_runtime::{ testing::{TestXt, UintAuthorityId}, traits::ConvertInto, - BuildStorage, Permill, + Permill, }; use sp_staking::{ offence::{OffenceError, ReportOffence}, SessionIndex, }; +#[cfg(feature = "runtime-benchmarks")] +use sp_runtime::BuildStorage; + use crate as slow_clap; use crate::Config; @@ -84,24 +87,22 @@ impl ReportOffence for OffenceHandler { } } -pub fn alice_account_id() -> ::AccountId { 69 } -pub fn eve_account_id() -> ::AccountId { 1337 } - pub fn new_test_ext() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default() + let t = frame_system::GenesisConfig::::default() .build_storage() .unwrap(); - pallet_balances::GenesisConfig:: { - balances: vec![ (alice_account_id(), 100) ], - } - .assimilate_storage(&mut t) - .unwrap(); - let mut result = sp_io::TestExternalities::new(t); result.execute_with(|| { - System::set_block_number(1); + for i in 1..=3 { + System::inc_providers(&i); + assert_eq!(Session::set_keys( + RuntimeOrigin::signed(i), + i.into(), + vec![], + ), Ok(())); + } }); result @@ -167,6 +168,7 @@ impl frame_support::traits::EstimateNextSessionRotation for TestNextSession impl ghost_networks::Config for Runtime { type RuntimeEvent = RuntimeEvent; + type Currency = Balances; type NetworkId = u32; type RegisterOrigin = EnsureRoot; type UpdateOrigin = EnsureRoot; @@ -199,23 +201,15 @@ impl Config for Runtime { type ReportUnresponsiveness = OffenceHandler; type MaxAuthorities = ConstU32<5>; - type MaxNumberOfClaps = ConstU32<100>; - type ApplauseThreshold = ConstU32<0>; - type MaxAuthorityInfoInSession = ConstU32<5_000>; - type OffenceThreshold = ConstU32<40>; + type ApplauseThreshold = ConstU32<50>; + type OffenceThreshold = ConstU32<75>; type UnsignedPriority = ConstU64<{ 1 << 20 }>; - type TreasuryPalletId = TreasuryPalletId; type WeightInfo = (); } pub type Extrinsic = TestXt; -// impl frame_system::offchain::SigningTypes for Runtime { -// type Public = ::Signer; -// type Signature = Signature; -// } - impl frame_system::offchain::SendTransactionTypes for Runtime where RuntimeCall: From, @@ -224,30 +218,16 @@ where type Extrinsic = Extrinsic; } -// impl frame_system::offchain::CreateSignedTransaction for Runtime -// where -// RuntimeCall: From, -// { -// fn create_transaction>( -// call: Self::OverarchingCall, -// _public: Self::Public, -// _account: Self::AccountId, -// nonce: Self::Nonce, -// ) -> Option<(RuntimeCall, ::SignaturePayload)> { -// Some((call, (nonce.into(), ()))) -// } -// } +pub fn advance_session() { + let now = System::block_number().max(1); + System::set_block_number(now + 1); + Session::rotate_session(); -// pub fn advance_session() { -// let now = System::block_number().max(1); -// System::set_block_number(now + 1); -// Session::rotate_session(); -// -// let authorities = Session::validators() -// .into_iter() -// .map(UintAuthorityId) -// .collect(); -// -// SlowClap::set_authorities(authorities); -// assert_eq!(Session::current_index(), (now / Period::get()) as u32); -// } + let authorities = Session::validators() + .into_iter() + .map(UintAuthorityId) + .collect(); + + SlowClap::set_test_authorities(authorities); + assert_eq!(Session::current_index(), (now / Period::get()) as u32); +} diff --git a/pallets/slow-clap/src/tests.rs b/pallets/slow-clap/src/tests.rs index 4eca1eb..00ae8e7 100644 --- a/pallets/slow-clap/src/tests.rs +++ b/pallets/slow-clap/src/tests.rs @@ -2,16 +2,21 @@ use super::*; use crate::mock::*; -use frame_support::{assert_err, assert_ok}; +use frame_support::{assert_err, assert_ok, dispatch}; use sp_core::offchain::{ testing, - testing::TestOffchainExt, OffchainWorkerExt, - // OffchainDbExt, TransactionPoolExt, + testing::{TestOffchainExt, TestTransactionPoolExt}, + OffchainWorkerExt, OffchainDbExt, TransactionPoolExt, }; -// use sp_runtime::testing::UintAuthorityId; +use sp_runtime::testing::UintAuthorityId; -fn prepare_networks() -> (u32, u32) { - let network = NetworkData { +const MAX_DEVIATION_DEPTH: u32 = 10; + +fn prepare_evm_network( + maybe_network_id: Option, + maybe_incoming_commission: Option, +) -> NetworkData { + let network_data = NetworkData { chain_name: "Ethereum".into(), default_endpoint: get_rpc_endpoint(), finality_delay: Some(69), @@ -19,24 +24,45 @@ fn prepare_networks() -> (u32, u32) { network_type: ghost_networks::NetworkType::Evm, gatekeeper: get_gatekeeper(), topic_name: get_topic_name(), - incoming_fee: 0, + incoming_fee: maybe_incoming_commission.unwrap_or_default(), outgoing_fee: 0, }; assert_ok!(Networks::register_network( RuntimeOrigin::root(), - get_network_id(), - network)); + maybe_network_id.unwrap_or(1u32), + network_data.clone())); - (1, 69) + network_data } -fn prepare_companion(amount: u64) -> Companion, BalanceOf> { - Companion { - network_id: 1, - receiver: H160::from_low_u64_ne(1337), - amount: amount, - } +fn do_clap_from( + session_index: u32, + network_id: u32, + authority_index: u32, + transaction_removed: bool, +) -> dispatch::DispatchResult { + let (transaction_hash, receiver, amount) = get_mocked_metadata(); + let clap = Clap { + block_number: 420, + removed: transaction_removed, + transaction_hash, + session_index, + authority_index, + network_id, + receiver, + amount, + }; + let authority = UintAuthorityId::from((authority_index + 1) as u64); + 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) } #[test] @@ -53,295 +79,6 @@ fn test_throttling_slash_function() { assert_eq!(dummy_offence.slash_fraction(17), Perbill::from_parts(46200000)); } -#[test] -fn propose_companion_works_as_expected() { - new_test_ext().execute_with(|| { - let actor = alice_account_id(); - let companion_id = SlowClap::current_companion_id(); - let (valid_network_id, _) = prepare_networks(); - let companion = prepare_companion(100); - - assert_eq!(Balances::balance(&actor), 100); - assert_eq!(Balances::total_issuance(), 100); - assert_eq!(SlowClap::companions(valid_network_id, companion_id), None); - assert_eq!(SlowClap::companion_details(companion_id), None); - assert_eq!(SlowClap::current_companion_id(), companion_id); - - assert_ok!(SlowClap::propose_companion(RuntimeOrigin::signed(actor), valid_network_id, companion.clone())); - - assert_eq!(Balances::balance(&actor), 0); - assert_eq!(Balances::total_issuance(), 0); - assert_eq!(SlowClap::companions(valid_network_id, companion_id), Some(actor)); - assert_eq!(SlowClap::companion_details(companion_id), Some(companion)); - assert_eq!(SlowClap::current_companion_id(), companion_id + 1); - }); -} - -#[test] -fn propose_companion_emits_event() { - new_test_ext().execute_with(|| { - let actor = alice_account_id(); - let companion_id = SlowClap::current_companion_id(); - let (valid_network_id, _) = prepare_networks(); - let companion = prepare_companion(100); - - System::reset_events(); - assert_ok!(SlowClap::propose_companion( - RuntimeOrigin::signed(actor), - valid_network_id, companion.clone())); - System::assert_has_event(RuntimeEvent::SlowClap( - crate::Event::CompanionCreated { - companion_id, - owner: actor, - companion, - })); - }); -} - -#[test] -fn could_not_propose_if_not_enough_funds() { - new_test_ext().execute_with(|| { - let actor = alice_account_id(); - let (valid_network_id, _) = prepare_networks(); - let companion = prepare_companion(1_000); - assert_err!(SlowClap::propose_companion( - RuntimeOrigin::signed(actor), - valid_network_id, companion.clone()), - sp_runtime::TokenError::FundsUnavailable); - - let companion = prepare_companion(100 / 2); - let prev_balance = Balances::balance(&actor); - assert_ok!(SlowClap::propose_companion( - RuntimeOrigin::signed(actor), - valid_network_id, companion.clone())); - assert_eq!(Balances::balance(&actor), prev_balance / 2); - - }); -} - -#[test] -fn companion_amount_should_be_existent() { - new_test_ext().execute_with(|| { - let actor = alice_account_id(); - let (valid_network_id, _) = prepare_networks(); - let companion = prepare_companion(1); - - assert_err!(SlowClap::propose_companion( - RuntimeOrigin::signed(actor), - valid_network_id, companion.clone()), - crate::Error::::CompanionAmountNotExistent); - }); -} - -#[test] -fn could_not_register_companion_if_network_not_registered() { - new_test_ext().execute_with(|| { - let actor = alice_account_id(); - let (_, invalid_network_id) = prepare_networks(); - let companion = prepare_companion(100); - - assert_err!(SlowClap::propose_companion( - RuntimeOrigin::signed(actor), - invalid_network_id, - companion.clone()), - crate::Error::::NonRegisteredNetwork); - }); -} - -#[test] -fn release_companion_works() { - new_test_ext().execute_with(|| { - let actor = alice_account_id(); - let companion_id = SlowClap::current_companion_id(); - let (valid_network_id, _) = prepare_networks(); - let companion = prepare_companion(50); - assert_ok!(SlowClap::propose_companion( - RuntimeOrigin::signed(actor), - valid_network_id, - companion.clone())); - - assert_eq!(SlowClap::release_blocks(companion_id), 0); - assert_ok!(SlowClap::release_companion( - RuntimeOrigin::signed(actor), - valid_network_id, companion_id)); - assert_eq!(SlowClap::release_blocks(companion_id), 69 + 1); - }); -} - -#[test] -fn release_companion_emits_event() { - new_test_ext().execute_with(|| { - let actor = alice_account_id(); - let companion_id = SlowClap::current_companion_id(); - let (valid_network_id, _) = prepare_networks(); - let companion = prepare_companion(50); - assert_ok!(SlowClap::propose_companion( - RuntimeOrigin::signed(actor), - valid_network_id, companion.clone())); - - System::reset_events(); - assert_ok!(SlowClap::release_companion( - RuntimeOrigin::signed(actor), - valid_network_id, companion_id)); - System::assert_has_event(RuntimeEvent::SlowClap( - crate::Event::CompanionReleased { - companion_id, - network_id: valid_network_id, - who: actor, - release_block: 69 + 1 })); - }); -} - -#[test] -fn could_not_release_from_random_account() { - new_test_ext().execute_with(|| { - let actor = alice_account_id(); - let companion_id = SlowClap::current_companion_id(); - let (valid_network_id, _) = prepare_networks(); - let companion = prepare_companion(50); - assert_ok!(SlowClap::propose_companion( - RuntimeOrigin::signed(actor), - valid_network_id, companion.clone())); - - assert_err!(SlowClap::release_companion( - RuntimeOrigin::signed(eve_account_id()), - valid_network_id, companion_id), - crate::Error::::NotValidCompanion); - }); -} - -#[test] -fn kill_companion_works() { - new_test_ext().execute_with(|| { - let actor = alice_account_id(); - let companion_id = SlowClap::current_companion_id(); - let (valid_network_id, _) = prepare_networks(); - let companion = prepare_companion(50); - assert_ok!(SlowClap::propose_companion( - RuntimeOrigin::signed(actor), - valid_network_id, companion.clone())); - - assert_ok!(SlowClap::release_companion( - RuntimeOrigin::signed(actor), - valid_network_id, companion_id)); - let release_block = ReleaseBlocks::::get(companion_id); - - assert_eq!(SlowClap::companions(valid_network_id, companion_id), Some(actor)); - assert_eq!(SlowClap::companion_details(companion_id), Some(companion)); - assert_eq!(Balances::balance(&actor), 50); - assert_eq!(Balances::total_issuance(), 50); - - System::set_block_number(release_block + 1); - assert_ok!(SlowClap::kill_companion( - RuntimeOrigin::signed(actor), - valid_network_id, companion_id)); - - assert_eq!(SlowClap::companions(valid_network_id, companion_id), None); - assert_eq!(SlowClap::companion_details(companion_id), None); - assert_eq!(Balances::balance(&actor), 100); - assert_eq!(Balances::total_issuance(), 100); - }); -} - -#[test] -fn kill_companion_emits_event() { - new_test_ext().execute_with(|| { - let actor = alice_account_id(); - let companion_id = SlowClap::current_companion_id(); - let (valid_network_id, _) = prepare_networks(); - let companion = prepare_companion(50); - - assert_ok!(SlowClap::propose_companion( - RuntimeOrigin::signed(actor), - valid_network_id, companion.clone())); - assert_ok!(SlowClap::release_companion( - RuntimeOrigin::signed(actor), - valid_network_id, companion_id)); - System::set_block_number( - ReleaseBlocks::::get(companion_id) + 1); - - System::reset_events(); - assert_ok!(SlowClap::kill_companion( - RuntimeOrigin::signed(actor), - valid_network_id, companion_id)); - System::assert_has_event(RuntimeEvent::SlowClap( - crate::Event::CompanionKilled { - network_id: valid_network_id, - who: actor, - companion_id, - freed_balance: 50, - })); - }); -} - -#[test] -fn could_not_kill_companion_that_was_not_released() { - new_test_ext().execute_with(|| { - let actor = alice_account_id(); - let companion_id = SlowClap::current_companion_id(); - let (valid_network_id, _) = prepare_networks(); - let companion = prepare_companion(50); - - assert_ok!(SlowClap::propose_companion( - RuntimeOrigin::signed(actor), - valid_network_id, companion.clone())); - assert_ok!(SlowClap::release_companion( - RuntimeOrigin::signed(actor), - valid_network_id, companion_id)); - let release_block = ReleaseBlocks::::get(companion_id); - - assert_eq!(SlowClap::companions(valid_network_id, companion_id), Some(actor)); - assert_eq!(SlowClap::companion_details(companion_id), Some(companion.clone())); - assert_eq!(Balances::balance(&actor), 50); - assert_eq!(Balances::total_issuance(), 50); - - System::set_block_number(release_block / 2); - assert_err!(SlowClap::kill_companion( - RuntimeOrigin::signed(actor), - valid_network_id, companion_id), - crate::Error::::ReleaseTimeHasNotCome); - - assert_eq!(SlowClap::companions(valid_network_id, companion_id), Some(actor)); - assert_eq!(SlowClap::companion_details(companion_id), Some(companion)); - assert_eq!(Balances::balance(&actor), 50); - assert_eq!(Balances::total_issuance(), 50); - }); -} - -#[test] -fn could_not_kill_companion_from_random_account() { - new_test_ext().execute_with(|| { - let actor = alice_account_id(); - let companion_id = SlowClap::current_companion_id(); - let (valid_network_id, _) = prepare_networks(); - let companion = prepare_companion(50); - - assert_ok!(SlowClap::propose_companion( - RuntimeOrigin::signed(actor), - valid_network_id, companion.clone())); - assert_ok!(SlowClap::release_companion( - RuntimeOrigin::signed(actor), - valid_network_id, companion_id)); - let release_block = ReleaseBlocks::::get(companion_id); - - assert_eq!(SlowClap::companions(valid_network_id, companion_id), Some(actor)); - assert_eq!(SlowClap::companion_details(companion_id), Some(companion.clone())); - assert_eq!(Balances::balance(&actor), 50); - assert_eq!(Balances::total_issuance(), 50); - - System::set_block_number(release_block + 1); - assert_err!(SlowClap::kill_companion( - RuntimeOrigin::signed(eve_account_id()), - valid_network_id, companion_id), - crate::Error::::NotValidCompanion); - - assert_eq!(SlowClap::companions(valid_network_id, companion_id), Some(actor)); - assert_eq!(SlowClap::companion_details(companion_id), Some(companion)); - assert_eq!(Balances::balance(&actor), 50); - assert_eq!(Balances::total_issuance(), 50); - }); -} - #[test] fn request_body_is_correct_for_get_block_number() { let (offchain, _) = TestOffchainExt::new(); @@ -349,8 +86,8 @@ fn request_body_is_correct_for_get_block_number() { t.register_extension(OffchainWorkerExt::new(offchain)); t.execute_with(|| { - let request_body = SlowClap::prepare_request_body( - None, 0, Vec::new(), Vec::new()); + let network_data = prepare_evm_network(Some(1), None); + let request_body = SlowClap::prepare_request_body_for_latest_block(&network_data); assert_eq!(core::str::from_utf8(&request_body).unwrap(), r#"{"id":0,"jsonrpc":"2.0","method":"eth_blockNumber"}"#); }); @@ -363,11 +100,13 @@ fn request_body_is_correct_for_get_logs() { t.register_extension(OffchainWorkerExt::new(offchain)); t.execute_with(|| { - let request_body = SlowClap::prepare_request_body( - Some(1337), 69, get_gatekeeper(), get_topic_name()); - assert_eq!( - core::str::from_utf8(&request_body).unwrap(), - r#"{"id":0,"jsonrpc":"2.0","method":"eth_getLogs","params":[{"fromBlock":1268,"toBlock":1337,"address":"0x4d224452801ACEd8B2F0aebE155379bb5D594381","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}]}"#, + let from_block: u64 = 420; + let to_block: u64 = 1337; + let network_data = prepare_evm_network(Some(1), None); + let request_body = SlowClap::prepare_request_body_for_latest_transfers( + from_block, to_block, &network_data); + assert_eq!(core::str::from_utf8(&request_body).unwrap(), + r#"{"id":0,"jsonrpc":"2.0","method":"eth_getLogs","params":[{"fromBlock":"0x1a4","toBlock":"0x539","address":"0x4d224452801ACEd8B2F0aebE155379bb5D594381","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}]}"#, ); }); } @@ -381,10 +120,10 @@ fn should_make_http_call_for_block_number() { evm_block_response(&mut state.write()); t.execute_with(|| { - let request_body = SlowClap::prepare_request_body( - None, 0, Vec::new(), Vec::new()); - let raw_response = SlowClap::fetch_from_remote( - get_rpc_endpoint(), request_body).unwrap(); + let rpc_endpoint = get_rpc_endpoint(); + let network_data = prepare_evm_network(Some(1), None); + let request_body = SlowClap::prepare_request_body_for_latest_block(&network_data); + let raw_response = SlowClap::fetch_from_remote(&rpc_endpoint, &request_body).unwrap(); assert_eq!(raw_response.len(), 45usize); // precalculated }); } @@ -398,10 +137,15 @@ fn should_make_http_call_for_logs() { evm_logs_response(&mut state.write()); t.execute_with(|| { - let request_body = SlowClap::prepare_request_body( - Some(20335857), 84, get_gatekeeper(), get_topic_name()); - let raw_response = SlowClap::fetch_from_remote( - get_rpc_endpoint(), request_body).unwrap(); + let from_block: u64 = 420; + let to_block: u64 = 1337; + let rpc_endpoint = get_rpc_endpoint(); + + let network_data = prepare_evm_network(Some(1), None); + let request_body = SlowClap::prepare_request_body_for_latest_transfers( + from_block, to_block, &network_data); + let raw_response = SlowClap::fetch_from_remote(&rpc_endpoint, &request_body) + .unwrap(); assert_eq!(raw_response.len(), 1805); // precalculated }); } @@ -415,146 +159,423 @@ fn should_make_http_call_and_parse_block_number() { evm_block_response(&mut state.write()); t.execute_with(|| { - let evm_response = SlowClap::fetch_and_parse( - None, 0, Vec::::new(), - Vec::::new(), get_rpc_endpoint()).unwrap(); - let evm_block_number = match evm_response { - EvmResponseType::BlockNumber(evm_block) => Some(evm_block), - _ => None, - }; - assert_eq!(evm_block_number.unwrap_or_default(), 20335745); + let rpc_endpoint = get_rpc_endpoint(); + let network_data = prepare_evm_network(Some(1), None); + + let request_body = SlowClap::prepare_request_body_for_latest_block(&network_data); + let raw_response = SlowClap::fetch_from_remote(&rpc_endpoint, &request_body).unwrap(); + let evm_block_number = SlowClap::apply_evm_response( + &raw_response, 69, Default::default(), 420, 1).unwrap_or_default(); + + assert_eq!(evm_block_number, 20335745); }); } #[test] fn should_make_http_call_and_parse_logs() { let (offchain, state) = TestOffchainExt::new(); + let (pool, _state) = TestTransactionPoolExt::new(); let mut t = sp_io::TestExternalities::default(); + + t.register_extension(OffchainDbExt::new(offchain.clone())); t.register_extension(OffchainWorkerExt::new(offchain)); + t.register_extension(TransactionPoolExt::new(pool)); evm_logs_response(&mut state.write()); - let expected_logs = get_expected_logs(); t.execute_with(|| { - let evm_response = SlowClap::fetch_and_parse( - Some(20335857), 84, get_gatekeeper(), - get_topic_name(), get_rpc_endpoint() - ).unwrap(); - let evm_logs = match evm_response { - EvmResponseType::TransactionLogs(logs) => Some(logs), - _ => None, - }; - assert!(evm_logs.is_some()); - let evm_logs = evm_logs.unwrap(); - assert_eq!(evm_logs, expected_logs); + let from_block: u64 = 420; + let to_block: u64 = 1337; + let rpc_endpoint = get_rpc_endpoint(); + + let network_data = prepare_evm_network(Some(1), None); + let request_body = SlowClap::prepare_request_body_for_latest_transfers( + from_block, to_block, &network_data); + let raw_response = SlowClap::fetch_from_remote(&rpc_endpoint, &request_body).unwrap(); + let evm_block_number = SlowClap::apply_evm_response( + &raw_response, 69, Default::default(), 420, 1).unwrap_or(20335745); + + assert_eq!(evm_block_number, 0); }); } #[test] -fn should_catch_error_in_json_response() { - let (offchain, state) = TestOffchainExt::new(); - let mut t = sp_io::TestExternalities::default(); - t.register_extension(OffchainWorkerExt::new(offchain)); +fn should_collect_commission_accordingly() { + let (network_id, _, _) = generate_unique_hash(None, None, None, None); + let (_, _, amount) = get_mocked_metadata(); - evm_error_response(&mut state.write()); + new_test_ext().execute_with(|| { + let _ = prepare_evm_network(Some(network_id), Some(500_000_000)); + let session_index = advance_session_and_get_index(); - t.execute_with(|| { - assert_err!(SlowClap::fetch_and_parse( - None, 0, Vec::::new(), - Vec::::new(), get_rpc_endpoint()), - OffchainErr::ErrorInEvmResponse); + assert_eq!(Networks::accumulated_commission(), 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!(Networks::accumulated_commission(), amount.saturating_div(2)); }); } -// #[test] -// fn conversion_to_claps_is_correct() { -// todo!(); -// } -// -// #[test] -// fn evm_block_storage_is_empty_by_default() { -// todo!(); -// } -// -// #[test] -// fn evm_block_is_stored_locally_after_first_response() { -// todo!(); -// } -// -// #[test] -// fn evm_block_storage_is_none_after_logs() { -// todo!(); -// } -// -// #[test] -// fn send_evm_usigned_transaction_from_authority() { -// todo!(); -// } +#[test] +fn should_increase_gatkeeper_amount_accordingly() { + let (network_id, _, _) = generate_unique_hash(None, None, None, None); + let (_, _, amount) = get_mocked_metadata(); -// #[test] -// fn should_report_offline_validators() { -// new_test_ext().execute_with(|| { -// let block = 1; -// System::set_block_number(block); -// advance_session(); -// let validators = vec![1, 2, 3, 4, 5, 6]; -// Validators::mutate(|l| *l = Some(validators.clone())); -// advance_session(); -// -// advance_session(); -// -// let offences = Offences::take(); -// assert_eq!( -// offences, -// vec![( -// vec![], -// ThrottlingOffence { -// session_index: 2, -// validator_set_count: 3, -// offenders: vec![(1, 1), (2, 2), (3, 3)], -// } -// )] -// ); -// -// for (idx, v) in validators.into_iter().take(4).enumerate() { -// let _ = clap(); -// } -// -// advance_session(); -// -// let offences = Offences::take(); -// assert_eq!( -// offences, -// vec![( -// vec![], -// ThrottlingOffence { -// session_index: 3, -// validator_set_count: 6, -// offenders: vec![(5, 5), (6, 6)], -// } -// )] -// ); -// }); -// } -// -// #[test] -// fn should_increase_actions_of_validators_when_clap_is_received() { -// } -// -// #[test] -// fn same_claps_should_not_increase_actions() { -// } -// -// #[test] -// fn should_cleanup_received_claps_on_session_end() { -// } -// -// #[test] -// fn should_mark_validator_if_disabled() { -// } + new_test_ext().execute_with(|| { + let _ = prepare_evm_network(Some(network_id), Some(500_000_000)); + let session_index = advance_session_and_get_index(); + + 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_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!(Networks::gatekeeper_amount(network_id), amount.saturating_div(2)); + assert_eq!(Networks::bridged_imbalance().bridged_in, amount.saturating_div(2)); + assert_eq!(Networks::bridged_imbalance().bridged_out, 0); + }); +} + +#[test] +fn should_mark_clapped_transaction_when_clap_received() { + 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); + + assert_claps_info_correct(&storage_key, &session_index, 0); + assert_ok!(do_clap_from(session_index, network_id, 0, false)); + assert_claps_info_correct(&storage_key, &session_index, 1); + assert_ok!(do_clap_from(session_index, network_id, 1, false)); + assert_claps_info_correct(&storage_key, &session_index, 2); + assert_ok!(do_clap_from(session_index, network_id, 2, false)); + assert_claps_info_correct(&storage_key, &session_index, 3); + }); +} + +#[test] +fn should_applause_and_take_next_claps() { + 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); + + assert_eq!(pallet::ApplausesForTransaction::::get(&storage_key), false); + assert_ok!(do_clap_from(session_index, network_id, 0, false)); + assert_eq!(pallet::ApplausesForTransaction::::get(&storage_key), false); + assert_ok!(do_clap_from(session_index, network_id, 1, false)); + assert_eq!(pallet::ApplausesForTransaction::::get(&storage_key), true); + assert_ok!(do_clap_from(session_index, network_id, 2, false)); + assert_eq!(pallet::ApplausesForTransaction::::get(&storage_key), true); + }); +} + +#[test] +fn should_throw_error_on_clap_duplication() { + 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); + + assert_claps_info_correct(&storage_key, &session_index, 0); + assert_ok!(do_clap_from(session_index, network_id, 0, false)); + assert_claps_info_correct(&storage_key, &session_index, 1); + assert_err!(do_clap_from(session_index, network_id, 0, false), + Error::::AlreadyClapped); + assert_claps_info_correct(&storage_key, &session_index, 1); + }); +} + +#[test] +fn should_throw_error_on_removal_of_unregistered_clap() { + 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); + + assert_claps_info_correct(&storage_key, &session_index, 0); + assert_err!(do_clap_from(session_index, network_id, 0, true), + Error::::UnregisteredClapRemove); + assert_claps_info_correct(&storage_key, &session_index, 0); + }); +} + +#[test] +fn should_throw_error_if_session_index_is_not_current() { + 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(); + + for deviation in 1..MAX_DEVIATION_DEPTH { + let session_index_up = session_index.saturating_add(deviation); + let session_index_down = session_index.saturating_sub(deviation); + let storage_key_up = (session_index_up, transaction_hash, unique_transaction_hash); + let storage_key_down = (session_index_down, transaction_hash, unique_transaction_hash); + + 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::::ClapForWrongSession); + assert_err!(do_clap_from(session_index_down, network_id, 0, false), + Error::::ClapForWrongSession); + assert_claps_info_correct(&storage_key_up, &session_index, 0); + assert_claps_info_correct(&storage_key_down, &session_index, 0); + } + }); +} + +#[test] +fn should_throw_error_if_signer_has_incorrect_index() { + 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); + + assert_claps_info_correct(&storage_key, &session_index, 0); + let clap = Clap { + block_number: 420, + removed: false, + transaction_hash, + session_index, + authority_index: 1337, + network_id, + receiver: 69, + amount: 420, + }; + let authority = UintAuthorityId::from((1) as u64); + let signature = authority.sign(&clap.encode()).unwrap(); + assert_err!(SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature), + Error::::NotAnAuthority); + assert_claps_info_correct(&storage_key, &session_index, 0); + }); +} + +#[test] +fn should_throw_error_if_validator_disabled_and_ignore_later() { + 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); + + assert_claps_info_correct(&storage_key, &session_index, 0); + assert_eq!(Session::disable_index(0), true); + assert_err!(do_clap_from(session_index, network_id, 0, false), + Error::::CurrentValidatorIsDisabled); + + assert_eq!(pallet::ReceivedClaps::::get(&storage_key).len(), 0); + assert_eq!(pallet::ClapsInSession::::get(&session_index).len(), 1); + assert_eq!(pallet::ClapsInSession::::get(&session_index) + .into_iter() + .filter(|(_, v)| !v.disabled) + .collect::>() + .len(), 0); + + assert_ok!(do_clap_from(session_index, network_id, 1, false)); + assert_eq!(Session::disable_index(1), true); + + assert_eq!(pallet::ReceivedClaps::::get(&storage_key).len(), 1); + assert_eq!(pallet::ClapsInSession::::get(&session_index).len(), 2); + assert_eq!(pallet::ClapsInSession::::get(&session_index) + .into_iter() + .filter(|(_, v)| !v.disabled) + .collect::>() + .len(), 0); + }); +} + +#[test] +fn should_throw_error_on_gatekeeper_amount_overflow() { + let big_amount: u64 = u64::MAX; + let first_receiver: u64 = 1337; + let second_receiver: u64 = 420; + let (network_id, transaction_hash, unique_transaction_hash) = + generate_unique_hash(None, None, Some(first_receiver), Some(big_amount)); + + new_test_ext().execute_with(|| { + let session_index = advance_session_and_get_index(); + let storage_key_first = (session_index, transaction_hash, unique_transaction_hash); + + for authority_index in 0..=2 { + let clap = Clap { + block_number: 420, + removed: false, + transaction_hash, + session_index, + authority_index, + network_id, + receiver: first_receiver, + amount: big_amount, + }; + let authority = UintAuthorityId::from((authority_index + 1) as u64); + let signature = authority.sign(&clap.encode()).unwrap(); + assert_ok!(SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature)); + } + + assert_claps_info_correct(&storage_key_first, &session_index, 3); + assert_eq!(pallet::ApplausesForTransaction::::get(&storage_key_first), true); + assert_eq!(Balances::balance(&first_receiver), big_amount); + assert_eq!(Balances::balance(&second_receiver), 0); + + for authority_index in 0..=2 { + let clap = Clap { + block_number: 420, + removed: false, + transaction_hash: H256::repeat_byte(3u8), + session_index, + authority_index, + network_id, + receiver: second_receiver, + amount: 420, + }; + let authority = UintAuthorityId::from((authority_index + 1) as u64); + let signature = authority.sign(&clap.encode()).unwrap(); + if authority_index == 0 { + assert_ok!(SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature)); + } else { + assert_err!(SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature), + Error::::CouldNotIncreaseGatekeeperAmount); + } + } + + assert_eq!(Balances::balance(&first_receiver), big_amount); + assert_eq!(Balances::balance(&second_receiver), 0); + + assert_eq!(Networks::gatekeeper_amount(network_id), big_amount); + assert_eq!(Networks::bridged_imbalance().bridged_in, big_amount); + assert_eq!(Networks::bridged_imbalance().bridged_out, 0); + }); +} + +#[test] +fn should_throw_error_on_commission_overflow() { + let big_amount: u64 = u64::MAX; + let first_receiver: u64 = 1337; + let second_receiver: u64 = 420; + let network_id_other: u32 = 69; + let (network_id, transaction_hash, unique_transaction_hash) = + generate_unique_hash(None, None, Some(first_receiver), Some(big_amount)); + + new_test_ext().execute_with(|| { + let _ = prepare_evm_network(Some(network_id), Some(0)); + let _ = prepare_evm_network(Some(network_id_other), Some(0)); + let session_index = advance_session_and_get_index(); + let storage_key_first = (session_index, transaction_hash, unique_transaction_hash); + + for authority_index in 0..=2 { + let clap = Clap { + block_number: 420, + removed: false, + transaction_hash, + session_index, + authority_index, + network_id, + receiver: first_receiver, + amount: big_amount, + }; + let authority = UintAuthorityId::from((authority_index + 1) as u64); + let signature = authority.sign(&clap.encode()).unwrap(); + assert_ok!(SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature)); + } + + assert_claps_info_correct(&storage_key_first, &session_index, 3); + assert_eq!(pallet::ApplausesForTransaction::::get(&storage_key_first), true); + assert_eq!(Balances::balance(&first_receiver), big_amount); + assert_eq!(Balances::balance(&second_receiver), 0); + + for authority_index in 0..=2 { + let clap = Clap { + block_number: 420, + removed: false, + transaction_hash: H256::repeat_byte(3u8), + session_index, + authority_index, + network_id: network_id_other, + receiver: 420, + amount: big_amount, + }; + let authority = UintAuthorityId::from((authority_index + 1) as u64); + let signature = authority.sign(&clap.encode()).unwrap(); + if authority_index == 0 { + assert_ok!(SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature)); + } else { + assert_err!(SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature), + Error::::CouldNotIncreaseGatekeeperAmount); + } + } + + assert_eq!(Balances::balance(&first_receiver), big_amount); + assert_eq!(Balances::balance(&second_receiver), 0); + + assert_eq!(Networks::gatekeeper_amount(network_id), big_amount); + assert_eq!(Networks::gatekeeper_amount(network_id_other), 0); + assert_eq!(Networks::bridged_imbalance().bridged_in, big_amount); + assert_eq!(Networks::bridged_imbalance().bridged_out, 0); + }); +} + +// TODO: check event +// TODO: multiple logs will create multiple records +// TODO: errors should be checked as much as possible +// TODO: offences generated as expected + +fn advance_session_and_get_index() -> u32 { + advance_session(); + assert_eq!(Session::validators(), Vec::::new()); + advance_session(); + + Session::session_index() +} + +fn generate_unique_hash( + maybe_network_id: Option, + maybe_transaction_hash: Option, + maybe_receiver: Option, + maybe_amount: Option, +) -> (u32, H256, H256) { + let network_id = maybe_network_id.unwrap_or(1); + let (transaction_hash, receiver, amount) = get_mocked_metadata(); + + let transaction_hash = maybe_transaction_hash.unwrap_or(transaction_hash); + let receiver = maybe_receiver.unwrap_or(receiver); + let amount = maybe_amount.unwrap_or(amount); + + let unique_transaction_hash = SlowClap::generate_unique_hash( + &receiver, + &amount, + &network_id, + ); + + (network_id, transaction_hash, unique_transaction_hash) +} + +fn assert_claps_info_correct( + storage_key: &(u32, H256, H256), + session_index: &u32, + index: usize, +) { + assert_eq!(pallet::ReceivedClaps::::get(storage_key).len(), index); + assert_eq!(pallet::ClapsInSession::::get(session_index).len(), index); +} fn get_rpc_endpoint() -> Vec { - b"https://nd-422-757-666.p2pify.com/0a9d79d93fb2f4a4b1e04695da2b77a7/".to_vec() + b"https://rpc.endpoint.network.com".to_vec() } fn get_gatekeeper() -> Vec { @@ -565,21 +586,22 @@ fn get_topic_name() -> Vec { b"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef".to_vec() } -fn get_network_id() -> u32 { - // let mut network_id: NetworkIdOf = Default::default(); - // network_id.saturating_inc(); - // network_id - 1u32 +fn get_mocked_metadata() -> (H256, u64, u64) { + let transaction_hash = H256::repeat_byte(69u8); + let receiver: u64 = 1337; + let amount: u64 = 420_000_000_000_000; + + (transaction_hash, receiver, amount) } fn evm_block_response(state: &mut testing::OffchainState) { let expected_body = br#"{"id":0,"jsonrpc":"2.0","method":"eth_blockNumber"}"#.to_vec(); state.expect_request(testing::PendingRequest { method: "POST".into(), - uri: "https://nd-422-757-666.p2pify.com/0a9d79d93fb2f4a4b1e04695da2b77a7/".into(), + uri: "https://rpc.endpoint.network.com".into(), headers: vec![ - ("ACCEPT".to_string(), "APPLICATION/JSON".to_string()), - ("CONTENT-TYPE".to_string(), "APPLICATION/JSON".to_string()), + ("Accept".to_string(), "application/json".to_string()), + ("Content-Type".to_string(), "application/json".to_string()), ], response: Some(b"{\"id\":0,\"jsonrpc\":\"2.0\",\"result\":\"0x1364c81\"}".to_vec()), body: expected_body, @@ -588,24 +610,8 @@ fn evm_block_response(state: &mut testing::OffchainState) { }); } -fn evm_error_response(state: &mut testing::OffchainState) { - let expected_body = br#"{"id":0,"jsonrpc":"2.0","method":"eth_blockNumber"}"#.to_vec(); - state.expect_request(testing::PendingRequest { - method: "POST".into(), - uri: "https://nd-422-757-666.p2pify.com/0a9d79d93fb2f4a4b1e04695da2b77a7/".into(), - headers: vec![ - ("ACCEPT".to_string(), "APPLICATION/JSON".to_string()), - ("CONTENT-TYPE".to_string(), "APPLICATION/JSON".to_string()), - ], - response: Some(b"{\"id\":0,\"jsonrpc\":\"2.0\",\"error\":\"some bad error occures :-(\"}".to_vec()), - body: expected_body, - sent: true, - ..Default::default() - }); -} - fn evm_logs_response(state: &mut testing::OffchainState) { - let expected_body = br#"{"id":0,"jsonrpc":"2.0","method":"eth_getLogs","params":[{"fromBlock":20335773,"toBlock":20335857,"address":"0x4d224452801ACEd8B2F0aebE155379bb5D594381","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}]}"#.to_vec(); + let expected_body = br#"{"id":0,"jsonrpc":"2.0","method":"eth_getLogs","params":[{"fromBlock":"0x1a4","toBlock":"0x539","address":"0x4d224452801ACEd8B2F0aebE155379bb5D594381","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}]}"#.to_vec(); let expected_response = br#"{ "jsonrpc":"2.0", @@ -646,10 +652,10 @@ fn evm_logs_response(state: &mut testing::OffchainState) { state.expect_request(testing::PendingRequest { method: "POST".into(), - uri: "https://nd-422-757-666.p2pify.com/0a9d79d93fb2f4a4b1e04695da2b77a7/".into(), + uri: "https://rpc.endpoint.network.com".into(), headers: vec![ - ("ACCEPT".to_string(), "APPLICATION/JSON".to_string()), - ("CONTENT-TYPE".to_string(), "APPLICATION/JSON".to_string()), + ("Accept".to_string(), "application/json".to_string()), + ("Content-Type".to_string(), "application/json".to_string()), ], body: expected_body, response: Some(expected_response), @@ -657,39 +663,3 @@ fn evm_logs_response(state: &mut testing::OffchainState) { ..Default::default() }); } - -fn get_expected_logs() -> Vec { - let byte_converter = |s: &str| -> Vec { - (2..s.len()) - .step_by(2) - .map(|i| u8::from_str_radix(&s[i..i+2], 16).expect("valid u8 symbol; qed")) - .collect() - }; - - vec![ - Log { - transaction_hash: Some(H256::from_slice(&byte_converter("0xa82f2fe87f4ba01ab3bd2cd4d0fe75a26f0e9a37e2badc004a0e38f9d088a758"))), - block_number: Some(20335773), - topics: vec![ - byte_converter("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), - byte_converter("0x000000000000000000000000d91efec7e42f80156d1d9f660a69847188950747"), - byte_converter("0x0000000000000000000000005e611dfbe71b988cf2a3e5fe4191b5e3d4c4212a"), - ], - address: Some(b"0x4d224452801aced8b2f0aebe155379bb5d594381".to_vec()), - data: sp_std::collections::btree_map::BTreeMap::from([(0, 1308625900000000000000)]), - removed: false, - }, - Log { - transaction_hash: Some(H256::from_slice(&byte_converter("0xd9f02b79a90db7536b0afb5e24bbc1f4319dc3d8c57af7c39941acd249ec053a"))), - block_number: Some(20335857), - topics: vec![ - byte_converter("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), - byte_converter("0x0000000000000000000000005954ab967bc958940b7eb73ee84797dc8a2afbb9"), - byte_converter("0x000000000000000000000000465165be7cacdbd2cbc8334f549fab9783ad6e7a"), - ], - address: Some(b"0x4d224452801aced8b2f0aebe155379bb5d594381".to_vec()), - data: sp_std::collections::btree_map::BTreeMap::from([(0, 45862703604421729909)]), - removed: false, - }, - ] -}