avoid errors from minting sub-existential balance

Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
This commit is contained in:
Uncle Stinky 2025-06-13 16:42:14 +03:00
parent 7be24ed139
commit 5beb22f116
Signed by: st1nky
GPG Key ID: 016064BD97603B40
4 changed files with 53 additions and 26 deletions

6
Cargo.lock generated
View File

@ -3648,7 +3648,7 @@ dependencies = [
[[package]] [[package]]
name = "ghost-networks" name = "ghost-networks"
version = "0.1.5" version = "0.1.6"
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.23" version = "0.3.24"
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.21" version = "0.3.22"
dependencies = [ dependencies = [
"frame-support", "frame-support",
"sp-runtime 31.0.1", "sp-runtime 31.0.1",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ghost-slow-clap" name = "ghost-slow-clap"
version = "0.3.23" version = "0.3.24"
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

View File

@ -28,7 +28,7 @@ use sp_runtime::{
self as rt_offchain, HttpError, self as rt_offchain, HttpError,
storage::{MutateStorageError, StorageRetrievalError, StorageValueRef}, storage::{MutateStorageError, StorageRetrievalError, StorageValueRef},
}, },
traits::{CheckedSub, BlockNumberProvider, Convert, Saturating}, traits::{BlockNumberProvider, Convert, Saturating},
}; };
use sp_std::{ use sp_std::{
vec::Vec, prelude::*, vec::Vec, prelude::*,
@ -330,6 +330,7 @@ pub mod pallet {
UnregisteredClapRemove, UnregisteredClapRemove,
TooMuchAuthorities, TooMuchAuthorities,
CouldNotAccumulateCommission, CouldNotAccumulateCommission,
CouldNotAccumulateIncomingImbalance,
CouldNotIncreaseGatekeeperAmount, CouldNotIncreaseGatekeeperAmount,
} }
@ -580,25 +581,21 @@ impl<T: Config> Pallet<T> {
.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); .mul_ceil(clap.amount);
let final_amount = clap.amount.saturating_sub(commission);
let final_amount = clap.amount let _ = T::NetworkDataHandler::increase_gatekeeper_amount(&clap.network_id, &clap.amount)
.checked_sub(&commission)
.map(|value| T::Currency::minimum_balance()
.lt(&value)
.then(|| value)
)
.flatten()
.unwrap_or_default();
let _ = T::NetworkDataHandler::increase_gatekeeper_amount(&clap.network_id, &final_amount)
.map_err(|_| Error::<T>::CouldNotIncreaseGatekeeperAmount)?; .map_err(|_| Error::<T>::CouldNotIncreaseGatekeeperAmount)?;
let _ = T::NetworkDataHandler::accumulate_incoming_imbalance(&final_amount)
.map_err(|_| Error::<T>::CouldNotAccumulateIncomingImbalance)?;
let _ = T::NetworkDataHandler::accumulate_commission(&commission) let _ = T::NetworkDataHandler::accumulate_commission(&commission)
.map_err(|_| Error::<T>::CouldNotAccumulateCommission)?; .map_err(|_| Error::<T>::CouldNotAccumulateCommission)?;
if final_amount > T::Currency::minimum_balance() {
T::Currency::mint_into( T::Currency::mint_into(
&clap.receiver, &clap.receiver,
final_amount final_amount
)?; )?;
}
*is_applaused = true; *is_applaused = true;

View File

@ -265,7 +265,7 @@ fn should_increase_gatkeeper_amount_accordingly() {
assert_ok!(do_clap_from(session_index, network_id, 1, false)); assert_ok!(do_clap_from(session_index, network_id, 1, false));
assert_ok!(do_clap_from(session_index, network_id, 2, 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::gatekeeper_amount(network_id), amount);
assert_eq!(Networks::bridged_imbalance().bridged_in, amount.saturating_div(2)); assert_eq!(Networks::bridged_imbalance().bridged_in, amount.saturating_div(2));
assert_eq!(Networks::bridged_imbalance().bridged_out, 0); assert_eq!(Networks::bridged_imbalance().bridged_out, 0);
}); });
@ -594,7 +594,7 @@ fn should_throw_error_on_commission_overflow() {
assert_ok!(SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature)); assert_ok!(SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature));
} else { } else {
assert_err!(SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature), assert_err!(SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature),
Error::<Runtime>::CouldNotIncreaseGatekeeperAmount); Error::<Runtime>::CouldNotAccumulateIncomingImbalance);
} }
} }
@ -617,19 +617,19 @@ fn should_nullify_commission_on_finalize() {
let (_, _, amount) = get_mocked_metadata(); let (_, _, amount) = get_mocked_metadata();
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
let _ = prepare_evm_network(Some(network_id), Some(500_000_000)); let _ = prepare_evm_network(Some(network_id), Some(1_000_000_000));
let session_index = advance_session_and_get_index(); let session_index = advance_session_and_get_index();
assert_eq!(Networks::accumulated_commission(), 0); 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, 0, false));
assert_ok!(do_clap_from(session_index, network_id, 1, false)); assert_ok!(do_clap_from(session_index, network_id, 1, false));
assert_eq!(Networks::accumulated_commission(), amount.saturating_div(2)); assert_eq!(Networks::accumulated_commission(), amount);
assert_eq!(Networks::is_nullification_period(), false); assert_eq!(Networks::is_nullification_period(), false);
assert_eq!(BridgedInflationCurve::<RewardCurve, Runtime>::era_payout( assert_eq!(BridgedInflationCurve::<RewardCurve, Runtime>::era_payout(
total_staked, total_staked,
(total_issuance + amount).into(), total_issuance,
0), (1260099399952u128, 208739900600048u128)); // precomputed values 0), (420000000000000, 0)); // precomputed values
assert_eq!(Networks::is_nullification_period(), true); assert_eq!(Networks::is_nullification_period(), true);
Networks::on_finalize(System::block_number()); Networks::on_finalize(System::block_number());
@ -793,10 +793,40 @@ fn should_emit_event_on_each_clap_and_on_applause() {
}); });
} }
#[test]
fn should_not_fail_on_sub_existential_balance() {
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(1_000_000_000)); // 100%
let session_index = advance_session_and_get_index();
let received_claps_key = (session_index, transaction_hash, unique_transaction_hash);
assert_eq!(Networks::accumulated_commission(), 0);
assert_eq!(Networks::gatekeeper_amount(network_id), 0);
assert_eq!(Networks::bridged_imbalance().bridged_in, 0);
assert_eq!(Networks::bridged_imbalance().bridged_out, 0);
assert_eq!(Balances::balance(&receiver), 0);
assert_eq!(SlowClap::applauses_for_transaction(&received_claps_key), false);
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);
assert_eq!(Networks::gatekeeper_amount(network_id), amount);
assert_eq!(Networks::bridged_imbalance().bridged_in, 0);
assert_eq!(Networks::bridged_imbalance().bridged_out, 0);
assert_eq!(Balances::balance(&receiver), 0);
assert_eq!(SlowClap::applauses_for_transaction(&received_claps_key), true);
});
}
// 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
// TODO: deal with below existential amount after commission
fn advance_session_and_get_index() -> u32 { fn advance_session_and_get_index() -> u32 {
advance_session(); advance_session();