diff --git a/Cargo.lock b/Cargo.lock index 6888a80..7c353a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3834,7 +3834,7 @@ dependencies = [ [[package]] name = "ghost-slow-clap" -version = "0.3.17" +version = "0.3.19" dependencies = [ "frame-benchmarking", "frame-support", @@ -3843,6 +3843,8 @@ dependencies = [ "log", "pallet-balances", "pallet-session", + "pallet-staking", + "pallet-staking-reward-curve", "parity-scale-codec", "scale-info", "serde", diff --git a/pallets/slow-clap/Cargo.toml b/pallets/slow-clap/Cargo.toml index ef217c2..e9675ac 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.18" +version = "0.3.19" description = "Applause protocol for the EVM bridge" license.workspace = true authors.workspace = true @@ -27,11 +27,13 @@ sp-staking = { workspace = true } sp-io = { workspace = true } sp-std = { workspace = true } -pallet-balances = { workspace = true } ghost-networks = { workspace = true } [dev-dependencies] -pallet-session = { workspace = true, default-features = true } +pallet-balances = { workspace = true } +pallet-session = { workspace = true } +pallet-staking = { workspace = true } +pallet-staking-reward-curve = { workspace = true } [features] default = ["std"] @@ -49,6 +51,7 @@ std = [ "sp-io/std", "sp-std/std", "pallet-session/std", + "pallet-staking/std", "pallet-balances/std", "ghost-networks/std", ] diff --git a/pallets/slow-clap/src/lib.rs b/pallets/slow-clap/src/lib.rs index 6c5e4dc..c56263e 100644 --- a/pallets/slow-clap/src/lib.rs +++ b/pallets/slow-clap/src/lib.rs @@ -558,7 +558,7 @@ impl Pallet { received_claps_key: &(SessionIndex, &H256, &H256), ) -> DispatchResult { ApplausesForTransaction::::try_mutate(received_claps_key, |is_applaused| { - if *is_applaused { + if *is_applaused || T::NetworkDataHandler::is_nullification_period() { return Ok(()) } diff --git a/pallets/slow-clap/src/mock.rs b/pallets/slow-clap/src/mock.rs index 5e35901..088bc8d 100644 --- a/pallets/slow-clap/src/mock.rs +++ b/pallets/slow-clap/src/mock.rs @@ -2,15 +2,13 @@ use frame_support::{ derive_impl, parameter_types, - traits::{ConstU32, ConstU64}, - weights::Weight, - PalletId, + traits::{ConstU32, ConstU64}, weights::Weight, }; use frame_system::EnsureRoot; use pallet_session::historical as pallet_session_historical; use sp_runtime::{ testing::{TestXt, UintAuthorityId}, - traits::ConvertInto, + traits::ConvertInto, curve::PiecewiseLinear, Permill, }; use sp_staking::{ @@ -176,9 +174,20 @@ impl ghost_networks::Config for Runtime { type WeightInfo = (); } +pallet_staking_reward_curve::build! { + const REWARD_CURVE: PiecewiseLinear<'static> = curve!( + min_inflation: 0_006_000, + max_inflation: 1_000_000, + ideal_stake: 0_690_000, + falloff: 0_050_000, + max_piece_count: 100, + test_precision: 0_005_000, + ); +} + parameter_types! { pub static ExistentialDeposit: u64 = 2; - pub const TreasuryPalletId: PalletId = PalletId(*b"mck/test"); + pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE; } #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] diff --git a/pallets/slow-clap/src/tests.rs b/pallets/slow-clap/src/tests.rs index 00ae8e7..204eef4 100644 --- a/pallets/slow-clap/src/tests.rs +++ b/pallets/slow-clap/src/tests.rs @@ -10,6 +10,9 @@ use sp_core::offchain::{ }; use sp_runtime::testing::UintAuthorityId; +use ghost_networks::BridgedInflationCurve; +use pallet_staking::EraPayout; + const MAX_DEVIATION_DEPTH: u32 = 10; fn prepare_evm_network( @@ -262,18 +265,24 @@ fn should_mark_clapped_transaction_when_clap_received() { fn should_applause_and_take_next_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 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_eq!(Balances::balance(&receiver), 0); assert_ok!(do_clap_from(session_index, network_id, 0, false)); assert_eq!(pallet::ApplausesForTransaction::::get(&storage_key), false); + assert_eq!(Balances::balance(&receiver), 0); assert_ok!(do_clap_from(session_index, network_id, 1, false)); assert_eq!(pallet::ApplausesForTransaction::::get(&storage_key), true); + assert_eq!(Balances::balance(&receiver), amount); assert_ok!(do_clap_from(session_index, network_id, 2, false)); assert_eq!(pallet::ApplausesForTransaction::::get(&storage_key), true); + assert_eq!(Balances::balance(&receiver), amount); + }); } @@ -530,10 +539,74 @@ fn should_throw_error_on_commission_overflow() { }); } +#[test] +fn should_nullify_commission_on_finalize() { + let total_staked = 69_000_000; + let total_issuance = 100_000_000; + + let (network_id, _, _) = generate_unique_hash(None, None, None, None); + let (_, _, amount) = get_mocked_metadata(); + + 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::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_eq!(Networks::accumulated_commission(), amount.saturating_div(2)); + assert_eq!(Networks::is_nullification_period(), false); + + assert_eq!(BridgedInflationCurve::::era_payout( + total_staked, + (total_issuance + amount).into(), + 0), (1260099399952u128, 208739900600048u128)); // precomputed values + assert_eq!(Networks::is_nullification_period(), true); + Networks::on_finalize(System::block_number()); + + assert_eq!(Networks::is_nullification_period(), false); + assert_eq!(Networks::accumulated_commission(), 0); + assert_ok!(do_clap_from(session_index, network_id, 2, false)); + assert_eq!(Networks::accumulated_commission(), 0); + }); +} + +#[test] +fn should_avoid_applause_during_nullification_period() { + let total_staked = 69_000_000; + let total_issuance = 100_000_000; + + let (network_id, _, _) = 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(); + + assert_eq!(Networks::is_nullification_period(), false); + assert_eq!(BridgedInflationCurve::::era_payout( + total_staked, + total_issuance, + 0), (0, 0)); + assert_eq!(Networks::is_nullification_period(), true); + + 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); + + 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); + }); +} + // TODO: check event // TODO: multiple logs will create multiple records // TODO: errors should be checked as much as possible // TODO: offences generated as expected +// TODO: deal with below existential amount after commission fn advance_session_and_get_index() -> u32 { advance_session();