mitigate FROST/OCW DoS via dynamic bridge out threshold
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
This commit is contained in:
parent
d3b926cd47
commit
8ef28234ae
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ghost-exodus"
|
||||
version = "0.0.12"
|
||||
version = "0.0.13"
|
||||
description = "Threshold signature generation with DKG included"
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
@ -506,7 +506,7 @@ mod benchmarks {
|
||||
QualificationDkgState::<T>::insert(&network_curve, dkg_state);
|
||||
ActiveDkgAuthorities::<T>::insert(&network_curve, active_dkg);
|
||||
ActiveAuthorities::<T>::insert(&network_curve, authorities);
|
||||
ExodusRequests::<T>::insert(network_curve, exodus_session, request);
|
||||
ExodusRequests::<T>::insert((network_curve, exodus_session), request);
|
||||
|
||||
let nonce_package = PackageContext::default()
|
||||
.with_network_curve(NetworkCurve::Secp256k1)
|
||||
@ -584,7 +584,7 @@ mod benchmarks {
|
||||
QualificationDkgState::<T>::insert(&network_curve, dkg_state);
|
||||
ActiveDkgAuthorities::<T>::insert(&network_curve, active_dkg);
|
||||
ActiveAuthorities::<T>::insert(&network_curve, authorities);
|
||||
ExodusRequests::<T>::insert(network_curve, exodus_session, request);
|
||||
ExodusRequests::<T>::insert((network_curve, exodus_session), request);
|
||||
|
||||
let mut roast_state = RoastSessionState::<ParticipantsBitmap::<T>>::default();
|
||||
roast_state.status = RoastStatus::GroupCommitments;
|
||||
@ -723,7 +723,7 @@ mod benchmarks {
|
||||
QualificationDkgState::<T>::insert(&network_curve, dkg_state);
|
||||
ActiveDkgAuthorities::<T>::insert(&network_curve, active_dkg);
|
||||
ActiveAuthorities::<T>::insert(&network_curve, authorities);
|
||||
ExodusRequests::<T>::insert(network_curve, exodus_session, request);
|
||||
ExodusRequests::<T>::insert((network_curve, exodus_session), request);
|
||||
|
||||
let mut roast_state = RoastSessionState::<ParticipantsBitmap::<T>>::default();
|
||||
roast_state.status = RoastStatus::PartialSignatures;
|
||||
@ -796,7 +796,7 @@ mod benchmarks {
|
||||
assert!(!consensus_exists);
|
||||
assert!(!committers_exists);
|
||||
|
||||
assert!(!ExodusRequests::<T>::contains_key(&network_curve, &exodus_session));
|
||||
assert!(!ExodusRequests::<T>::contains_key((&network_curve, &exodus_session)));
|
||||
assert!(ExodusSignedGovernance::<T>::contains_key((&exodus_session, network_id), dkg_index));
|
||||
|
||||
Ok(())
|
||||
@ -808,7 +808,7 @@ mod benchmarks {
|
||||
let (_, network_curve, _, _) = prepare_pallet::<T>(1usize);
|
||||
|
||||
let network_id = NetworkIdOf::<T>::default();
|
||||
let base_amount: BalanceOf<T> = 10_000u32.into();
|
||||
let base_amount: BalanceOf<T> = T::BaseEvmAmount::get().unique_saturated_into();
|
||||
let amount = base_amount.saturating_add(T::Currency::minimum_balance());
|
||||
|
||||
let bounty = Perbill::from_percent(5);
|
||||
@ -840,7 +840,7 @@ mod benchmarks {
|
||||
receiver,
|
||||
);
|
||||
|
||||
assert!(ExodusRequests::<T>::contains_key(&network_curve, current_exodus_session));
|
||||
assert!(ExodusRequests::<T>::contains_key((&network_curve, current_exodus_session)));
|
||||
assert_eq!(CurrentExodus::<T>::get(), current_exodus_session + 1);
|
||||
assert_eq!(T::Currency::balance(&caller), initial_balance - amount);
|
||||
assert_eq!(T::Currency::total_issuance(), total_issuance - amount);
|
||||
|
||||
@ -423,6 +423,9 @@ pub mod pallet {
|
||||
#[pallet::constant]
|
||||
type MaxCursorLen: Get<u32>;
|
||||
|
||||
#[pallet::constant]
|
||||
type BaseEvmAmount: Get<u128>;
|
||||
|
||||
type WeightInfo: WeightInfo;
|
||||
}
|
||||
|
||||
@ -502,7 +505,7 @@ pub mod pallet {
|
||||
NoActiveAuthorities,
|
||||
InvalidSignatureShare,
|
||||
VerifyingShareNotFound,
|
||||
BelowExistentialBalance,
|
||||
BridgeQueueMinimumNotMet,
|
||||
DkgAuthoritiesInProgress,
|
||||
PackagesAlreadyRegistered,
|
||||
DkgAuthoritiesNotInitialized,
|
||||
@ -551,10 +554,12 @@ pub mod pallet {
|
||||
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn exodus_requests)]
|
||||
pub(super) type ExodusRequests<T: Config> = StorageDoubleMap<
|
||||
pub(super) type ExodusRequests<T: Config> = CountedStorageNMap<
|
||||
_,
|
||||
Twox64Concat, NetworkCurve,
|
||||
Twox64Concat, ExodusSession,
|
||||
(
|
||||
NMapKey<Twox64Concat, NetworkCurve>,
|
||||
NMapKey<Twox64Concat, ExodusSession>,
|
||||
),
|
||||
ExodusRequest<NetworkIdOf<T>, BalanceOf<T>>,
|
||||
OptionQuery,
|
||||
>;
|
||||
@ -1450,7 +1455,7 @@ pub mod pallet {
|
||||
exodus_request.next_roast_session = roast_session
|
||||
.saturating_add(1);
|
||||
|
||||
ExodusRequests::<T>::insert(&network_curve, &exodus_session, exodus_request);
|
||||
ExodusRequests::<T>::insert((&network_curve, &exodus_session), exodus_request);
|
||||
}
|
||||
|
||||
RoastSessions::<T>::insert(&exodus_session, &authority_index, roast_session);
|
||||
@ -1508,7 +1513,7 @@ pub mod pallet {
|
||||
let exodus_session = package.session;
|
||||
|
||||
ensure!(
|
||||
ExodusRequests::<T>::contains_key(&network_curve, &exodus_session),
|
||||
ExodusRequests::<T>::contains_key((&network_curve, &exodus_session)),
|
||||
Error::<T>::ExodusRequestNotFound
|
||||
);
|
||||
|
||||
@ -1640,7 +1645,7 @@ pub mod pallet {
|
||||
);
|
||||
|
||||
let exodus_request =
|
||||
ExodusRequests::<T>::get(&network_curve, &exodus_session)
|
||||
ExodusRequests::<T>::get((&network_curve, &exodus_session))
|
||||
.ok_or(Error::<T>::ExodusRequestNotFound)?;
|
||||
|
||||
ensure!(
|
||||
@ -1795,7 +1800,7 @@ pub mod pallet {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
ExodusRequests::<T>::remove(&network_curve, &exodus_session);
|
||||
ExodusRequests::<T>::remove((&network_curve, &exodus_session));
|
||||
|
||||
match exodus_request.r#type {
|
||||
ExodusRequestType::EvmRotation { .. } => {
|
||||
@ -1838,10 +1843,17 @@ pub mod pallet {
|
||||
) -> DispatchResult {
|
||||
let who = ensure_signed(origin)?;
|
||||
|
||||
ensure!(
|
||||
amount > T::Currency::minimum_balance(),
|
||||
Error::<T>::BelowExistentialBalance
|
||||
);
|
||||
let base_evm_amount: BalanceOf<T> = T::BaseEvmAmount::get()
|
||||
.unique_saturated_into();
|
||||
|
||||
let pending_requests_count = ExodusRequests::<T>::count()
|
||||
.unique_saturated_into();
|
||||
|
||||
let minimum_amount = base_evm_amount
|
||||
.saturating_mul(pending_requests_count)
|
||||
.saturating_add(T::Currency::minimum_balance());
|
||||
|
||||
ensure!(amount >= minimum_amount, Error::<T>::BridgeQueueMinimumNotMet);
|
||||
|
||||
let actual_amount = T::Currency::burn_from(
|
||||
&who,
|
||||
@ -1861,7 +1873,7 @@ pub mod pallet {
|
||||
Self::deposit_event(Event::<T>::EvmBridgeOutRegistered {
|
||||
who,
|
||||
network_id,
|
||||
amount,
|
||||
amount: actual_amount,
|
||||
bounty,
|
||||
receiver,
|
||||
});
|
||||
@ -2151,7 +2163,7 @@ impl<T: Config> Pallet<T> {
|
||||
let exodus_session = CurrentExodus::<T>::get();
|
||||
let request = request_builder(exodus_session);
|
||||
|
||||
ExodusRequests::<T>::insert(&network.curve, &exodus_session, request);
|
||||
ExodusRequests::<T>::insert((&network.curve, &exodus_session), request);
|
||||
CurrentExodus::<T>::put(exodus_session.saturating_add(1));
|
||||
|
||||
Ok(())
|
||||
@ -2566,7 +2578,7 @@ impl<T: Config> Pallet<T> {
|
||||
};
|
||||
|
||||
weight.saturating_accrue(T::DbWeight::get().writes(1));
|
||||
ExodusRequests::<T>::insert(&network_curve, &exodus_session, exodus_request);
|
||||
ExodusRequests::<T>::insert((&network_curve, &exodus_session), exodus_request);
|
||||
|
||||
if let None = dkg_qualification
|
||||
.try_push_rotation_session(exodus_session, network_type) {
|
||||
@ -2607,7 +2619,7 @@ impl<T: Config> Pallet<T> {
|
||||
let r#type = rotation_session.network_type;
|
||||
|
||||
weight.saturating_accrue(T::DbWeight::get().reads(1));
|
||||
if ExodusRequests::<T>::contains_key(&network_curve, &session) {
|
||||
if ExodusRequests::<T>::contains_key((&network_curve, &session)) {
|
||||
return false
|
||||
};
|
||||
|
||||
@ -3088,7 +3100,7 @@ impl<T: Config> Pallet<T> {
|
||||
}
|
||||
|
||||
let exodus_request =
|
||||
ExodusRequests::<T>::get(&network_curve, &package.session)
|
||||
ExodusRequests::<T>::get((&network_curve, &package.session))
|
||||
.ok_or(InvalidTransaction::BadProof)?;
|
||||
|
||||
let roast_session =
|
||||
@ -3286,7 +3298,7 @@ impl<T: Config> Pallet<T> {
|
||||
|
||||
let threshold = get_byzantium_quorum_threshold(max_authorities);
|
||||
|
||||
let mut exodus_requests = ExodusRequests::<T>::iter_prefix(&network_curve)
|
||||
let mut exodus_requests = ExodusRequests::<T>::iter_prefix((&network_curve,))
|
||||
.collect::<Vec<(ExodusSession, ExodusRequest<_, _>)>>();
|
||||
|
||||
exodus_requests.sort_by_key(|&(session, _)| session);
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
use frame_system::EnsureRoot;
|
||||
use frame_support::{
|
||||
derive_impl, parameter_types,
|
||||
traits::{ConstU32, ConstU64},
|
||||
traits::{ConstU32, ConstU64, ConstU128},
|
||||
};
|
||||
use crate as ghost_exodus;
|
||||
|
||||
@ -143,6 +143,7 @@ impl ghost_exodus::Config for Runtime {
|
||||
type DkgRoundPeriod = ConstU64<100>;
|
||||
type RemovalLimit = ConstU32<20>;
|
||||
type MaxCursorLen = ConstU32<20>;
|
||||
type BaseEvmAmount = ConstU128<5>;
|
||||
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
@ -810,7 +810,7 @@ fn run_dkg_session(
|
||||
return Err(TestError::InvalidStorageAfter);
|
||||
}
|
||||
|
||||
if ExodusRequests::<Runtime>::contains_key(curve, exodus) {
|
||||
if ExodusRequests::<Runtime>::contains_key((curve, exodus)) {
|
||||
let roast_state = RoastSessionStates::<Runtime>::get(exodus, roast);
|
||||
if !roast_state.partial_signers.contains(idx)
|
||||
|| !roast_state.nonce_committers.contains(idx)
|
||||
@ -881,7 +881,7 @@ fn run_dkg_session(
|
||||
|
||||
for auth in prev_authorities.iter() {
|
||||
let request_exists = ext.execute_with(|| {
|
||||
ExodusRequests::<Runtime>::contains_key(&curve, &exodus_session)
|
||||
ExodusRequests::<Runtime>::contains_key((&curve, &exodus_session))
|
||||
});
|
||||
|
||||
if !request_exists { break; }
|
||||
@ -1446,6 +1446,118 @@ fn test_prepare_round5_kick_on_empty_justifications() {
|
||||
assert_eq!(indexes.active_bits_count(), authorities_len as u32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn register_evm_bridge_out_dynamic_threshold_works() {
|
||||
let (mut ext, _) = new_test_ext();
|
||||
|
||||
ext.execute_with(|| {
|
||||
let network_curve = NetworkCurve::Secp256k1;
|
||||
let network_id = 69u64;
|
||||
let user = 420u64;
|
||||
|
||||
let receiver = EvmAddress::from([2u8; 20]);
|
||||
let bounty = Perbill::from_percent(10);
|
||||
|
||||
let initial_balance: BalanceOf<Runtime> = 100_000u128.unique_saturated_into();
|
||||
let _ = <Runtime as Config>::Currency::set_balance(&user, initial_balance);
|
||||
|
||||
let base_evm_amount_u128 = <<Runtime as Config>::BaseEvmAmount as Get<u128>>::get();
|
||||
let base_evm_amount: BalanceOf<Runtime> = base_evm_amount_u128.unique_saturated_into();
|
||||
let existential_deposit = <Runtime as Config>::Currency::minimum_balance();
|
||||
|
||||
let network_data = NetworkDataBuilder::default()
|
||||
.with_network_curve(network_curve)
|
||||
.build();
|
||||
|
||||
let _ = <Runtime as Config>::NetworkDataHandler::register(network_id, network_data).unwrap();
|
||||
let _ = <Runtime as Config>::NetworkDataHandler::register_incoming(&network_id, 1337).unwrap();
|
||||
|
||||
let mut dkg_state = PendingDkgAuthorities::default();
|
||||
loop {
|
||||
if dkg_state.is_dkg_finalized() { break; }
|
||||
dkg_state.next_phase();
|
||||
}
|
||||
|
||||
QualificationDkgState::<Runtime>::insert(&network_curve, dkg_state);
|
||||
|
||||
let amount_1 = existential_deposit;
|
||||
assert_ok!(Exodus::register_evm_bridge_out_exodus(
|
||||
RuntimeOrigin::signed(user.clone()),
|
||||
network_id,
|
||||
amount_1,
|
||||
bounty,
|
||||
receiver,
|
||||
));
|
||||
|
||||
assert_eq!(ExodusRequests::<Runtime>::count(), 1);
|
||||
|
||||
System::assert_last_event(RuntimeEvent::Exodus(
|
||||
Event::EvmBridgeOutRegistered {
|
||||
who: user.clone(),
|
||||
network_id,
|
||||
amount: amount_1,
|
||||
bounty,
|
||||
receiver,
|
||||
}
|
||||
));
|
||||
|
||||
assert_err!(
|
||||
Exodus::register_evm_bridge_out_exodus(
|
||||
RuntimeOrigin::signed(user.clone()),
|
||||
network_id,
|
||||
amount_1,
|
||||
bounty,
|
||||
receiver,
|
||||
),
|
||||
Error::<Runtime>::BridgeQueueMinimumNotMet
|
||||
);
|
||||
|
||||
let amount_2 = existential_deposit.saturating_add(base_evm_amount);
|
||||
assert_ok!(Exodus::register_evm_bridge_out_exodus(
|
||||
RuntimeOrigin::signed(user.clone()),
|
||||
network_id,
|
||||
amount_2,
|
||||
bounty,
|
||||
receiver,
|
||||
));
|
||||
|
||||
assert_err!(
|
||||
Exodus::register_evm_bridge_out_exodus(
|
||||
RuntimeOrigin::signed(user.clone()),
|
||||
network_id,
|
||||
amount_2,
|
||||
bounty,
|
||||
receiver,
|
||||
),
|
||||
Error::<Runtime>::BridgeQueueMinimumNotMet
|
||||
);
|
||||
|
||||
assert_eq!(ExodusRequests::<Runtime>::count(), 2);
|
||||
|
||||
let amount_3 = amount_2.saturating_add(base_evm_amount);
|
||||
assert_ok!(Exodus::register_evm_bridge_out_exodus(
|
||||
RuntimeOrigin::signed(user.clone()),
|
||||
network_id,
|
||||
amount_3,
|
||||
bounty,
|
||||
receiver,
|
||||
));
|
||||
|
||||
assert_eq!(ExodusRequests::<Runtime>::count(), 3);
|
||||
|
||||
assert_err!(
|
||||
Exodus::register_evm_bridge_out_exodus(
|
||||
RuntimeOrigin::signed(user.clone()),
|
||||
network_id,
|
||||
amount_3,
|
||||
bounty,
|
||||
receiver,
|
||||
),
|
||||
Error::<Runtime>::BridgeQueueMinimumNotMet
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_merkle_tree_success_power_of_two_for_shares() {
|
||||
let network_curve = NetworkCurve::Secp256k1;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user