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(&para_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