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:
Marcin S
2023-01-12 04:24:42 -05:00
committed by GitHub
parent ae74d33a93
commit 2efa3bab98
11 changed files with 126 additions and 74 deletions
+7 -5
View File
@@ -190,16 +190,18 @@ async fn handle_pvf_check(
PreCheckOutcome::Valid => Judgement::Valid,
PreCheckOutcome::Invalid => Judgement::Invalid,
PreCheckOutcome::Failed => {
// Abstain.
// Always vote against in case of failures. Voting against a PVF when encountering a
// timeout (or an unlikely node-specific issue) can be considered safe, since
// there is no slashing for being on the wrong side on a pre-check vote.
//
// Returning here will leave the PVF in the view dangling. Since it is there, no new
// pre-checking request will be sent.
// Also, by being more strict here, we can safely be more lenient during preparation and
// avoid the risk of getting slashed there.
gum::info!(
target: LOG_TARGET,
?validation_code_hash,
"Pre-check failed, abstaining from voting",
"Pre-check failed, voting against",
);
return
Judgement::Invalid
},
};
+16 -9
View File
@@ -157,7 +157,7 @@ impl TestState {
self.active_leaves_update(handle, Some(leaf), None, &[]).await
}
async fn deactive_leaves(
async fn deactivate_leaves(
&mut self,
handle: &mut VirtualOverseer,
deactivated: impl IntoIterator<Item = &Hash>,
@@ -894,8 +894,8 @@ fn unexpected_pvf_check_judgement() {
// Catch the pre-check request, but don't reply just yet.
let pre_check = test_state.expect_candidate_precheck(&mut handle).await;
// Now deactive the leaf and reply to the precheck request.
test_state.deactive_leaves(&mut handle, &[block_1.block_hash]).await;
// Now deactivate the leaf and reply to the precheck request.
test_state.deactivate_leaves(&mut handle, &[block_1.block_hash]).await;
pre_check.reply(PreCheckOutcome::Invalid);
// the subsystem must remain silent.
@@ -906,14 +906,17 @@ fn unexpected_pvf_check_judgement() {
});
}
// Check that we do not abstain for a nondeterministic failure. Currently, this means the behavior
// is the same as if the pre-check returned `PreCheckOutcome::Invalid`.
#[test]
fn abstain_for_nondeterministic_pvfcheck_failure() {
fn dont_abstain_for_nondeterministic_pvfcheck_failure() {
test_harness(|mut test_state, mut handle| {
async move {
let block_1 = FakeLeaf::new(dummy_hash(), 1, vec![dummy_validation_code_hash(1)]);
test_state
.activate_leaf_with_session(
&mut handle,
FakeLeaf::new(dummy_hash(), 1, vec![dummy_validation_code_hash(1)]),
block_1.clone(),
StartsNewSession { session_index: 2, validators: vec![OUR_VALIDATOR] },
)
.await;
@@ -922,10 +925,14 @@ fn abstain_for_nondeterministic_pvfcheck_failure() {
test_state.expect_session_for_child(&mut handle).await;
test_state.expect_validators(&mut handle).await;
test_state
.expect_candidate_precheck(&mut handle)
.await
.reply(PreCheckOutcome::Failed);
// Catch the pre-check request, but don't reply just yet.
let pre_check = test_state.expect_candidate_precheck(&mut handle).await;
// Now deactivate the leaf and reply to the precheck request.
test_state.deactivate_leaves(&mut handle, &[block_1.block_hash]).await;
pre_check.reply(PreCheckOutcome::Failed);
// the subsystem must remain silent.
test_state.send_conclude(&mut handle).await;
}