Add events to the bridge parachains pallet (#1568)

* add events to the bridge parachains pallet

* clippy + spellcheck

* fix compilation

* untracked is not a word?
This commit is contained in:
Svyatoslav Nikolsky
2022-09-09 13:09:05 +03:00
committed by Bastian Köcher
parent 95c30c780c
commit 653ff6ef0d
4 changed files with 275 additions and 17 deletions
+4 -2
View File
@@ -540,6 +540,7 @@ parameter_types! {
pub type WithRialtoParachainsInstance = (); pub type WithRialtoParachainsInstance = ();
impl pallet_bridge_parachains::Config<WithRialtoParachainsInstance> for Runtime { impl pallet_bridge_parachains::Config<WithRialtoParachainsInstance> for Runtime {
type Event = Event;
type WeightInfo = pallet_bridge_parachains::weights::MillauWeight<Runtime>; type WeightInfo = pallet_bridge_parachains::weights::MillauWeight<Runtime>;
type BridgesGrandpaPalletInstance = RialtoGrandpaInstance; type BridgesGrandpaPalletInstance = RialtoGrandpaInstance;
type ParasPalletName = RialtoParasPalletName; type ParasPalletName = RialtoParasPalletName;
@@ -551,6 +552,7 @@ impl pallet_bridge_parachains::Config<WithRialtoParachainsInstance> for Runtime
pub type WithWestendParachainsInstance = pallet_bridge_parachains::Instance1; pub type WithWestendParachainsInstance = pallet_bridge_parachains::Instance1;
impl pallet_bridge_parachains::Config<WithWestendParachainsInstance> for Runtime { impl pallet_bridge_parachains::Config<WithWestendParachainsInstance> for Runtime {
type Event = Event;
type WeightInfo = pallet_bridge_parachains::weights::MillauWeight<Runtime>; type WeightInfo = pallet_bridge_parachains::weights::MillauWeight<Runtime>;
type BridgesGrandpaPalletInstance = WestendGrandpaInstance; type BridgesGrandpaPalletInstance = WestendGrandpaInstance;
type ParasPalletName = WestendParasPalletName; type ParasPalletName = WestendParasPalletName;
@@ -592,10 +594,10 @@ construct_runtime!(
// Westend bridge modules. // Westend bridge modules.
BridgeWestendGrandpa: pallet_bridge_grandpa::<Instance1>::{Pallet, Call, Config<T>, Storage}, BridgeWestendGrandpa: pallet_bridge_grandpa::<Instance1>::{Pallet, Call, Config<T>, Storage},
BridgeWestendParachains: pallet_bridge_parachains::<Instance1>::{Pallet, Call, Storage}, BridgeWestendParachains: pallet_bridge_parachains::<Instance1>::{Pallet, Call, Storage, Event<T>},
// RialtoParachain bridge modules. // RialtoParachain bridge modules.
BridgeRialtoParachains: pallet_bridge_parachains::{Pallet, Call, Storage}, BridgeRialtoParachains: pallet_bridge_parachains::{Pallet, Call, Storage, Event<T>},
BridgeRialtoParachainMessages: pallet_bridge_messages::<Instance1>::{Pallet, Call, Storage, Event<T>, Config<T>}, BridgeRialtoParachainMessages: pallet_bridge_messages::<Instance1>::{Pallet, Call, Storage, Event<T>, Config<T>},
// Pallet for sending XCM. // Pallet for sending XCM.
+9 -3
View File
@@ -17,7 +17,7 @@
use crate::{Config, Pallet, RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use crate::{Config, Pallet, RelayBlockHash, RelayBlockHasher, RelayBlockNumber};
use bp_runtime::FilterCall; use bp_runtime::FilterCall;
use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; use frame_support::{dispatch::CallableCallFor, traits::IsSubType};
use sp_runtime::transaction_validity::{TransactionValidity, ValidTransaction}; use sp_runtime::transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction};
/// Validate parachain heads in order to avoid "mining" transactions that provide /// Validate parachain heads in order to avoid "mining" transactions that provide
/// outdated bridged parachain heads. Without this validation, even honest relayers /// outdated bridged parachain heads. Without this validation, even honest relayers
@@ -57,13 +57,19 @@ where
}; };
let maybe_stored_best_head = crate::ParasInfo::<T, I>::get(parachain); let maybe_stored_best_head = crate::ParasInfo::<T, I>::get(parachain);
Self::validate_updated_parachain_head( let is_valid = Self::validate_updated_parachain_head(
parachain, parachain,
&maybe_stored_best_head, &maybe_stored_best_head,
updated_at_relay_block_number, updated_at_relay_block_number,
parachain_head_hash, parachain_head_hash,
"Rejecting obsolete parachain-head transaction", "Rejecting obsolete parachain-head transaction",
) );
if is_valid {
Ok(ValidTransaction::default())
} else {
InvalidTransaction::Stale.into()
}
} }
} }
+254 -9
View File
@@ -77,6 +77,27 @@ pub mod pallet {
/// Weight info of the given parachains pallet. /// Weight info of the given parachains pallet.
pub type WeightInfoOf<T, I> = <T as Config<I>>::WeightInfo; pub type WeightInfoOf<T, I> = <T as Config<I>>::WeightInfo;
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config<I>, I: 'static = ()> {
/// The caller has provided head of parachain that the pallet is not configured to track.
UntrackedParachainRejected { parachain: ParaId },
/// The caller has declared that he has provided given parachain head, but it is missing
/// from the storage proof.
MissingParachainHead { parachain: ParaId },
/// The caller has provided parachain head hash that is not matching the hash read from the
/// storage proof.
IncorrectParachainHeadHash {
parachain: ParaId,
parachain_head_hash: ParaHash,
actual_parachain_head_hash: ParaHash,
},
/// The caller has provided obsolete parachain head, which is already known to the pallet.
RejectedObsoleteParachainHead { parachain: ParaId, parachain_head_hash: ParaHash },
/// Parachain head has been updated.
UpdatedParachainHead { parachain: ParaId, parachain_head_hash: ParaHash },
}
#[pallet::error] #[pallet::error]
pub enum Error<T, I = ()> { pub enum Error<T, I = ()> {
/// Relay chain block hash is unknown to us. /// Relay chain block hash is unknown to us.
@@ -100,6 +121,8 @@ pub mod pallet {
pub trait Config<I: 'static = ()>: pub trait Config<I: 'static = ()>:
pallet_bridge_grandpa::Config<Self::BridgesGrandpaPalletInstance> pallet_bridge_grandpa::Config<Self::BridgesGrandpaPalletInstance>
{ {
/// The overarching event type.
type Event: From<Event<Self, I>> + IsType<<Self as frame_system::Config>::Event>;
/// Benchmarks results from runtime we're plugged into. /// Benchmarks results from runtime we're plugged into.
type WeightInfo: WeightInfoExt; type WeightInfo: WeightInfoExt;
@@ -248,6 +271,7 @@ pub mod pallet {
"The head of parachain {:?} has been provided, but it is not tracked by the pallet", "The head of parachain {:?} has been provided, but it is not tracked by the pallet",
parachain, parachain,
); );
Self::deposit_event(Event::UntrackedParachainRejected { parachain });
continue; continue;
} }
@@ -264,6 +288,7 @@ pub mod pallet {
"Looks like it has been deregistered from the source relay chain" "Looks like it has been deregistered from the source relay chain"
}, },
); );
Self::deposit_event(Event::MissingParachainHead { parachain });
continue; continue;
}, },
Err(e) => { Err(e) => {
@@ -273,6 +298,7 @@ pub mod pallet {
parachain, parachain,
e, e,
); );
Self::deposit_event(Event::MissingParachainHead { parachain });
continue; continue;
}, },
}; };
@@ -288,6 +314,11 @@ pub mod pallet {
parachain_head_hash, parachain_head_hash,
actual_parachain_head_hash, actual_parachain_head_hash,
); );
Self::deposit_event(Event::IncorrectParachainHeadHash {
parachain,
parachain_head_hash,
actual_parachain_head_hash,
});
continue; continue;
} }
@@ -387,16 +418,20 @@ pub mod pallet {
/// Check if para head has been already updated at better relay chain block. /// Check if para head has been already updated at better relay chain block.
/// Without this check, we may import heads in random order. /// Without this check, we may import heads in random order.
///
/// Returns `true` if the pallet is ready to import given parachain head.
/// Returns `false` if the pallet already knows the same or better parachain head.
#[must_use]
pub fn validate_updated_parachain_head( pub fn validate_updated_parachain_head(
parachain: ParaId, parachain: ParaId,
maybe_stored_best_head: &Option<ParaInfo>, maybe_stored_best_head: &Option<ParaInfo>,
updated_at_relay_block_number: RelayBlockNumber, updated_at_relay_block_number: RelayBlockNumber,
updated_head_hash: ParaHash, updated_head_hash: ParaHash,
err_log_prefix: &str, err_log_prefix: &str,
) -> TransactionValidity { ) -> bool {
let stored_best_head = match maybe_stored_best_head { let stored_best_head = match maybe_stored_best_head {
Some(stored_best_head) => stored_best_head, Some(stored_best_head) => stored_best_head,
None => return Ok(ValidTransaction::default()), None => return true,
}; };
if stored_best_head.best_head_hash.at_relay_block_number >= if stored_best_head.best_head_hash.at_relay_block_number >=
@@ -410,7 +445,7 @@ pub mod pallet {
stored_best_head.best_head_hash.at_relay_block_number, stored_best_head.best_head_hash.at_relay_block_number,
updated_at_relay_block_number updated_at_relay_block_number
); );
return InvalidTransaction::Stale.into() return false
} }
if stored_best_head.best_head_hash.head_hash == updated_head_hash { if stored_best_head.best_head_hash.head_hash == updated_head_hash {
@@ -423,10 +458,10 @@ pub mod pallet {
stored_best_head.best_head_hash.at_relay_block_number, stored_best_head.best_head_hash.at_relay_block_number,
updated_at_relay_block_number updated_at_relay_block_number
); );
return InvalidTransaction::Stale.into() return false
} }
Ok(ValidTransaction::default()) true
} }
/// Try to update parachain head. /// Try to update parachain head.
@@ -439,14 +474,20 @@ pub mod pallet {
) -> Result<UpdateParachainHeadArtifacts, ()> { ) -> Result<UpdateParachainHeadArtifacts, ()> {
// check if head has been already updated at better relay chain block. Without this // check if head has been already updated at better relay chain block. Without this
// check, we may import heads in random order // check, we may import heads in random order
Self::validate_updated_parachain_head( let is_valid = Self::validate_updated_parachain_head(
parachain, parachain,
&stored_best_head, &stored_best_head,
updated_at_relay_block_number, updated_at_relay_block_number,
updated_head_hash, updated_head_hash,
"The parachain head can't be updated", "The parachain head can't be updated",
) );
.map_err(|_| ())?; if !is_valid {
Self::deposit_event(Event::RejectedObsoleteParachainHead {
parachain,
parachain_head_hash: updated_head_hash,
});
return Err(())
}
let next_imported_hash_position = stored_best_head let next_imported_hash_position = stored_best_head
.map_or(0, |stored_best_head| stored_best_head.next_imported_hash_position); .map_or(0, |stored_best_head| stored_best_head.next_imported_hash_position);
@@ -485,6 +526,10 @@ pub mod pallet {
); );
ImportedParaHeads::<T, I>::remove(parachain, head_hash_to_prune); ImportedParaHeads::<T, I>::remove(parachain, head_hash_to_prune);
} }
Self::deposit_event(Event::UpdatedParachainHead {
parachain,
parachain_head_hash: updated_head_hash,
});
Ok(UpdateParachainHeadArtifacts { best_head: updated_best_para_head, prune_happened }) Ok(UpdateParachainHeadArtifacts { best_head: updated_best_para_head, prune_happened })
} }
@@ -526,7 +571,8 @@ pub mod pallet {
mod tests { mod tests {
use super::*; use super::*;
use crate::mock::{ use crate::mock::{
run_test, test_relay_header, Origin, TestRuntime, PARAS_PALLET_NAME, UNTRACKED_PARACHAIN_ID, run_test, test_relay_header, Event as TestEvent, Origin, TestRuntime, PARAS_PALLET_NAME,
UNTRACKED_PARACHAIN_ID,
}; };
use codec::Encode; use codec::Encode;
@@ -545,6 +591,7 @@ mod tests {
traits::{Get, OnInitialize}, traits::{Get, OnInitialize},
weights::Weight, weights::Weight,
}; };
use frame_system::{EventRecord, Pallet as System, Phase};
use sp_runtime::DispatchError; use sp_runtime::DispatchError;
use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, Recorder, TrieMut}; use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, Recorder, TrieMut};
@@ -733,6 +780,28 @@ mod tests {
ImportedParaHeads::<TestRuntime>::get(ParaId(3), head_hash(3, 10)), ImportedParaHeads::<TestRuntime>::get(ParaId(3), head_hash(3, 10)),
Some(head_data(3, 10)) Some(head_data(3, 10))
); );
assert_eq!(
System::<TestRuntime>::events(),
vec![
EventRecord {
phase: Phase::Initialization,
event: TestEvent::Parachains(Event::UpdatedParachainHead {
parachain: ParaId(1),
parachain_head_hash: initial_best_head(1).best_head_hash.head_hash,
}),
topics: vec![],
},
EventRecord {
phase: Phase::Initialization,
event: TestEvent::Parachains(Event::UpdatedParachainHead {
parachain: ParaId(3),
parachain_head_hash: head_data(3, 10).hash(),
}),
topics: vec![],
}
],
);
}); });
} }
@@ -764,6 +833,17 @@ mod tests {
ImportedParaHeads::<TestRuntime>::get(ParaId(1), head_data(1, 10).hash()), ImportedParaHeads::<TestRuntime>::get(ParaId(1), head_data(1, 10).hash()),
None None
); );
assert_eq!(
System::<TestRuntime>::events(),
vec![EventRecord {
phase: Phase::Initialization,
event: TestEvent::Parachains(Event::UpdatedParachainHead {
parachain: ParaId(1),
parachain_head_hash: head_data(1, 5).hash(),
}),
topics: vec![],
}],
);
// import head#10 of parachain#1 at relay block #1 // import head#10 of parachain#1 at relay block #1
proceed(1, state_root_10); proceed(1, state_root_10);
@@ -786,6 +866,27 @@ mod tests {
ImportedParaHeads::<TestRuntime>::get(ParaId(1), head_data(1, 10).hash()), ImportedParaHeads::<TestRuntime>::get(ParaId(1), head_data(1, 10).hash()),
Some(head_data(1, 10)) Some(head_data(1, 10))
); );
assert_eq!(
System::<TestRuntime>::events(),
vec![
EventRecord {
phase: Phase::Initialization,
event: TestEvent::Parachains(Event::UpdatedParachainHead {
parachain: ParaId(1),
parachain_head_hash: head_data(1, 5).hash(),
}),
topics: vec![],
},
EventRecord {
phase: Phase::Initialization,
event: TestEvent::Parachains(Event::UpdatedParachainHead {
parachain: ParaId(1),
parachain_head_hash: head_data(1, 10).hash(),
}),
topics: vec![],
}
],
);
}); });
} }
@@ -834,6 +935,34 @@ mod tests {
next_imported_hash_position: 1, next_imported_hash_position: 1,
}) })
); );
assert_eq!(
System::<TestRuntime>::events(),
vec![
EventRecord {
phase: Phase::Initialization,
event: TestEvent::Parachains(Event::UpdatedParachainHead {
parachain: ParaId(1),
parachain_head_hash: head_data(1, 5).hash(),
}),
topics: vec![],
},
EventRecord {
phase: Phase::Initialization,
event: TestEvent::Parachains(Event::UntrackedParachainRejected {
parachain: ParaId(UNTRACKED_PARACHAIN_ID),
}),
topics: vec![],
},
EventRecord {
phase: Phase::Initialization,
event: TestEvent::Parachains(Event::UpdatedParachainHead {
parachain: ParaId(2),
parachain_head_hash: head_data(1, 5).hash(),
}),
topics: vec![],
}
],
);
}); });
} }
@@ -846,12 +975,44 @@ mod tests {
initialize(state_root); initialize(state_root);
assert_ok!(import_parachain_1_head(0, state_root, parachains.clone(), proof.clone())); assert_ok!(import_parachain_1_head(0, state_root, parachains.clone(), proof.clone()));
assert_eq!(ParasInfo::<TestRuntime>::get(ParaId(1)), Some(initial_best_head(1))); assert_eq!(ParasInfo::<TestRuntime>::get(ParaId(1)), Some(initial_best_head(1)));
assert_eq!(
System::<TestRuntime>::events(),
vec![EventRecord {
phase: Phase::Initialization,
event: TestEvent::Parachains(Event::UpdatedParachainHead {
parachain: ParaId(1),
parachain_head_hash: initial_best_head(1).best_head_hash.head_hash,
}),
topics: vec![],
}],
);
// try to import head#0 of parachain#1 at relay block#1 // try to import head#0 of parachain#1 at relay block#1
// => call succeeds, but nothing is changed // => call succeeds, but nothing is changed
proceed(1, state_root); proceed(1, state_root);
assert_ok!(import_parachain_1_head(1, state_root, parachains, proof)); assert_ok!(import_parachain_1_head(1, state_root, parachains, proof));
assert_eq!(ParasInfo::<TestRuntime>::get(ParaId(1)), Some(initial_best_head(1))); assert_eq!(ParasInfo::<TestRuntime>::get(ParaId(1)), Some(initial_best_head(1)));
assert_eq!(
System::<TestRuntime>::events(),
vec![
EventRecord {
phase: Phase::Initialization,
event: TestEvent::Parachains(Event::UpdatedParachainHead {
parachain: ParaId(1),
parachain_head_hash: initial_best_head(1).best_head_hash.head_hash,
}),
topics: vec![],
},
EventRecord {
phase: Phase::Initialization,
event: TestEvent::Parachains(Event::RejectedObsoleteParachainHead {
parachain: ParaId(1),
parachain_head_hash: initial_best_head(1).best_head_hash.head_hash,
}),
topics: vec![],
}
],
);
}); });
} }
@@ -878,6 +1039,17 @@ mod tests {
next_imported_hash_position: 1, next_imported_hash_position: 1,
}) })
); );
assert_eq!(
System::<TestRuntime>::events(),
vec![EventRecord {
phase: Phase::Initialization,
event: TestEvent::Parachains(Event::UpdatedParachainHead {
parachain: ParaId(1),
parachain_head_hash: head_data(1, 10).hash(),
}),
topics: vec![],
}],
);
// now try to import head#5 at relay block#0 // now try to import head#5 at relay block#0
// => nothing is changed, because better head has already been imported // => nothing is changed, because better head has already been imported
@@ -892,6 +1064,27 @@ mod tests {
next_imported_hash_position: 1, next_imported_hash_position: 1,
}) })
); );
assert_eq!(
System::<TestRuntime>::events(),
vec![
EventRecord {
phase: Phase::Initialization,
event: TestEvent::Parachains(Event::UpdatedParachainHead {
parachain: ParaId(1),
parachain_head_hash: head_data(1, 10).hash(),
}),
topics: vec![],
},
EventRecord {
phase: Phase::Initialization,
event: TestEvent::Parachains(Event::RejectedObsoleteParachainHead {
parachain: ParaId(1),
parachain_head_hash: head_data(1, 5).hash(),
}),
topics: vec![],
}
],
);
}); });
} }
@@ -1047,5 +1240,57 @@ mod tests {
); );
} }
#[test]
fn ignores_parachain_head_if_it_is_missing_from_storage_proof() {
let (state_root, proof, _) = prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]);
let parachains = vec![(ParaId(2), Default::default())];
run_test(|| {
initialize(state_root);
assert_ok!(Pallet::<TestRuntime>::submit_parachain_heads(
Origin::signed(1),
(0, test_relay_header(0, state_root).hash()),
parachains,
proof,
));
assert_eq!(
System::<TestRuntime>::events(),
vec![EventRecord {
phase: Phase::Initialization,
event: TestEvent::Parachains(Event::MissingParachainHead {
parachain: ParaId(2),
}),
topics: vec![],
}],
);
});
}
#[test]
fn ignores_parachain_head_if_parachain_head_hash_is_wrong() {
let (state_root, proof, _) = prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]);
let parachains = vec![(ParaId(1), head_data(1, 10).hash())];
run_test(|| {
initialize(state_root);
assert_ok!(Pallet::<TestRuntime>::submit_parachain_heads(
Origin::signed(1),
(0, test_relay_header(0, state_root).hash()),
parachains,
proof,
));
assert_eq!(
System::<TestRuntime>::events(),
vec![EventRecord {
phase: Phase::Initialization,
event: TestEvent::Parachains(Event::IncorrectParachainHeadHash {
parachain: ParaId(1),
parachain_head_hash: head_data(1, 10).hash(),
actual_parachain_head_hash: head_data(1, 0).hash(),
}),
topics: vec![],
}],
);
});
}
generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted); generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted);
} }
+8 -3
View File
@@ -46,7 +46,7 @@ construct_runtime! {
System: frame_system::{Pallet, Call, Config, Storage, Event<T>}, System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
Grandpa1: pallet_bridge_grandpa::<Instance1>::{Pallet}, Grandpa1: pallet_bridge_grandpa::<Instance1>::{Pallet},
Grandpa2: pallet_bridge_grandpa::<Instance2>::{Pallet}, Grandpa2: pallet_bridge_grandpa::<Instance2>::{Pallet},
Parachains: pallet_bridge_parachains::{Call, Pallet}, Parachains: pallet_bridge_parachains::{Call, Pallet, Event<T>},
} }
} }
@@ -67,7 +67,7 @@ impl frame_system::Config for TestRuntime {
type AccountId = AccountId; type AccountId = AccountId;
type Lookup = IdentityLookup<Self::AccountId>; type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header; type Header = Header;
type Event = (); type Event = Event;
type BlockHashCount = BlockHashCount; type BlockHashCount = BlockHashCount;
type Version = (); type Version = ();
type PalletInfo = PalletInfo; type PalletInfo = PalletInfo;
@@ -112,6 +112,7 @@ parameter_types! {
} }
impl pallet_bridge_parachains::Config for TestRuntime { impl pallet_bridge_parachains::Config for TestRuntime {
type Event = Event;
type WeightInfo = (); type WeightInfo = ();
type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1; type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1;
type ParasPalletName = ParasPalletName; type ParasPalletName = ParasPalletName;
@@ -166,7 +167,11 @@ impl Chain for OtherBridgedChain {
} }
pub fn run_test<T>(test: impl FnOnce() -> T) -> T { pub fn run_test<T>(test: impl FnOnce() -> T) -> T {
sp_io::TestExternalities::new(Default::default()).execute_with(test) sp_io::TestExternalities::new(Default::default()).execute_with(|| {
System::set_block_number(1);
System::reset_events();
test()
})
} }
pub fn test_relay_header( pub fn test_relay_header(