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

PVF prechecking #4209

Closed
wants to merge 60 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
f271c09
pvf host: store only compiled artifacts on disk
slumber Sep 22, 2021
554b8a0
Correctly handle failed artifacts
slumber Sep 23, 2021
8bdc7d3
Serialize result of PVF preparation uniquely
slumber Sep 23, 2021
7c0b8ed
Set the artifact state depending on the result
slumber Sep 27, 2021
11de207
Return the result of PVF preparation directly
slumber Sep 27, 2021
d323ac6
Move PrepareError to the error module
slumber Sep 27, 2021
f129cd6
Update doc comments
slumber Sep 27, 2021
cae0540
Update misleading comment
slumber Sep 27, 2021
5c12abb
Commit everything so far
pepyakin Sep 29, 2021
e5e905e
.
pepyakin Sep 29, 2021
574dc40
first revision
pepyakin Oct 1, 2021
9eff8d5
work
pepyakin Oct 2, 2021
c81cdc6
Stuff
pepyakin Oct 3, 2021
67e0395
fix reference counting for rejecting
pepyakin Oct 3, 2021
b8487ed
fixes
pepyakin Oct 4, 2021
a6863ce
rename
pepyakin Oct 4, 2021
d387231
paragenesisargs
pepyakin Oct 4, 2021
33127f4
prepare to pivot
pepyakin Oct 5, 2021
d3f51aa
Restore ParaPastCodeMeta
pepyakin Oct 5, 2021
ce67024
Restore stealing mechanism
pepyakin Oct 5, 2021
ad4fe78
Fix test compilation
pepyakin Oct 5, 2021
efdb781
Fix tests
pepyakin Oct 11, 2021
81bb85d
Update rustdoc for paras
pepyakin Oct 12, 2021
dbe031e
get back validation code length check
pepyakin Oct 12, 2021
6481e26
Merge master
pepyakin Oct 13, 2021
b73f244
small test refactor
pepyakin Oct 13, 2021
a7e9890
General clean up
pepyakin Oct 14, 2021
8af822f
Merge master
pepyakin Oct 14, 2021
0f61c09
Runtime API
pepyakin Oct 14, 2021
8805d5c
Client side implementation
pepyakin Oct 21, 2021
e84d310
pvf host: turn off parallel compilation
slumber Oct 21, 2021
78fad55
pvf host: implement precheck requests
slumber Oct 21, 2021
7e25dc0
Fix warnings
slumber Oct 21, 2021
c90f64f
Unnecessary clone
slumber Oct 22, 2021
57255fa
Add a note about timed out outcome
slumber Oct 22, 2021
2f05716
Revert the pool outcome handling behavior
slumber Oct 22, 2021
66f05c5
Move the prepare result type into error mod
slumber Oct 22, 2021
3cca95a
Merge master
pepyakin Oct 22, 2021
8f51e94
Test prepare done
slumber Oct 22, 2021
b17eb79
Merge remote-tracking branch 'origin/master' into slumber-pvf-host-pr…
slumber Oct 22, 2021
d38e164
fmt
slumber Oct 22, 2021
a6c6f1a
Add an explanation to wasmtime config
slumber Nov 1, 2021
907c4a5
Split pvf host test
slumber Nov 1, 2021
84e1481
Merge remote-tracking branch 'origin/pep-pvfcheck-runtime' into slumb…
slumber Nov 2, 2021
e7001e7
Handle precheck request in candidate validation subsystem
slumber Nov 3, 2021
2751871
Complete runtime impls
slumber Nov 3, 2021
31af008
Merge remote-tracking branch 'origin/master' into slumber-pvf-prechec…
slumber Nov 3, 2021
49cf80c
Integrate node changes
slumber Nov 16, 2021
47e52ba
Expand the debug log
pepyakin Nov 16, 2021
74a1b25
0.9.13 is the current upcoming version
pepyakin Nov 17, 2021
cc09b8f
Pass the outcome
pepyakin Nov 17, 2021
c272f04
Merge master
pepyakin Nov 17, 2021
9f8fe1c
Make paras module backwards compatible with pre-go-ahead runtimes
pepyakin Nov 18, 2021
31b95fd
Fix tests for the backwards compatible approach
pepyakin Nov 18, 2021
7c15bf2
Use non-empty validation code
pepyakin Nov 18, 2021
f49cbf3
fmt
pepyakin Nov 18, 2021
892e7fa
Doc changes
pepyakin Nov 18, 2021
53f4d8c
Drive by comment
pepyakin Nov 23, 2021
7e4ba34
merge master
pepyakin Nov 23, 2021
92d620d
Fix
pepyakin Nov 23, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ members = [
"node/core/parachains-inherent",
"node/core/provisioner",
"node/core/pvf",
"node/core/pvf-checker",
"node/core/runtime-api",
"node/network/approval-distribution",
"node/network/bridge",
Expand Down
100 changes: 98 additions & 2 deletions node/core/candidate-validation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,16 @@
#![warn(missing_docs)]

use polkadot_node_core_pvf::{
InvalidCandidate as WasmInvalidCandidate, Pvf, ValidationError, ValidationHost,
InvalidCandidate as WasmInvalidCandidate, PrepareError, Pvf, ValidationError, ValidationHost,
};
use polkadot_node_primitives::{
BlockData, InvalidCandidate, PoV, ValidationResult, POV_BOMB_LIMIT, VALIDATION_CODE_BOMB_LIMIT,
};
use polkadot_node_subsystem::{
errors::RuntimeApiError,
messages::{
CandidateValidationMessage, RuntimeApiMessage, RuntimeApiRequest, ValidationFailed,
CandidateValidationMessage, PreCheckOutcome, RuntimeApiMessage, RuntimeApiRequest,
ValidationFailed,
},
overseer, FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemContext, SubsystemError,
SubsystemResult, SubsystemSender,
Expand Down Expand Up @@ -194,6 +195,30 @@ where

ctx.spawn("validate-from-exhaustive", bg.boxed())?;
},
CandidateValidationMessage::PreCheck(
relay_parent,
validation_code_hash,
response_sender,
) => {
let bg = {
let mut sender = ctx.sender().clone();
let validation_host = validation_host.clone();

async move {
let precheck_result = precheck_pvf(
&mut sender,
validation_host,
relay_parent,
validation_code_hash,
)
.await;

let _ = response_sender.send(precheck_result);
}
};

ctx.spawn("candidate-validation-pre-check", bg.boxed())?;
},
},
}
}
Expand Down Expand Up @@ -235,6 +260,64 @@ where
})
}

