mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 21:01:02 +00:00
Finality Pallet Rate Limiter (#720)
* Add simple rate limiting mechanism * Add tests * Small test cleanup * Hook MaxRequests into runtimes
This commit is contained in:
committed by
Bastian Köcher
parent
010748e409
commit
d835233571
@@ -300,11 +300,20 @@ impl pallet_substrate_bridge::Config for Runtime {
|
|||||||
type BridgedChain = bp_rialto::Rialto;
|
type BridgedChain = bp_rialto::Rialto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parameter_types! {
|
||||||
|
// This is a pretty unscientific cap.
|
||||||
|
//
|
||||||
|
// Note that once this is hit the pallet will essentially throttle incoming requests down to one
|
||||||
|
// call per block.
|
||||||
|
pub const MaxRequests: u32 = 50;
|
||||||
|
}
|
||||||
|
|
||||||
impl pallet_finality_verifier::Config for Runtime {
|
impl pallet_finality_verifier::Config for Runtime {
|
||||||
type BridgedChain = bp_rialto::Rialto;
|
type BridgedChain = bp_rialto::Rialto;
|
||||||
type HeaderChain = pallet_substrate_bridge::Module<Runtime>;
|
type HeaderChain = pallet_substrate_bridge::Module<Runtime>;
|
||||||
type AncestryProof = Vec<bp_rialto::Header>;
|
type AncestryProof = Vec<bp_rialto::Header>;
|
||||||
type AncestryChecker = bp_header_chain::LinearAncestryChecker;
|
type AncestryChecker = bp_header_chain::LinearAncestryChecker;
|
||||||
|
type MaxRequests = MaxRequests;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl pallet_shift_session_manager::Config for Runtime {}
|
impl pallet_shift_session_manager::Config for Runtime {}
|
||||||
|
|||||||
@@ -407,11 +407,20 @@ impl pallet_substrate_bridge::Config for Runtime {
|
|||||||
type BridgedChain = bp_millau::Millau;
|
type BridgedChain = bp_millau::Millau;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parameter_types! {
|
||||||
|
// This is a pretty unscientific cap.
|
||||||
|
//
|
||||||
|
// Note that once this is hit the pallet will essentially throttle incoming requests down to one
|
||||||
|
// call per block.
|
||||||
|
pub const MaxRequests: u32 = 50;
|
||||||
|
}
|
||||||
|
|
||||||
impl pallet_finality_verifier::Config for Runtime {
|
impl pallet_finality_verifier::Config for Runtime {
|
||||||
type BridgedChain = bp_millau::Millau;
|
type BridgedChain = bp_millau::Millau;
|
||||||
type HeaderChain = pallet_substrate_bridge::Module<Runtime>;
|
type HeaderChain = pallet_substrate_bridge::Module<Runtime>;
|
||||||
type AncestryProof = Vec<bp_millau::Header>;
|
type AncestryProof = Vec<bp_millau::Header>;
|
||||||
type AncestryChecker = bp_header_chain::LinearAncestryChecker;
|
type AncestryChecker = bp_header_chain::LinearAncestryChecker;
|
||||||
|
type MaxRequests = MaxRequests;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl pallet_shift_session_manager::Config for Runtime {}
|
impl pallet_shift_session_manager::Config for Runtime {}
|
||||||
|
|||||||
@@ -72,13 +72,28 @@ pub mod pallet {
|
|||||||
/// The type through which we will verify that a given header is related to the last
|
/// The type through which we will verify that a given header is related to the last
|
||||||
/// finalized header in our storage pallet.
|
/// finalized header in our storage pallet.
|
||||||
type AncestryChecker: AncestryChecker<<Self::BridgedChain as Chain>::Header, Self::AncestryProof>;
|
type AncestryChecker: AncestryChecker<<Self::BridgedChain as Chain>::Header, Self::AncestryProof>;
|
||||||
|
|
||||||
|
/// The upper bound on the number of requests allowed by the pallet.
|
||||||
|
///
|
||||||
|
/// Once this bound is reached the pallet will not allow any dispatchables to be called
|
||||||
|
/// until the request count has decreased.
|
||||||
|
#[pallet::constant]
|
||||||
|
type MaxRequests: Get<u32>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pallet::pallet]
|
#[pallet::pallet]
|
||||||
pub struct Pallet<T>(PhantomData<T>);
|
pub struct Pallet<T>(PhantomData<T>);
|
||||||
|
|
||||||
#[pallet::hooks]
|
#[pallet::hooks]
|
||||||
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
|
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
|
||||||
|
fn on_initialize(_n: T::BlockNumber) -> frame_support::weights::Weight {
|
||||||
|
<RequestCount<T>>::mutate(|count| *count = count.saturating_sub(1));
|
||||||
|
|
||||||
|
(0_u64)
|
||||||
|
.saturating_add(T::DbWeight::get().reads(1))
|
||||||
|
.saturating_add(T::DbWeight::get().writes(1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[pallet::call]
|
#[pallet::call]
|
||||||
impl<T: Config> Pallet<T> {
|
impl<T: Config> Pallet<T> {
|
||||||
@@ -98,6 +113,12 @@ pub mod pallet {
|
|||||||
) -> DispatchResultWithPostInfo {
|
) -> DispatchResultWithPostInfo {
|
||||||
let _ = ensure_signed(origin)?;
|
let _ = ensure_signed(origin)?;
|
||||||
|
|
||||||
|
ensure!(
|
||||||
|
Self::request_count() < T::MaxRequests::get(),
|
||||||
|
<Error<T>>::TooManyRequests
|
||||||
|
);
|
||||||
|
<RequestCount<T>>::mutate(|count| *count += 1);
|
||||||
|
|
||||||
frame_support::debug::trace!("Going to try and finalize header {:?}", finality_target);
|
frame_support::debug::trace!("Going to try and finalize header {:?}", finality_target);
|
||||||
|
|
||||||
let authority_set = T::HeaderChain::authority_set();
|
let authority_set = T::HeaderChain::authority_set();
|
||||||
@@ -127,6 +148,17 @@ pub mod pallet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The current number of requests for calling dispatchables.
|
||||||
|
///
|
||||||
|
/// If the `RequestCount` hits `MaxRequests`, no more calls will be allowed to the pallet until
|
||||||
|
/// the request capacity is increased.
|
||||||
|
///
|
||||||
|
/// The `RequestCount` is decreased by one at the beginning of every block. This is to ensure
|
||||||
|
/// that the pallet can always make progress.
|
||||||
|
#[pallet::storage]
|
||||||
|
#[pallet::getter(fn request_count)]
|
||||||
|
pub(super) type RequestCount<T: Config> = StorageValue<_, u32, ValueQuery>;
|
||||||
|
|
||||||
#[pallet::error]
|
#[pallet::error]
|
||||||
pub enum Error<T> {
|
pub enum Error<T> {
|
||||||
/// The given justification is invalid for the given header.
|
/// The given justification is invalid for the given header.
|
||||||
@@ -138,6 +170,8 @@ pub mod pallet {
|
|||||||
InvalidAuthoritySet,
|
InvalidAuthoritySet,
|
||||||
/// Failed to write a header to the underlying header chain.
|
/// Failed to write a header to the underlying header chain.
|
||||||
FailedToWriteHeader,
|
FailedToWriteHeader,
|
||||||
|
/// There are too many requests for the current window to handle.
|
||||||
|
TooManyRequests,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,27 +200,34 @@ mod tests {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
fn submit_finality_proof() -> frame_support::dispatch::DispatchResultWithPostInfo {
|
||||||
fn succesfully_imports_header_with_valid_finality_and_ancestry_proofs() {
|
|
||||||
run_test(|| {
|
|
||||||
initialize_substrate_bridge();
|
|
||||||
|
|
||||||
let child = test_header(1);
|
let child = test_header(1);
|
||||||
let header = test_header(2);
|
let header = test_header(2);
|
||||||
|
|
||||||
let set_id = 1;
|
let set_id = 1;
|
||||||
let grandpa_round = 1;
|
let grandpa_round = 1;
|
||||||
let justification =
|
let justification = make_justification_for_header(&header, grandpa_round, set_id, &authority_list()).encode();
|
||||||
make_justification_for_header(&header, grandpa_round, set_id, &authority_list()).encode();
|
|
||||||
let ancestry_proof = vec![child, header.clone()];
|
let ancestry_proof = vec![child, header.clone()];
|
||||||
|
|
||||||
assert_ok!(Module::<TestRuntime>::submit_finality_proof(
|
Module::<TestRuntime>::submit_finality_proof(Origin::signed(1), header, justification, ancestry_proof)
|
||||||
Origin::signed(1),
|
}
|
||||||
header.clone(),
|
|
||||||
justification,
|
|
||||||
ancestry_proof,
|
|
||||||
));
|
|
||||||
|
|
||||||
|
fn next_block() {
|
||||||
|
use frame_support::traits::OnInitialize;
|
||||||
|
|
||||||
|
let current_number = frame_system::Module::<TestRuntime>::block_number();
|
||||||
|
frame_system::Module::<TestRuntime>::set_block_number(current_number + 1);
|
||||||
|
let _ = Module::<TestRuntime>::on_initialize(current_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn succesfully_imports_header_with_valid_finality_and_ancestry_proofs() {
|
||||||
|
run_test(|| {
|
||||||
|
initialize_substrate_bridge();
|
||||||
|
|
||||||
|
assert_ok!(submit_finality_proof());
|
||||||
|
|
||||||
|
let header = test_header(2);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pallet_substrate_bridge::Module::<TestRuntime>::best_headers(),
|
pallet_substrate_bridge::Module::<TestRuntime>::best_headers(),
|
||||||
vec![(*header.number(), header.hash())]
|
vec![(*header.number(), header.hash())]
|
||||||
@@ -288,4 +329,55 @@ mod tests {
|
|||||||
);
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn disallows_imports_once_limit_is_hit_in_single_block() {
|
||||||
|
run_test(|| {
|
||||||
|
initialize_substrate_bridge();
|
||||||
|
assert_ok!(submit_finality_proof());
|
||||||
|
assert_ok!(submit_finality_proof());
|
||||||
|
assert_err!(submit_finality_proof(), <Error<TestRuntime>>::TooManyRequests);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn allows_request_after_new_block_has_started() {
|
||||||
|
run_test(|| {
|
||||||
|
initialize_substrate_bridge();
|
||||||
|
assert_ok!(submit_finality_proof());
|
||||||
|
assert_ok!(submit_finality_proof());
|
||||||
|
|
||||||
|
next_block();
|
||||||
|
assert_ok!(submit_finality_proof());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn disallows_imports_once_limit_is_hit_across_different_blocks() {
|
||||||
|
run_test(|| {
|
||||||
|
initialize_substrate_bridge();
|
||||||
|
assert_ok!(submit_finality_proof());
|
||||||
|
assert_ok!(submit_finality_proof());
|
||||||
|
|
||||||
|
next_block();
|
||||||
|
assert_ok!(submit_finality_proof());
|
||||||
|
assert_err!(submit_finality_proof(), <Error<TestRuntime>>::TooManyRequests);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn allows_max_requests_after_long_time_with_no_activity() {
|
||||||
|
run_test(|| {
|
||||||
|
initialize_substrate_bridge();
|
||||||
|
assert_ok!(submit_finality_proof());
|
||||||
|
assert_ok!(submit_finality_proof());
|
||||||
|
|
||||||
|
next_block();
|
||||||
|
next_block();
|
||||||
|
|
||||||
|
next_block();
|
||||||
|
assert_ok!(submit_finality_proof());
|
||||||
|
assert_ok!(submit_finality_proof());
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ impl pallet_substrate_bridge::Config for TestRuntime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
pub const MaxElementsInSingleProof: Option<u32> = Some(5);
|
pub const MaxRequests: u32 = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::pallet::Config for TestRuntime {
|
impl crate::pallet::Config for TestRuntime {
|
||||||
@@ -79,6 +79,7 @@ impl crate::pallet::Config for TestRuntime {
|
|||||||
type HeaderChain = pallet_substrate_bridge::Module<TestRuntime>;
|
type HeaderChain = pallet_substrate_bridge::Module<TestRuntime>;
|
||||||
type AncestryProof = Vec<<Self::BridgedChain as Chain>::Header>;
|
type AncestryProof = Vec<<Self::BridgedChain as Chain>::Header>;
|
||||||
type AncestryChecker = Checker<<Self::BridgedChain as Chain>::Header, Self::AncestryProof>;
|
type AncestryChecker = Checker<<Self::BridgedChain as Chain>::Header, Self::AncestryProof>;
|
||||||
|
type MaxRequests = MaxRequests;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|||||||
Reference in New Issue
Block a user