mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 18:41:03 +00:00
pre-checking: Reject failed PVFs (#6492)
* pre-checking: Reject failed PVFs * paras: immediately reject any PVF that cannot reach a supermajority * Make the `quorum` reject condition a bit more clear semantically * Add comment * Update implementer's guide * Update a link Not related to the rest of the PR, but I randomly noticed and fixed this. * Update runtime/parachains/src/paras/tests.rs Co-authored-by: s0me0ne-unkn0wn <48632512+s0me0ne-unkn0wn@users.noreply.github.com> * Remove unneeded loop * Log PVF retries using `info!` * Change retry logs to `warn!` and add preparation failure log * Log PVF execution failure * Clarify why we reject failed PVFs * Fix PVF reject runtime benchmarks Co-authored-by: s0me0ne-unkn0wn <48632512+s0me0ne-unkn0wn@users.noreply.github.com>
This commit is contained in:
@@ -170,7 +170,8 @@ where
|
||||
}
|
||||
|
||||
/// Generates a list of votes combined with signatures for the active validator set. The number of
|
||||
/// votes is equal to the minimum number of votes required to reach the supermajority.
|
||||
/// votes is equal to the minimum number of votes required to reach the threshold for either accept
|
||||
/// or reject.
|
||||
fn generate_statements<T>(
|
||||
vote_outcome: VoteOutcome,
|
||||
) -> impl Iterator<Item = (PvfCheckStatement, ValidatorSignature)>
|
||||
@@ -179,7 +180,11 @@ where
|
||||
{
|
||||
let validators = ParasShared::<T>::active_validator_keys();
|
||||
|
||||
let required_votes = primitives::supermajority_threshold(validators.len());
|
||||
let accept_threshold = primitives::supermajority_threshold(validators.len());
|
||||
let required_votes = match vote_outcome {
|
||||
VoteOutcome::Accept => accept_threshold,
|
||||
VoteOutcome::Reject => validators.len() - accept_threshold,
|
||||
};
|
||||
(0..required_votes).map(move |validator_index| {
|
||||
let stmt = PvfCheckStatement {
|
||||
accept: vote_outcome == VoteOutcome::Accept,
|
||||
|
||||
@@ -73,13 +73,14 @@
|
||||
//!
|
||||
//! # PVF Pre-checking
|
||||
//!
|
||||
//! As was mentioned above, a brand new validation code should go through a process of approval.
|
||||
//! As part of this process, validators from the active set will take the validation code and
|
||||
//! check if it is malicious. Once they did that and have their judgement, either accept or reject,
|
||||
//! they issue a statement in a form of an unsigned extrinsic. This extrinsic is processed by this
|
||||
//! pallet. Once supermajority is gained for accept, then the process that initiated the check
|
||||
//! is resumed (as mentioned before this can be either upgrading of validation code or onboarding).
|
||||
//! If supermajority is gained for reject, then the process is canceled.
|
||||
//! As was mentioned above, a brand new validation code should go through a process of approval. As
|
||||
//! part of this process, validators from the active set will take the validation code and check if
|
||||
//! it is malicious. Once they did that and have their judgement, either accept or reject, they
|
||||
//! issue a statement in a form of an unsigned extrinsic. This extrinsic is processed by this
|
||||
//! pallet. Once supermajority is gained for accept, then the process that initiated the check is
|
||||
//! resumed (as mentioned before this can be either upgrading of validation code or onboarding). If
|
||||
//! getting a supermajority becomes impossible (>1/3 of validators have already voted against), then
|
||||
//! we reject.
|
||||
//!
|
||||
//! Below is a state diagram that depicts states of a single PVF pre-checking vote.
|
||||
//!
|
||||
@@ -92,8 +93,8 @@
|
||||
//! │ │ │
|
||||
//! │ ┌───────┐
|
||||
//! │ │ │
|
||||
//! └─▶│ init │────supermajority ┌──────────┐
|
||||
//! │ │ against │ │
|
||||
//! └─▶│ init │──── >1/3 against ┌──────────┐
|
||||
//! │ │ │ │ │
|
||||
//! └───────┘ └──────────▶│ rejected │
|
||||
//! ▲ │ │ │
|
||||
//! │ │ session └──────────┘
|
||||
@@ -452,12 +453,14 @@ impl<BlockNumber> PvfCheckActiveVoteState<BlockNumber> {
|
||||
|
||||
/// Returns `None` if the quorum is not reached, or the direction of the decision.
|
||||
fn quorum(&self, n_validators: usize) -> Option<PvfCheckOutcome> {
|
||||
let q_threshold = primitives::supermajority_threshold(n_validators);
|
||||
// NOTE: counting the reject votes is deliberately placed first. This is to err on the safe.
|
||||
if self.votes_reject.count_ones() >= q_threshold {
|
||||
Some(PvfCheckOutcome::Rejected)
|
||||
} else if self.votes_accept.count_ones() >= q_threshold {
|
||||
let accept_threshold = primitives::supermajority_threshold(n_validators);
|
||||
// At this threshold, a supermajority is no longer possible, so we reject.
|
||||
let reject_threshold = n_validators - accept_threshold;
|
||||
|
||||
if self.votes_accept.count_ones() >= accept_threshold {
|
||||
Some(PvfCheckOutcome::Accepted)
|
||||
} else if self.votes_reject.count_ones() > reject_threshold {
|
||||
Some(PvfCheckOutcome::Rejected)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -1011,7 +1014,7 @@ pub mod pallet {
|
||||
}
|
||||
|
||||
if let Some(outcome) = active_vote.quorum(validators.len()) {
|
||||
// The supermajority quorum has been achieved.
|
||||
// The quorum has been achieved.
|
||||
//
|
||||
// Remove the PVF vote from the active map and finalize the PVF checking according
|
||||
// to the outcome.
|
||||
|
||||
@@ -1249,15 +1249,24 @@ fn pvf_check_upgrade_reject() {
|
||||
Paras::schedule_code_upgrade(a, new_code.clone(), RELAY_PARENT, &Configuration::config());
|
||||
check_code_is_stored(&new_code);
|
||||
|
||||
// Supermajority of validators vote against `new_code`. PVF should be rejected.
|
||||
IntoIterator::into_iter([0, 1, 2, 3])
|
||||
.map(|i| PvfCheckStatement {
|
||||
accept: false,
|
||||
subject: new_code.hash(),
|
||||
session_index: EXPECTED_SESSION,
|
||||
validator_index: i.into(),
|
||||
})
|
||||
.for_each(sign_and_include_pvf_check_statement);
|
||||
// 1/3 of validators vote against `new_code`. PVF should not be rejected yet.
|
||||
sign_and_include_pvf_check_statement(PvfCheckStatement {
|
||||
accept: false,
|
||||
subject: new_code.hash(),
|
||||
session_index: EXPECTED_SESSION,
|
||||
validator_index: 0.into(),
|
||||
});
|
||||
|
||||
// Verify that the new code is not yet discarded.
|
||||
check_code_is_stored(&new_code);
|
||||
|
||||
// >1/3 of validators vote against `new_code`. PVF should be rejected.
|
||||
sign_and_include_pvf_check_statement(PvfCheckStatement {
|
||||
accept: false,
|
||||
subject: new_code.hash(),
|
||||
session_index: EXPECTED_SESSION,
|
||||
validator_index: 1.into(),
|
||||
});
|
||||
|
||||
// Verify that the new code is discarded.
|
||||
check_code_is_not_stored(&new_code);
|
||||
|
||||
Reference in New Issue
Block a user