async fn request_validation_code_by_hash<Sender>(
sender: &mut Sender,
relay_parent: Hash,
validation_code_hash: ValidationCodeHash,
) -> Result<Option<ValidationCode>, RuntimeRequestFailed>
where
Sender: SubsystemSender,
{
let (tx, rx) = oneshot::channel();
runtime_api_request(
sender,
relay_parent,
RuntimeApiRequest::ValidationCodeByHash(validation_code_hash, tx),
rx,
)
.await
}

async fn precheck_pvf<Sender>(
sender: &mut Sender,
mut validation_backend: impl ValidationBackend,
relay_parent: Hash,
validation_code_hash: ValidationCodeHash,
) -> PreCheckOutcome
where
Sender: SubsystemSender,
{
let validation_code =
match request_validation_code_by_hash(sender, relay_parent, validation_code_hash).await {
Ok(Some(code)) => code,
_ => return PreCheckOutcome::Failed,
};

let raw_validation_code = match sp_maybe_compressed_blob::decompress(
&validation_code.0,
VALIDATION_CODE_BOMB_LIMIT,
) {
Ok(code) => code,
Err(e) => {
tracing::debug!(target: LOG_TARGET, err=?e, "precheck: cannot decompress validation code");
return PreCheckOutcome::Invalid
},
};

match validation_backend
.precheck_pvf(Pvf::from_code(raw_validation_code.to_vec()))
.await
{
Ok(_) => PreCheckOutcome::Valid,
Err(prepare_err) => match prepare_err {
PrepareError::Prevalidation(_) |
PrepareError::Preparation(_) |
PrepareError::Panic(_) => PreCheckOutcome::Invalid,
PrepareError::TimedOut | PrepareError::DidNotMakeIt => PreCheckOutcome::Failed,
},
}
}

#[derive(Debug)]
enum AssumptionCheckOutcome {
Matches(PersistedValidationData, ValidationCode),
Expand Down Expand Up @@ -485,6 +568,8 @@ trait ValidationBackend {
timeout: Duration,
params: ValidationParams,
) -> Result<WasmValidationResult, ValidationError>;

async fn precheck_pvf(&mut self, pvf: Pvf) -> Result<(), PrepareError>;
}

#[async_trait]
Expand Down Expand Up @@ -518,6 +603,17 @@ impl ValidationBackend for ValidationHost {

validation_result
}

async fn precheck_pvf(&mut self, pvf: Pvf) -> Result<(), PrepareError> {
let (tx, rx) = oneshot::channel();
if let Err(_) = self.precheck_pvf(pvf, tx).await {
return Err(PrepareError::DidNotMakeIt)
}

let precheck_result = rx.await.or(Err(PrepareError::DidNotMakeIt))?;

precheck_result
}
}

