Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit 4a2d498

Browse files
pepyakinslumber
andauthored
pvf-precheck: Candidate Validation Changes (#4409)
* pvf-precheck: Candidate Validation Changes Co-authored-by: Chris Sosnin <[email protected]> * Rename `MockValidationBackend` and specialize it * Add pre-check tests Co-authored-by: Chris Sosnin <[email protected]>
1 parent 0043fe5 commit 4a2d498

File tree

4 files changed

+324
-18
lines changed

4 files changed

+324
-18
lines changed

node/core/candidate-validation/src/lib.rs

Lines changed: 107 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,16 @@
2424
#![warn(missing_docs)]
2525

2626
use polkadot_node_core_pvf::{
27-
InvalidCandidate as WasmInvalidCandidate, Pvf, ValidationError, ValidationHost,
27+
InvalidCandidate as WasmInvalidCandidate, PrepareError, Pvf, ValidationError, ValidationHost,
2828
};
2929
use polkadot_node_primitives::{
3030
BlockData, InvalidCandidate, PoV, ValidationResult, POV_BOMB_LIMIT, VALIDATION_CODE_BOMB_LIMIT,
3131
};
3232
use polkadot_node_subsystem::{
3333
errors::RuntimeApiError,
3434
messages::{
35-
CandidateValidationMessage, RuntimeApiMessage, RuntimeApiRequest, ValidationFailed,
35+
CandidateValidationMessage, PreCheckOutcome, RuntimeApiMessage, RuntimeApiRequest,
36+
ValidationFailed,
3637
},
3738
overseer, FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemContext, SubsystemError,
3839
SubsystemResult, SubsystemSender,
@@ -194,6 +195,30 @@ where
194195

195196
ctx.spawn("validate-from-exhaustive", bg.boxed())?;
196197
},
198+
CandidateValidationMessage::PreCheck(
199+
relay_parent,
200+
validation_code_hash,
201+
response_sender,
202+
) => {
203+
let bg = {
204+
let mut sender = ctx.sender().clone();
205+
let validation_host = validation_host.clone();
206+
207+
async move {
208+
let precheck_result = precheck_pvf(
209+
&mut sender,
210+
validation_host,
211+
relay_parent,
212+
validation_code_hash,
213+
)
214+
.await;
215+
216+
let _ = response_sender.send(precheck_result);
217+
}
218+
};
219+
220+
ctx.spawn("candidate-validation-pre-check", bg.boxed())?;
221+
},
197222
},
198223
}
199224
}
@@ -235,6 +260,73 @@ where
235260
})
236261
}
237262

263+
async fn request_validation_code_by_hash<Sender>(
264+
sender: &mut Sender,
265+
relay_parent: Hash,
266+
validation_code_hash: ValidationCodeHash,
267+
) -> Result<Option<ValidationCode>, RuntimeRequestFailed>
268+
where
269+
Sender: SubsystemSender,
270+
{
271+
let (tx, rx) = oneshot::channel();
272+
runtime_api_request(
273+
sender,
274+
relay_parent,
275+
RuntimeApiRequest::ValidationCodeByHash(validation_code_hash, tx),
276+
rx,
277+
)
278+
.await
279+
}
280+
281+
async fn precheck_pvf<Sender>(
282+
sender: &mut Sender,
283+
mut validation_backend: impl ValidationBackend,
284+
relay_parent: Hash,
285+
validation_code_hash: ValidationCodeHash,
286+
) -> PreCheckOutcome
287+
where
288+
Sender: SubsystemSender,
289+
{
290+
let validation_code =
291+
match request_validation_code_by_hash(sender, relay_parent, validation_code_hash).await {
292+
Ok(Some(code)) => code,
293+
_ => {
294+
// The reasoning why this is "failed" and not invalid is because we assume that
295+
// during pre-checking voting the relay-chain will pin the code. In case the code
296+
// actually is not there, we issue failed since this looks more like a bug. This
297+
// leads to us abstaining.
298+
tracing::warn!(
299+
target: LOG_TARGET,
300+
?relay_parent,
301+
?validation_code_hash,
302+
"precheck: requested validation code is not found on-chain!",
303+
);
304+
return PreCheckOutcome::Failed
305+
},
306+
};
307+
308+
let validation_code = match sp_maybe_compressed_blob::decompress(
309+
&validation_code.0,
310+
VALIDATION_CODE_BOMB_LIMIT,
311+
) {
312+
Ok(code) => Pvf::from_code(code.into_owned()),
313+
Err(e) => {
314+
tracing::debug!(target: LOG_TARGET, err=?e, "precheck: cannot decompress validation code");
315+
return PreCheckOutcome::Invalid
316+
},
317+
};
318+
319+
match validation_backend.precheck_pvf(validation_code).await {
320+
Ok(_) => PreCheckOutcome::Valid,
321+
Err(prepare_err) => match prepare_err {
322+
PrepareError::Prevalidation(_) |
323+
PrepareError::Preparation(_) |
324+
PrepareError::Panic(_) => PreCheckOutcome::Invalid,
325+
PrepareError::TimedOut | PrepareError::DidNotMakeIt => PreCheckOutcome::Failed,
326+
},
327+
}
328+
}
329+
238330
#[derive(Debug)]
239331
enum AssumptionCheckOutcome {
240332
Matches(PersistedValidationData, ValidationCode),
@@ -487,6 +579,8 @@ trait ValidationBackend {
487579
timeout: Duration,
488580
params: ValidationParams,
489581
) -> Result<WasmValidationResult, ValidationError>;
582+
583+
async fn precheck_pvf(&mut self, pvf: Pvf) -> Result<(), PrepareError>;
490584
}
491585

492586
#[async_trait]
@@ -520,6 +614,17 @@ impl ValidationBackend for ValidationHost {
520614

521615
validation_result
522616
}
617+
618+
async fn precheck_pvf(&mut self, pvf: Pvf) -> Result<(), PrepareError> {
619+
let (tx, rx) = oneshot::channel();
620+
if let Err(_) = self.precheck_pvf(pvf, tx).await {
621+
return Err(PrepareError::DidNotMakeIt)
622+
}
623+
624+
let precheck_result = rx.await.or(Err(PrepareError::DidNotMakeIt))?;
625+
626+
precheck_result
627+
}
523628
}
524629

525630
/// Does basic checks of a candidate. Provide the encoded PoV-block. Returns `Ok` if basic checks

0 commit comments

Comments
 (0)