Unverified Commit 6aa5bfba authored by Fedor Sakharov's avatar Fedor Sakharov Committed by GitHub
Browse files

CandidateBackingSubsystem (#1312)

* Updates guide for CandidateBacking

* Move assignment types to primitives

* Initial implementation.

* More functionality

* use assert_matches

* Changes to report misbehaviors

* Some fixes after a review

* Remove a blank line

* Update guide and some types

* Adds run_job function

* Some comments and refactorings

* Fix review

* Remove warnings

* Use summary in kicking off validation

* Parallelize requests

* Validation provides local and global validation params

* Test issued validity tracking

* Nits from review
parent 10928c72
Pipeline #100054 canceled with stages
in 5 minutes and 38 seconds
This diff is collapsed.
......@@ -50,6 +50,7 @@ members = [
"node/overseer",
"node/primitives",
"node/service",
"node/core/backing",
"node/subsystem",
"node/test-helpers/subsystem",
"node/test-service",
......
[package]
name = "polkadot-node-core-backing"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
[dependencies]
futures = "0.3.5"
log = "0.4.8"
sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "master" }
keystore = { package = "sc-keystore", git = "https://github.com/paritytech/substrate", branch = "master" }
primitives = { package = "sp-core", git = "https://github.com/paritytech/substrate", branch = "master" }
polkadot-primitives = { path = "../../../primitives" }
polkadot-node-primitives = { path = "../../primitives" }
polkadot-subsystem = { package = "polkadot-node-subsystem", path = "../../subsystem" }
erasure-coding = { package = "polkadot-erasure-coding", path = "../../../erasure-coding" }
statement-table = { package = "polkadot-statement-table", path = "../../../statement-table" }
futures-timer = "3.0.2"
streamunordered = "0.5.1"
derive_more = "0.99.9"
bitvec = { version = "0.17.4", default-features = false, features = ["alloc"] }
[dev-dependencies]
sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" }
futures = { version = "0.3.5", features = ["thread-pool"] }
subsystem-test = { package = "polkadot-subsystem-test-helpers", path = "../../test-helpers/subsystem" }
assert_matches = "1.3.0"
This diff is collapsed.
......@@ -59,6 +59,7 @@ impl Subsystem1 {
ctx.send_message(AllMessages::CandidateValidation(
CandidateValidationMessage::Validate(
Default::default(),
Default::default(),
Default::default(),
PoVBlock {
......
......@@ -726,6 +726,7 @@ mod tests {
ctx.send_message(
AllMessages::CandidateValidation(
CandidateValidationMessage::Validate(
Default::default(),
Default::default(),
Default::default(),
PoVBlock {
......
......@@ -23,10 +23,17 @@
use parity_scale_codec::{Decode, Encode};
use polkadot_primitives::{Hash,
parachain::{
AbridgedCandidateReceipt, CandidateReceipt, CompactStatement,
EncodeAs, Signed,
AbridgedCandidateReceipt, CompactStatement,
EncodeAs, Signed, SigningContext, ValidatorIndex, ValidatorId,
}
};
use polkadot_statement_table::{
generic::{
ValidityDoubleVote as TableValidityDoubleVote,
MultipleCandidates as TableMultipleCandidates,
},
Misbehavior as TableMisbehavior,
};
/// A statement, where the candidate receipt is included in the `Seconded` variant.
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)]
......@@ -77,11 +84,128 @@ pub enum MisbehaviorReport {
/// this message should be dispatched with all of them, in arbitrary order.
///
/// This variant is also used when our own validity checks disagree with others'.
CandidateValidityDisagreement(CandidateReceipt, Vec<SignedFullStatement>),
CandidateValidityDisagreement(AbridgedCandidateReceipt, Vec<SignedFullStatement>),
/// I've noticed a peer contradicting itself about a particular candidate
SelfContradiction(CandidateReceipt, SignedFullStatement, SignedFullStatement),
SelfContradiction(AbridgedCandidateReceipt, SignedFullStatement, SignedFullStatement),
/// This peer has seconded more than one parachain candidate for this relay parent head
DoubleVote(CandidateReceipt, SignedFullStatement, SignedFullStatement),
DoubleVote(SignedFullStatement, SignedFullStatement),
}
/// A utility struct used to convert `TableMisbehavior` to `MisbehaviorReport`s.
pub struct FromTableMisbehavior {
/// Index of the validator.
pub id: ValidatorIndex,
/// The misbehavior reported by the table.
pub report: TableMisbehavior,
/// Signing context.
pub signing_context: SigningContext,
/// Misbehaving validator's public key.
pub key: ValidatorId,
}
/// Result of the validation of the candidate.
#[derive(Debug)]
pub enum ValidationResult {
/// Candidate is valid.
Valid,
/// Candidate is invalid.
Invalid,
}
impl std::convert::TryFrom<FromTableMisbehavior> for MisbehaviorReport {
type Error = ();
fn try_from(f: FromTableMisbehavior) -> Result<Self, Self::Error> {
match f.report {
TableMisbehavior::ValidityDoubleVote(
TableValidityDoubleVote::IssuedAndValidity((c, s1), (d, s2))
) => {
let receipt = c.clone();
let signed_1 = SignedFullStatement::new(
Statement::Seconded(c),
f.id,
s1,
&f.signing_context,
&f.key,
).ok_or(())?;
let signed_2 = SignedFullStatement::new(
Statement::Valid(d),
f.id,
s2,
&f.signing_context,
&f.key,
).ok_or(())?;
Ok(MisbehaviorReport::SelfContradiction(receipt, signed_1, signed_2))
}
TableMisbehavior::ValidityDoubleVote(
TableValidityDoubleVote::IssuedAndInvalidity((c, s1), (d, s2))
) => {
let receipt = c.clone();
let signed_1 = SignedFullStatement::new(
Statement::Seconded(c),
f.id,
s1,
&f.signing_context,
&f.key,
).ok_or(())?;
let signed_2 = SignedFullStatement::new(
Statement::Invalid(d),
f.id,
s2,
&f.signing_context,
&f.key,
).ok_or(())?;
Ok(MisbehaviorReport::SelfContradiction(receipt, signed_1, signed_2))
}
TableMisbehavior::ValidityDoubleVote(
TableValidityDoubleVote::ValidityAndInvalidity(c, s1, s2)
) => {
let signed_1 = SignedFullStatement::new(
Statement::Valid(c.hash()),
f.id,
s1,
&f.signing_context,
&f.key,
).ok_or(())?;
let signed_2 = SignedFullStatement::new(
Statement::Invalid(c.hash()),
f.id,
s2,
&f.signing_context,
&f.key,
).ok_or(())?;
Ok(MisbehaviorReport::SelfContradiction(c, signed_1, signed_2))
}
TableMisbehavior::MultipleCandidates(
TableMultipleCandidates {
first,
second,
}
) => {
let signed_1 = SignedFullStatement::new(
Statement::Seconded(first.0),
f.id,
first.1,
&f.signing_context,
&f.key,
).ok_or(())?;
let signed_2 = SignedFullStatement::new(
Statement::Seconded(second.0),
f.id,
second.1,
&f.signing_context,
&f.key,
).ok_or(())?;
Ok(MisbehaviorReport::DoubleVote(signed_1, signed_2))
}
_ => Err(()),
}
}
}
/// A unique identifier for a network protocol.
......
......@@ -28,10 +28,11 @@ use polkadot_primitives::{BlockNumber, Hash, Signature};
use polkadot_primitives::parachain::{
AbridgedCandidateReceipt, PoVBlock, ErasureChunk, BackedCandidate, Id as ParaId,
SignedAvailabilityBitfield, SigningContext, ValidatorId, ValidationCode, ValidatorIndex,
CandidateDescriptor,
CoreAssignment, CoreOccupied, HeadData, CandidateDescriptor, GlobalValidationSchedule,
LocalValidationData,
};
use polkadot_node_primitives::{
MisbehaviorReport, SignedFullStatement, View, ProtocolId,
MisbehaviorReport, SignedFullStatement, View, ProtocolId, ValidationResult,
};
use std::sync::Arc;
......@@ -53,12 +54,12 @@ pub enum CandidateSelectionMessage {
/// Messages received by the Candidate Backing subsystem.
#[derive(Debug)]
pub enum CandidateBackingMessage {
/// Registers a stream listener for updates to the set of backable candidates that could be backed
/// in a child of the given relay-parent, referenced by its hash.
RegisterBackingWatcher(Hash, mpsc::Sender<NewBackedCandidate>),
/// Requests a set of backable candidates that could be backed in a child of the given
/// relay-parent, referenced by its hash.
GetBackedCandidates(Hash, oneshot::Sender<Vec<NewBackedCandidate>>),
/// Note that the Candidate Backing subsystem should second the given candidate in the context of the
/// given relay-parent (ref. by hash). This candidate must be validated.
Second(Hash, AbridgedCandidateReceipt),
Second(Hash, AbridgedCandidateReceipt, PoVBlock),
/// Note a validator's statement about a particular candidate. Disagreements about validity must be escalated
/// to a broader check by Misbehavior Arbitration. Agreements are simply tallied until a quorum is reached.
Statement(Hash, SignedFullStatement),
......@@ -78,8 +79,12 @@ pub enum CandidateValidationMessage {
Validate(
Hash,
AbridgedCandidateReceipt,
HeadData,
PoVBlock,
oneshot::Sender<Result<(), ValidationFailed>>,
oneshot::Sender<Result<
(ValidationResult, GlobalValidationSchedule, LocalValidationData),
ValidationFailed,
>>,
),
}
......@@ -151,17 +156,34 @@ pub enum AvailabilityStoreMessage {
StoreChunk(Hash, ValidatorIndex, ErasureChunk),
}
/// The information on scheduler assignments that some somesystems may be querying.
#[derive(Debug, Clone)]
pub struct SchedulerRoster {
/// Validator-to-groups assignments.
pub validator_groups: Vec<Vec<ValidatorIndex>>,
/// All scheduled paras.
pub scheduled: Vec<CoreAssignment>,
/// Upcoming paras (chains and threads).
pub upcoming: Vec<ParaId>,
/// Occupied cores.
pub availability_cores: Vec<Option<CoreOccupied>>,
}
/// A request to the Runtime API subsystem.
#[derive(Debug)]
pub enum RuntimeApiRequest {
/// Get the current validator set.
Validators(oneshot::Sender<Vec<ValidatorId>>),
/// Get the assignments of validators to cores.
ValidatorGroups(oneshot::Sender<SchedulerRoster>),
/// Get a signing context for bitfields and statements.
SigningContext(oneshot::Sender<SigningContext>),
/// Get the validation code for a specific para, assuming execution under given block number, and
/// an optional block number representing an intermediate parablock executed in the context of
/// that block.
ValidationCode(ParaId, BlockNumber, Option<BlockNumber>, oneshot::Sender<ValidationCode>),
/// Get head data for a specific para.
HeadData(ParaId, oneshot::Sender<HeadData>),
}
/// A message to the Runtime API subsystem.
......
......@@ -171,6 +171,100 @@ pub struct DutyRoster {
pub validator_duty: Vec<Chain>,
}
/// The unique (during session) index of a core.
#[derive(Encode, Decode, Default, PartialOrd, Ord, Eq, PartialEq, Clone, Copy)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct CoreIndex(pub u32);
impl From<u32> for CoreIndex {
fn from(i: u32) -> CoreIndex {
CoreIndex(i)
}
}
/// The unique (during session) index of a validator group.
#[derive(Encode, Decode, Default, Clone, Copy)]
#[cfg_attr(feature = "std", derive(Eq, Hash, PartialEq, Debug))]
pub struct GroupIndex(pub u32);
impl From<u32> for GroupIndex {
fn from(i: u32) -> GroupIndex {
GroupIndex(i)
}
}
/// A claim on authoring the next block for a given parathread.
#[derive(Clone, Encode, Decode, Default)]
#[cfg_attr(feature = "std", derive(PartialEq, Debug))]
pub struct ParathreadClaim(pub Id, pub CollatorId);
/// An entry tracking a claim to ensure it does not pass the maximum number of retries.
#[derive(Clone, Encode, Decode, Default)]
#[cfg_attr(feature = "std", derive(PartialEq, Debug))]
pub struct ParathreadEntry {
/// The claim.
pub claim: ParathreadClaim,
/// Number of retries.
pub retries: u32,
}
/// What is occupying a specific availability core.
#[derive(Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(PartialEq, Debug))]
pub enum CoreOccupied {
/// A parathread.
Parathread(ParathreadEntry),
/// A parachain.
Parachain,
}
/// The assignment type.
#[derive(Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(PartialEq, Debug))]
pub enum AssignmentKind {
/// A parachain.
Parachain,
/// A parathread.
Parathread(CollatorId, u32),
}
/// How a free core is scheduled to be assigned.
#[derive(Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(PartialEq, Debug))]
pub struct CoreAssignment {
/// The core that is assigned.
pub core: CoreIndex,
/// The unique ID of the para that is assigned to the core.
pub para_id: Id,
/// The kind of the assignment.
pub kind: AssignmentKind,
/// The index of the validator group assigned to the core.
pub group_idx: GroupIndex,
}
impl CoreAssignment {
/// Get the ID of a collator who is required to collate this block.
pub fn required_collator(&self) -> Option<&CollatorId> {
match self.kind {
AssignmentKind::Parachain => None,
AssignmentKind::Parathread(ref id, _) => Some(id),
}
}
/// Get the `CoreOccupied` from this.
pub fn to_core_occupied(&self) -> CoreOccupied {
match self.kind {
AssignmentKind::Parachain => CoreOccupied::Parachain,
AssignmentKind::Parathread(ref collator, retries) => CoreOccupied::Parathread(
ParathreadEntry {
claim: ParathreadClaim(self.para_id, collator.clone()),
retries,
}
),
}
}
}
/// Extra data that is needed along with the other fields in a `CandidateReceipt`
/// to fully validate the candidate.
///
......@@ -382,6 +476,15 @@ pub struct AbridgedCandidateReceipt<H = Hash> {
pub commitments: CandidateCommitments<H>,
}
/// A candidate-receipt with commitments directly included.
pub struct CommitedCandidateReceipt<H = Hash> {
/// The descriptor of the candidae.
pub descriptor: CandidateDescriptor,
/// The commitments of the candidate receipt.
pub commitments: CandidateCommitments<H>
}
impl<H: AsRef<[u8]> + Encode> AbridgedCandidateReceipt<H> {
/// Check integrity vs. provided block data.
pub fn check_signature(&self) -> Result<(), ()> {
......@@ -473,7 +576,6 @@ impl AbridgedCandidateReceipt {
}
}
impl PartialOrd for AbridgedCandidateReceipt {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
......@@ -923,6 +1025,27 @@ impl<Payload: EncodeAs<RealPayload>, RealPayload: Encode> Signed<Payload, RealPa
out
}
/// Used to create a `Signed` from already existing parts.
#[cfg(feature = "std")]
pub fn new<H: Encode>(
payload: Payload,
validator_index: ValidatorIndex,
signature: ValidatorSignature,
context: &SigningContext<H>,
key: &ValidatorId,
) -> Option<Self> {
let s = Self {
payload,
validator_index,
signature,
real_payload: std::marker::PhantomData,
};
s.check_signature(context, key).ok()?;
Some(s)
}
/// Sign this payload with the given context and key, storing the validator index.
#[cfg(feature = "std")]
pub fn sign<H: Encode>(
......
......@@ -2,30 +2,43 @@
The Candidate Backing subsystem ensures every parablock considered for relay block inclusion has been seconded by at least one validator, and approved by a quorum. Parablocks for which no validator will assert correctness are discarded. If the block later proves invalid, the initial backers are slashable; this gives polkadot a rational threat model during subsequent stages.
Its role is to produce backable candidates for inclusion in new relay-chain blocks. It does so by issuing signed [`Statement`s](../../types/backing.md#statement-type) and tracking received statements signed by other validators. Once enough statements are received, they can be combined into backing for specific candidates.
Its role is to produce backable candidates for inclusion in new relay-chain blocks. It does so by issuing signed [`Statement`s][Statement] and tracking received statements signed by other validators. Once enough statements are received, they can be combined into backing for specific candidates.
Note that though the candidate backing subsystem attempts to produce as many backable candidates as possible, it does _not_ attempt to choose a single authoritative one. The choice of which actually gets included is ultimately up to the block author, by whatever metrics it may use; those are opaque to this subsystem.
Once a sufficient quorum has agreed that a candidate is valid, this subsystem notifies the [Provisioner](../utility/provisioner.md), which in turn engages block production mechanisms to include the parablock.
Once a sufficient quorum has agreed that a candidate is valid, this subsystem notifies the [Provisioner][PV], which in turn engages block production mechanisms to include the parablock.
## Protocol
The [Candidate Selection subsystem](candidate-selection.md) is the primary source of non-overseer messages into this subsystem. That subsystem generates appropriate [`CandidateBackingMessage`s](../../types/overseer-protocol.md#candidate-backing-message), and passes them to this subsystem.
Input: [`CandidateBackingMessage`][CBM]
This subsystem validates the candidates and generates an appropriate [`SignedStatement`](../../types/backing.md#signed-statement-type). All `SignedStatement`s are then passed on to the [Statement Distribution subsystem](statement-distribution.md) to be gossiped to peers. All [Proofs of Validity](../../types/availability.md#proof-of-validity) should be distributed via the [PoV Distribution](pov-distribution.md) subsystem. When this subsystem decides that a candidate is invalid, and it was recommended to us to second by our own Candidate Selection subsystem, a message is sent to the Candidate Selection subsystem with the candidate's hash so that the collator which recommended it can be penalized.
Output:
- [`CandidateValidationMessage`][CVM]
- [`RuntimeApiMessage`][RAM]
- [`CandidateSelectionMessage`][CSM]
- [`ProvisionerMessage`][PM]
- [`PoVDistributionMessage`][PDM]
- [`StatementDistributionMessage`][SDM]
## Functionality
The [Candidate Selection][CS] subsystem is the primary source of non-overseer messages into this subsystem. That subsystem generates appropriate [`CandidateBackingMessage`s][CBM] and passes them to this subsystem.
This subsystem requests validation from the [Candidate Validation][CV] and generates an appropriate [`Statement`][Statement]. All `Statement`s are then passed on to the [Statement Distribution][SD] subsystem to be gossiped to peers. When [Candidate Validation][CV] decides that a candidate is invalid, and it was recommended to us to second by our own [Candidate Selection][CS] subsystem, a message is sent to the [Candidate Selection][CS] subsystem with the candidate's hash so that the collator which recommended it can be penalized.
The subsystem should maintain a set of handles to Candidate Backing Jobs that are currently live, as well as the relay-parent to which they correspond.
### On Overseer Signal
* If the signal is an [`OverseerSignal`](../../types/overseer-protocol.md#overseer-signal)`::StartWork(relay_parent)`, spawn a Candidate Backing Job with the given relay parent, storing a bidirectional channel with the Candidate Backing Job in the set of handles.
* If the signal is an [`OverseerSignal`](../../types/overseer-protocol.md#overseer-signal)`::StopWork(relay_parent)`, cease the Candidate Backing Job under that relay parent, if any.
* If the signal is an [`OverseerSignal`][OverseerSignal]`::StartWork(relay_parent)`, spawn a Candidate Backing Job with the given relay parent, storing a bidirectional channel with the Candidate Backing Job in the set of handles.
* If the signal is an [`OverseerSignal`][OverseerSignal]`::StopWork(relay_parent)`, cease the Candidate Backing Job under that relay parent, if any.
### On `CandidateBackingMessage`
### On Receiving `CandidateBackingMessage`
* If the message corresponds to a particular relay-parent, forward the message to the Candidate Backing Job for that relay-parent, if any is live.
* If the message is a [`CandidateBackingMessage`][CBM]`::GetBackedCandidates`, get all backable candidates from the statement table and send them back.
* If the message is a [`CandidateBackingMessage`][CBM]`::Second`, sign and dispatch a `Seconded` statement only if we have not seconded any other candidate and have not signed a `Valid` statement for the requested candidate. Signing both a `Seconded` and `Valid` message is a double-voting misbehavior with a heavy penalty, and this could occur if another validator has seconded the same candidate and we've received their message before the internal seconding request.
* If the message is a [`CandidateBackingMessage`][CBM]`::Statement`, count the statement to the quorum. If the statement in the message is `Seconded` and it contains a candidate that belongs to our assignment, request the corresponding `PoV` from the `PoVDistribution` and launch validation. Issue our own `Valid` or `Invalid` statement as a result.
> big TODO: "contextual execution"
>
......@@ -39,36 +52,49 @@ The subsystem should maintain a set of handles to Candidate Backing Jobs that ar
The Candidate Backing Job represents the work a node does for backing candidates with respect to a particular relay-parent.
The goal of a Candidate Backing Job is to produce as many backable candidates as possible. This is done via signed [`Statement`s](../../types/backing.md#statement-type) by validators. If a candidate receives a majority of supporting Statements from the Parachain Validators currently assigned, then that candidate is considered backable.
The goal of a Candidate Backing Job is to produce as many backable candidates as possible. This is done via signed [`Statement`s][STMT] by validators. If a candidate receives a majority of supporting Statements from the Parachain Validators currently assigned, then that candidate is considered backable.
### On Startup
* Fetch current validator set, validator -> parachain assignments from runtime API.
* Fetch current validator set, validator -> parachain assignments from [`Runtime API`][RA] subsystem using [`RuntimeApiRequest::Validators`][RAM] and [`RuntimeApiRequest::ValidatorGroups`][RAM]
* Determine if the node controls a key in the current validator set. Call this the local key if so.
* If the local key exists, extract the parachain head and validation function for the parachain the local key is assigned to.
* If the local key exists, extract the parachain head and validation function from the [`Runtime API`][RA] for the parachain the local key is assigned to by issuing a [`RuntimeApiRequest::Validators`][RAM]
* Issue a [`RuntimeApiRequest::SigningContext`][RAM] message to get a context that will later be used upon signing.
### On Receiving New Signed Statement
### On Receiving New Candidate Backing Message
```rust
if let Statement::Seconded(candidate) = signed.statement {
if candidate is unknown and in local assignment {
spawn_validation_work(candidate, parachain head, validation function)
match msg {
CetBackedCandidates(hash, tx) => {
// Send back a set of backable candidates.
}
CandidateBackingMessage::Second(hash, candidate) => {
if candidate is unknown and in local assignment {
spawn_validation_work(candidate, parachain head, validation function)
}
}
CandidateBackingMessage::Statement(hash, statement) => {
// count to the votes on this candidate
if let Statement::Seconded(candidate) = statement {
if candidate.parachain_id == our_assignment {
spawn_validation_work(candidate, parachain head, validation function)
}
}
}
}
// add `Seconded` statements and `Valid` statements to a quorum. If quorum reaches validator-group
// majority, send a `BlockAuthorshipProvisioning::BackableCandidate(relay_parent, Candidate, Backing)` message.
```
### Spawning Validation Work
Add `Seconded` statements and `Valid` statements to a quorum. If quorum reaches validator-group majority, send a [`ProvisionerMessage`][PM]`::ProvisionableData(ProvisionableData::BackedCandidate(BackedCandidate))` message.
`Invalid` statements that conflict with already witnessed `Seconded` and `Valid` statements for the given candidate, statements that are double-votes, self-contradictions and so on, should result in issuing a [`ProvisionerMessage`][PM]`::MisbehaviorReport` message for each newly detected case of this kind.
### Validating Candidates.
```rust
fn spawn_validation_work(candidate, parachain head, validation function) {
asynchronously {
let pov = (fetch pov block).await
// dispatched to sub-process (OS process) pool.
let valid = validate_candidate(candidate, validation function, parachain head, pov).await;
let valid = (validate pov block).await;
if valid {
// make PoV available for later distribution. Send data to the availability store to keep.
// sign and dispatch `valid` statement to network if we have not seconded the given candidate.
......@@ -82,11 +108,30 @@ fn spawn_validation_work(candidate, parachain head, validation function) {
### Fetch Pov Block
Create a `(sender, receiver)` pair.
Dispatch a `PovFetchSubsystemMessage(relay_parent, candidate_hash, sender)` and listen on the receiver for a response.
Dispatch a [`PoVDistributionMessage`][PDM]`::FecthPoV(relay_parent, candidate_hash, sender)` and listen on the receiver for a response.
### On Receiving `CandidateBackingMessage`
* If the message is a `CandidateBackingMessage::RegisterBackingWatcher`, register the watcher and trigger it each time a new candidate is backable. Also trigger it once initially if there are any backable candidates at the time of receipt.
* If the message is a `CandidateBackingMessage::Second`, sign and dispatch a `Seconded` statement only if we have not seconded any other candidate and have not signed a `Valid` statement for the requested candidate. Signing both a `Seconded` and `Valid` message is a double-voting misbehavior with a heavy penalty, and this could occur if another validator has seconded the same candidate and we've received their message before the internal seconding request.
### Validate PoV Block
> TODO: send statements to Statement Distribution subsystem, handle shutdown signal from candidate backing subsystem
Create a `(sender, receiver)` pair.
Dispatch a `CandidateValidationMessage::Validate(validation function, candidate, pov, sender)` and listen on the receiver for a response.
### Distribute Signed Statemnet
Dispatch a [`StatementDistributionMessage`][PDM]`::Share(relay_parent, SignedFullStatement)`.
[OverseerSignal]: ../../types/overseer-protocol.md#overseer-signal
[Statement]: ../../types/backing.md#statement-type
[STMT]: ../../types/backing.md#statement-type
[CSM]: ../../types/overseer-protocol.md#candidate-selection-message
[RAM]: ../../types/overseer-protocol.md#runtime-api-message
[CVM]: ../../types/overseer-protocol.md#validation-request-type
[PM]: ../../types/overseer-protocol.md#provisioner-message
[CBM]: ../../types/overseer-protocol.md#candidate-backing-message
[PDM]: ../../types/overseer-protocol.md#pov-distribution-message
[SDM]: ../../types/overseer-protocol.md#statement-distribution-message
[CS]: candidate-selection.md
[CV]: ../utility/candidate-validation.md
[SD]: statement-distribution.md
[RA]: ../utility/runtime-api.md
[PV]: ../utility/provisioner.md
......@@ -86,9 +86,9 @@ enum BitfieldSigningMessage { }
```rust