diff --git a/bridges/bin/millau/runtime/src/lib.rs b/bridges/bin/millau/runtime/src/lib.rs index 437c9ff36f..822c0229ea 100644 --- a/bridges/bin/millau/runtime/src/lib.rs +++ b/bridges/bin/millau/runtime/src/lib.rs @@ -309,12 +309,19 @@ parameter_types! { // call per block. pub const MaxRequests: u32 = 50; pub const WestendValidatorCount: u32 = 255; + + // Number of headers to keep. + // + // Assuming the worst case of every header being finalized, we will keep headers for at least a + // week. + pub const HeadersToKeep: u32 = 7 * bp_millau::DAYS as u32; } pub type RialtoGrandpaInstance = (); impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_rialto::Rialto; type MaxRequests = MaxRequests; + type HeadersToKeep = HeadersToKeep; // TODO [#391]: Use weights generated for the Millau runtime instead of Rialto ones. type WeightInfo = pallet_bridge_grandpa::weights::RialtoWeight; @@ -324,6 +331,7 @@ pub type WestendGrandpaInstance = pallet_bridge_grandpa::Instance1; impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_westend::Westend; type MaxRequests = MaxRequests; + type HeadersToKeep = HeadersToKeep; // TODO [#391]: Use weights generated for the Millau runtime instead of Rialto ones. type WeightInfo = pallet_bridge_grandpa::weights::RialtoWeight; diff --git a/bridges/bin/rialto/runtime/src/lib.rs b/bridges/bin/rialto/runtime/src/lib.rs index 4db3101810..603365130a 100644 --- a/bridges/bin/rialto/runtime/src/lib.rs +++ b/bridges/bin/rialto/runtime/src/lib.rs @@ -414,11 +414,18 @@ parameter_types! { // Note that once this is hit the pallet will essentially throttle incoming requests down to one // call per block. pub const MaxRequests: u32 = 50; + + // Number of headers to keep. + // + // Assuming the worst case of every header being finalized, we will keep headers at least for a + // week. + pub const HeadersToKeep: u32 = 7 * bp_rialto::DAYS as u32; } impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_millau::Millau; type MaxRequests = MaxRequests; + type HeadersToKeep = HeadersToKeep; type WeightInfo = pallet_bridge_grandpa::weights::RialtoWeight; } diff --git a/bridges/modules/grandpa/src/lib.rs b/bridges/modules/grandpa/src/lib.rs index 58f58d6aa6..98f47dd6f1 100644 --- a/bridges/modules/grandpa/src/lib.rs +++ b/bridges/modules/grandpa/src/lib.rs @@ -91,6 +91,14 @@ pub mod pallet { #[pallet::constant] type MaxRequests: Get; + /// Maximal number of finalized headers to keep in the storage. + /// + /// The setting is there to prevent growing the on-chain state indefinitely. Note + /// the setting does not relate to block numbers - we will simply keep as much items + /// in the storage, so it doesn't guarantee any fixed timeframe for finality headers. + #[pallet::constant] + type HeadersToKeep: Get; + /// Weights gathered through benchmarking. type WeightInfo: WeightInfo; } @@ -153,10 +161,20 @@ pub mod pallet { verify_justification::(&justification, hash, *number, authority_set)?; let _enacted = try_enact_authority_change::(&finality_target, set_id)?; + let index = >::get(); + let pruning = >::try_get(index); >::put(hash); >::insert(hash, finality_target); + >::insert(index, hash); >::mutate(|count| *count += 1); + // Update ring buffer pointer and remove old header. + >::put((index + 1) % T::HeadersToKeep::get()); + if let Ok(hash) = pruning { + log::debug!(target: "runtime::bridge-grandpa", "Pruning old header: {:?}.", hash); + >::remove(hash); + } + log::info!(target: "runtime::bridge-grandpa", "Succesfully imported finalized header with hash {:?}!", hash); Ok(().into()) @@ -248,6 +266,15 @@ pub mod pallet { #[pallet::storage] pub(super) type BestFinalized, I: 'static = ()> = StorageValue<_, BridgedBlockHash, ValueQuery>; + /// A ring buffer of imported hashes. Ordered by the insertion time. + #[pallet::storage] + pub(super) type ImportedHashes, I: 'static = ()> = + StorageMap<_, Identity, u32, BridgedBlockHash>; + + /// Current ring buffer position. + #[pallet::storage] + pub(super) type ImportedHashesPointer, I: 'static = ()> = StorageValue<_, u32, ValueQuery>; + /// Headers which have been imported into the pallet. #[pallet::storage] pub(super) type ImportedHeaders, I: 'static = ()> = @@ -999,4 +1026,30 @@ mod tests { assert_ok!(submit_finality_proof(7)); }) } + + #[test] + fn should_prune_headers_over_headers_to_keep_parameter() { + run_test(|| { + initialize_substrate_bridge(); + assert_ok!(submit_finality_proof(1)); + let first_header = Pallet::::best_finalized(); + next_block(); + + assert_ok!(submit_finality_proof(2)); + next_block(); + assert_ok!(submit_finality_proof(3)); + next_block(); + assert_ok!(submit_finality_proof(4)); + next_block(); + assert_ok!(submit_finality_proof(5)); + next_block(); + + assert_ok!(submit_finality_proof(6)); + + assert!( + !Pallet::::is_known_header(first_header.hash()), + "First header should be pruned." + ); + }) + } } diff --git a/bridges/modules/grandpa/src/mock.rs b/bridges/modules/grandpa/src/mock.rs index 7d5901a679..20f5ea7bdf 100644 --- a/bridges/modules/grandpa/src/mock.rs +++ b/bridges/modules/grandpa/src/mock.rs @@ -81,6 +81,7 @@ impl frame_system::Config for TestRuntime { parameter_types! { pub const MaxRequests: u32 = 2; + pub const HeadersToKeep: u32 = 5; pub const SessionLength: u64 = 5; pub const NumValidators: u32 = 5; } @@ -88,6 +89,7 @@ parameter_types! { impl grandpa::Config for TestRuntime { type BridgedChain = TestBridgedChain; type MaxRequests = MaxRequests; + type HeadersToKeep = HeadersToKeep; type WeightInfo = (); }