From 21419a81d01229750dcb3995dd142dc901b0a28e Mon Sep 17 00:00:00 2001 From: Sergei Shulepov <sergei@parity.io> Date: Thu, 13 Jan 2022 18:44:14 +0100 Subject: [PATCH] paras: Add runtime events for PVF pre-checking (#4683) In this PR, paras module emit runtime events on certain PVF pre-checking related conditions. Specifically, there are 3 new events in the paras module: 1. PvfCheckStarted 2. PvfCheckAccepted 3. PvfCheckRejected All of those have identifiers for the parachain that triggered the PVF pre-checking and the validation code that goes through the pre-checking. The mechanics of those are as follows. Each time a new PVF is added, be it due to onboarding or upgrading, the `PvfCheckStarted` will be triggered. If another parachain triggers a pre-checking process for the validation code which is already being pre-checked, another `PvfCheckStarted` event will be triggered with the corresponding para id. When the PVF pre-checking voting for a PVF was finished, several `PvfCheckAccepted/Rejected` events will be triggered: one for each para id that was subscribed to this check (i.e. was a "cause" for it). If the PVF pre-checking is disabled, then one can still expect these events to be fired. Since insta PVF approval is syncronous, the `PvfCheckStarted` will be followed by the `PvfCheckAccepted` with the same validation code and para id. If somebody is interested in following validation code changes for a PVF of a parachain, they would need to subscribe to those events. I did not supply the topics for the events, since I am not sure if that's needed or will be used, but they can be added later if needed. --- polkadot/runtime/parachains/src/paras/mod.rs | 28 +++++++++ .../runtime/parachains/src/paras/tests.rs | 60 +++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index 656b4a466c2..f10c642a6dc 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -314,6 +314,16 @@ enum PvfCheckCause<BlockNumber> { }, } +impl<BlockNumber> PvfCheckCause<BlockNumber> { + /// Returns the ID of the para that initiated or subscribed to the pre-checking vote. + fn para_id(&self) -> ParaId { + match *self { + PvfCheckCause::Onboarding(id) => id, + PvfCheckCause::Upgrade { id, .. } => id, + } + } +} + /// Specifies what was the outcome of a PVF pre-checking vote. #[derive(Copy, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] enum PvfCheckOutcome { @@ -467,6 +477,15 @@ pub mod pallet { NewHeadNoted(ParaId), /// A para has been queued to execute pending actions. `para_id` ActionQueued(ParaId, SessionIndex), + /// The given para either initiated or subscribed to a PVF check for the given validation + /// code. `code_hash` `para_id` + PvfCheckStarted(ValidationCodeHash, ParaId), + /// The given validation code was rejected by the PVF pre-checking vote. + /// `code_hash` `para_id` + PvfCheckAccepted(ValidationCodeHash, ParaId), + /// The given validation code was accepted by the PVF pre-checking vote. + /// `code_hash` `para_id` + PvfCheckRejected(ValidationCodeHash, ParaId), } #[pallet::error] @@ -1345,6 +1364,9 @@ impl<T: Config> Pallet<T> { ) -> Weight { let mut weight = 0; for cause in causes { + weight += T::DbWeight::get().reads_writes(3, 2); + Self::deposit_event(Event::PvfCheckAccepted(*code_hash, cause.para_id())); + match cause { PvfCheckCause::Onboarding(id) => { weight += Self::proceed_with_onboarding(*id, sessions_observed); @@ -1432,6 +1454,9 @@ impl<T: Config> Pallet<T> { // Now we need to unbump it. weight += Self::decrease_code_ref(code_hash); + weight += T::DbWeight::get().reads_writes(3, 2); + Self::deposit_event(Event::PvfCheckRejected(*code_hash, cause.para_id())); + match cause { PvfCheckCause::Onboarding(id) => { // Here we need to undo everything that was done during `schedule_para_initialize`. @@ -1713,6 +1738,9 @@ impl<T: Config> Pallet<T> { ) -> Weight { let mut weight = 0; + weight += T::DbWeight::get().reads_writes(3, 2); + Self::deposit_event(Event::PvfCheckStarted(code_hash, cause.para_id())); + weight += T::DbWeight::get().reads(1); match PvfActiveVoteMap::<T>::get(&code_hash) { None => { diff --git a/polkadot/runtime/parachains/src/paras/tests.rs b/polkadot/runtime/parachains/src/paras/tests.rs index 483bf63154c..e7972c067e3 100644 --- a/polkadot/runtime/parachains/src/paras/tests.rs +++ b/polkadot/runtime/parachains/src/paras/tests.rs @@ -113,6 +113,49 @@ fn check_code_is_not_stored(validation_code: &ValidationCode) { assert!(!<Paras as Store>::CodeByHash::contains_key(validation_code.hash())); } +/// An utility for checking that certain events were deposited. +struct EventValidator { + events: + Vec<frame_system::EventRecord<<Test as frame_system::Config>::Event, primitives::v1::Hash>>, +} + +impl EventValidator { + fn new() -> Self { + Self { events: Vec::new() } + } + + fn started(&mut self, code: &ValidationCode, id: ParaId) -> &mut Self { + self.events.push(frame_system::EventRecord { + phase: frame_system::Phase::Initialization, + event: Event::PvfCheckStarted(code.hash(), id).into(), + topics: vec![], + }); + self + } + + fn rejected(&mut self, code: &ValidationCode, id: ParaId) -> &mut Self { + self.events.push(frame_system::EventRecord { + phase: frame_system::Phase::Initialization, + event: Event::PvfCheckRejected(code.hash(), id).into(), + topics: vec![], + }); + self + } + + fn accepted(&mut self, code: &ValidationCode, id: ParaId) -> &mut Self { + self.events.push(frame_system::EventRecord { + phase: frame_system::Phase::Initialization, + event: Event::PvfCheckAccepted(code.hash(), id).into(), + topics: vec![], + }); + self + } + + fn check(&self) { + assert_eq!(&frame_system::Pallet::<Test>::events(), &self.events); + } +} + #[test] fn para_past_code_pruning_works_correctly() { let mut past_code = ParaPastCodeMeta::default(); @@ -1048,6 +1091,14 @@ fn pvf_check_coalescing_onboarding_and_upgrade() { <Paras as Store>::FutureCodeUpgrades::get(&a), Some(RELAY_PARENT + validation_upgrade_delay), ); + + // Verify that the required events were emitted. + EventValidator::new() + .started(&validation_code, b) + .started(&validation_code, a) + .accepted(&validation_code, b) + .accepted(&validation_code, a) + .check(); }); } @@ -1157,6 +1208,9 @@ fn pvf_check_upgrade_reject() { assert!(<Paras as Store>::PvfActiveVoteMap::get(&new_code.hash()).is_none()); assert!(Paras::pvfs_require_precheck().is_empty()); assert!(<Paras as Store>::FutureCodeHash::get(&a).is_none()); + + // Verify that the required events were emitted. + EventValidator::new().started(&new_code, a).rejected(&new_code, a).check(); }); } @@ -1398,6 +1452,12 @@ fn add_trusted_validation_code_insta_approval() { <Paras as Store>::FutureCodeUpgrades::get(¶_id), Some(1 + validation_upgrade_delay) ); + + // Verify that the required events were emitted. + EventValidator::new() + .started(&validation_code, para_id) + .accepted(&validation_code, para_id) + .check(); }); } -- GitLab