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

Commit

Permalink
pvf-precheck: PVF pre-checker subsystem
Browse files Browse the repository at this point in the history
This commit implements the last major piece of #3211: the subsystem that
tracks PVFs that require voting, issues pre-check requests to
candidate-validation and makes sure that the votes are submitted to the
chain.

TODO:

- [ ] Run once more with polkadot-launch
  • Loading branch information
pepyakin committed Dec 30, 2021
1 parent d5818b2 commit ef957aa
Show file tree
Hide file tree
Showing 16 changed files with 1,765 additions and 6 deletions.
24 changes: 24 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 @@ -61,6 +61,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
28 changes: 28 additions & 0 deletions node/core/pvf-checker/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[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" }
polkadot-overseer = { path = "../../overseer" }

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

[dev-dependencies]
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" }
polkadot-node-subsystem-test-helpers = { path = "../../subsystem-test-helpers"}
test-helpers = { package = "polkadot-primitives-test-helpers", path = "../../../primitives/test-helpers" }
sp-application-crypto = { git = "https://github.com/paritytech/substrate", branch = "master" }
futures-timer = "3.0.2"
124 changes: 124 additions & 0 deletions node/core/pvf-checker/src/interest_view.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// 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,
};

/// Data about a particular validation code.
#[derive(Default, Debug)]
struct PvfData {
/// If `Some` then the PVF pre-checking was run for this PVF. If `None` we are either waiting
/// for the judgement to come in or the PVF pre-checking failed.
judgement: Option<bool>,

/// The set of block hashes where this PVF was seen.
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 leafs 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()
}
}

/// A structure that keeps track of relevant PVFs and judgements about them. A relevant PVF is one
/// that resides in at least a single active leaf.
#[derive(Debug)]
pub struct InterestView {
active_leaves: BTreeMap<Hash, HashSet<ValidationCodeHash>>,
pvfs: BTreeMap<ValidationCodeHash, PvfData>,
}

impl InterestView {
pub fn new() -> Self {
Self { active_leaves: 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();

if let Some((leaf, pending_pvfs)) = activated {
for pvf in &pending_pvfs {
match self.pvfs.entry(*pvf) {
btree_map::Entry::Vacant(v) => {
v.insert(PvfData::pending(leaf));
newcomers.push(*pvf);
},
btree_map::Entry::Occupied(mut o) => {
o.get_mut().seen_in(leaf);
},
}
}
self.active_leaves.entry(leaf).or_default().extend(pending_pvfs);
}

for leaf in deactivated {
let pvfs = self.active_leaves.remove(leaf);
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(leaf);
if now_empty {
o.remove();
}
}
}
}

newcomers
}

/// Handles a new judgement for the given `pvf`.
///
/// 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(()),
}
}

/// Returns all PVFs that previously received a judgement.
pub fn judgements(&self) -> impl Iterator<Item = (ValidationCodeHash, bool)> + '_ {
self.pvfs
.iter()
.filter_map(|(code_hash, data)| data.judgement.map(|accept| (*code_hash, accept)))
}
}
Loading

0 comments on commit ef957aa

Please sign in to comment.