/// Does basic checks of a candidate. Provide the encoded PoV-block. Returns `Ok` if basic checks
Expand Down
5 changes: 5 additions & 0 deletions node/core/candidate-validation/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use super::*;
use assert_matches::assert_matches;
use futures::executor;
use polkadot_node_core_pvf::PrepareError;
use polkadot_node_subsystem::messages::AllMessages;
use polkadot_node_subsystem_test_helpers as test_helpers;
use polkadot_node_subsystem_util::reexports::SubsystemContext;
Expand Down Expand Up @@ -346,6 +347,10 @@ impl ValidationBackend for MockValidatorBackend {
) -> Result<WasmValidationResult, ValidationError> {
self.result.clone()
}

async fn precheck_pvf(&mut self, pvf: Pvf) -> Result<(), PrepareError> {
todo!()
}
}

#[test]
Expand Down
24 changes: 24 additions & 0 deletions node/core/pvf-checker/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "polkadot-node-core-pvf-checker"
version = "0.9.13"
authors = ["Parity Technologies <[email protected]>"]
edition = "2018"

[dependencies]
futures = "0.3.17"
thiserror = "1.0.30"
tracing = "0.1.29"

polkadot-node-primitives = { path = "../../primitives" }
polkadot-node-subsystem = { path = "../../subsystem" }
polkadot-primitives = { path = "../../../primitives" }
polkadot-node-subsystem-util = { path = "../../subsystem-util" }

sp-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" }

# TODO:
#[dev-dependencies]
#assert_matches = "1.5.0"
#parity-scale-codec = "2.3.1"
#polkadot-node-subsystem-test-helpers = { path = "../../subsystem-test-helpers"}
#sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
116 changes: 116 additions & 0 deletions node/core/pvf-checker/src/interest_view.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.

// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.

use polkadot_primitives::v1::{Hash, ValidationCodeHash};
use std::collections::{
btree_map::{self, BTreeMap},
HashSet,
};

#[derive(Default)]
struct PvfData {
judgement: Option<bool>,
seen_in: HashSet<Hash>,
}

impl PvfData {
/// Initialize a new `PvfData` which is awaiting for the initial judgement.
fn pending(origin: Hash) -> Self {
// Preallocate the hashset with 5 items. This is the anticipated maximum heads we can
// deal at the same time. In the vast majority of the cases it will have length of 1.
let mut seen_in = HashSet::with_capacity(5);
seen_in.insert(origin);
Self { judgement: None, seen_in }
}

pub fn seen_in(&mut self, relay_hash: Hash) {
self.seen_in.insert(relay_hash);
}

/// Removes the given `relay_hash` from the set of seen in, and returns if the set is now empty.
pub fn remove_origin(&mut self, relay_hash: &Hash) -> bool {
self.seen_in.remove(relay_hash);
self.seen_in.is_empty()
}
}

pub struct InterestView {
heads: BTreeMap<Hash, HashSet<ValidationCodeHash>>,
pvfs: BTreeMap<ValidationCodeHash, PvfData>,
}

impl InterestView {
pub fn new() -> Self {
Self { heads: BTreeMap::new(), pvfs: BTreeMap::new() }
}

pub fn on_leaves_update(
&mut self,
activated: Option<(Hash, Vec<ValidationCodeHash>)>,
deactivated: &[Hash],
) -> Vec<ValidationCodeHash> {
let mut newcomers = Vec::new();

// TODO: it's important to run this first.
for (head, pending_pvfs) in activated {
for pvf in &pending_pvfs {
match self.pvfs.entry(*pvf) {
btree_map::Entry::Vacant(v) => {
v.insert(PvfData::pending(head));
newcomers.push(*pvf);
},
btree_map::Entry::Occupied(mut o) => {
o.get_mut().seen_in(head);
},
}
}
self.heads.entry(head).or_default().extend(pending_pvfs);
}

for head in deactivated {
let pvfs = self.heads.remove(&head);
for pvf in pvfs.into_iter().flatten() {
if let btree_map::Entry::Occupied(mut o) = self.pvfs.entry(pvf) {
let now_empty = o.get_mut().remove_origin(&head);
if now_empty {
o.remove();
}
}
}
}

newcomers
}

/// TODO:
/// Returns `Err` if the given PVF hash is not known.
pub fn on_judgement(&mut self, subject: ValidationCodeHash, accept: bool) -> Result<(), ()> {
match self.pvfs.get_mut(&subject) {
Some(data) => {
data.judgement = Some(accept);
Ok(())
},
None => Err(()),
}
}

pub fn judgements(&self) -> impl Iterator<Item = (ValidationCodeHash, bool)> + '_ {
self.pvfs.iter().filter_map(|(code_hash, data)| match data.judgement {
Some(accept) => Some((*code_hash, accept)),
None => None,
})
}
}
Loading