Dispute Coordinator Subsystem (#3150)

* skeleton for dispute-coordinator

* add coordinator and participation message types

* begin dispute-coordinator DB

* functions for loading

* implement strongly-typed DB transaction

* add some tests for DB transaction

* core logic for pruning

* guide: update candidate-votes key for coordinator

* update candidate-votes key

* use big-endian encoding for session, and implement upper bound generator

* finish implementing pruning

* add a test for note_current_session

* define state of the subsystem itself

* barebones subsystem definition

* control flow

* more control flow

* implement session-updating logic

* trace

* control flow for message handling

* Update node/core/dispute-coordinator/src/lib.rs

Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>

* Update node/subsystem/src/messages.rs

Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>

* some more control flow

* guide: remove overlay

* more control flow

* implement some DB getters

* make progress on importing statements

* add SignedDisputeStatement struct

* move ApprovalVote to shared primitives

* add a signing-payload API to explicit dispute statements

* add signing-payload to CompactStatement

* add relay-parent hash to seconded/valid dispute variatns

* correct import

* type-safe wrapper around dispute statements

* use checked dispute statement in message type

* extract rolling session window cache to subsystem-util

* extract session window tests

* approval-voting: use rolling session info cache

* reduce dispute window to match runtime in practice

* add byzantine_threshold and supermajority_threshold utilities to primitives

* integrate rolling session window

* Add PartialOrd to CandidateHash

* add Ord to CandidateHash

* implement active dispute update

* add dispute messages to AllMessages

* add dispute stubs to overseer

* inform dispute participation to participate

* implement issue_local_statement

* implement `determine_undisputed_chain`

* fix warnings

* test harness for dispute coordinator tests

* add more helpers to test harness

* add some more helpers

* some tests for dispute coordinator

* ignore wrong validator indices

* test finality voting rule constraint

* add more tests

* add variants to network bridge

* fix test compilation

* remove most dispute coordinator functionality

as of #3222 we can do most of the work within the approval voting subsystem

* Revert "remove most dispute coordinator functionality"

This reverts commit 9cd615e8eb6ca0b382cbaff525d813e753d6004e.

* Use thiserror

Co-authored-by: Bernhard Schuster <bernhard@ahoi.io>

* Update node/core/dispute-coordinator/src/lib.rs

Co-authored-by: Bernhard Schuster <bernhard@ahoi.io>

* extract tests to separate module

* address nit

* adjust run_iteration API

Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>
Co-authored-by: Bernhard Schuster <bernhard@ahoi.io>
This commit is contained in:
Robert Habermeier
2021-06-13 13:35:18 +02:00
committed by GitHub
parent 7f344df160
commit 5bc2b2779d
22 changed files with 3070 additions and 594 deletions
+8
View File
@@ -672,6 +672,14 @@ pub enum CompactStatement {
Valid(CandidateHash),
}
impl CompactStatement {
/// Yields the payload used for validator signatures on this kind
/// of statement.
pub fn signing_payload(&self, context: &SigningContext) -> Vec<u8> {
(self, context).encode()
}
}
// Inner helper for codec on `CompactStatement`.
#[derive(Encode, Decode)]
enum CompactStatementInner {
+119 -3
View File
@@ -842,6 +842,22 @@ pub struct SessionInfo {
pub needed_approvals: u32,
}
/// A vote of approval on a candidate.
#[derive(Clone, RuntimeDebug)]
pub struct ApprovalVote(pub CandidateHash);
impl ApprovalVote {
/// Yields the signing payload for this approval vote.
pub fn signing_payload(
&self,
session_index: SessionIndex,
) -> Vec<u8> {
const MAGIC: [u8; 4] = *b"APPR";
(MAGIC, &self.0, session_index).encode()
}
}
sp_api::decl_runtime_apis! {
/// The API for querying the state of parachains on-chain.
pub trait ParachainHost<H: Decode = Hash, N: Encode + Decode = BlockNumber> {
@@ -1064,6 +1080,60 @@ pub enum DisputeStatement {
Invalid(InvalidDisputeStatementKind),
}
impl DisputeStatement {
/// Get the payload data for this type of dispute statement.
pub fn payload_data(&self, candidate_hash: CandidateHash, session: SessionIndex) -> Vec<u8> {
match *self {
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit) => {
ExplicitDisputeStatement {
valid: true,
candidate_hash,
session,
}.signing_payload()
},
DisputeStatement::Valid(ValidDisputeStatementKind::BackingSeconded(inclusion_parent)) => {
CompactStatement::Seconded(candidate_hash).signing_payload(&SigningContext {
session_index: session,
parent_hash: inclusion_parent,
})
},
DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(inclusion_parent)) => {
CompactStatement::Valid(candidate_hash).signing_payload(&SigningContext {
session_index: session,
parent_hash: inclusion_parent,
})
},
DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalChecking) => {
ApprovalVote(candidate_hash).signing_payload(session)
},
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit) => {
ExplicitDisputeStatement {
valid: false,
candidate_hash,
session,
}.signing_payload()
},
}
}
/// Check the signature on a dispute statement.
pub fn check_signature(
&self,
validator_public: &ValidatorId,
candidate_hash: CandidateHash,
session: SessionIndex,
validator_signature: &ValidatorSignature,
) -> Result<(), ()> {
let payload = self.payload_data(candidate_hash, session);
if validator_signature.verify(&payload[..] , &validator_public) {
Ok(())
} else {
Err(())
}
}
}
/// Different kinds of statements of validity on a candidate.
#[derive(Encode, Decode, Clone, PartialEq, RuntimeDebug)]
pub enum ValidDisputeStatementKind {
@@ -1072,10 +1142,10 @@ pub enum ValidDisputeStatementKind {
Explicit,
/// A seconded statement on a candidate from the backing phase.
#[codec(index = 1)]
BackingSeconded,
BackingSeconded(Hash),
/// A valid statement on a candidate from the backing phase.
#[codec(index = 2)]
BackingValid,
BackingValid(Hash),
/// An approval vote from the approval checking phase.
#[codec(index = 3)]
ApprovalChecking,
@@ -1090,7 +1160,7 @@ pub enum InvalidDisputeStatementKind {
}
/// An explicit statement on a candidate issued as part of a dispute.
#[derive(Encode, Decode, Clone, PartialEq, RuntimeDebug)]
#[derive(Clone, PartialEq, RuntimeDebug)]
pub struct ExplicitDisputeStatement {
/// Whether the candidate is valid
pub valid: bool,
@@ -1100,6 +1170,15 @@ pub struct ExplicitDisputeStatement {
pub session: SessionIndex,
}
impl ExplicitDisputeStatement {
/// Produce the payload used for signing this type of statement.
pub fn signing_payload(&self) -> Vec<u8> {
const MAGIC: [u8; 4] = *b"DISP";
(MAGIC, self.valid, self.candidate_hash, self.session).encode()
}
}
/// A set of statements about a specific candidate.
#[derive(Encode, Decode, Clone, PartialEq, RuntimeDebug)]
pub struct DisputeStatementSet {
@@ -1140,6 +1219,19 @@ pub struct InherentData<HDR: HeaderT = Header> {
pub parent_header: HDR,
}
/// The maximum number of validators `f` which may safely be faulty.
///
/// The total number of validators is `n = 3f + e` where `e in { 1, 2, 3 }`.
pub fn byzantine_threshold(n: usize) -> usize {
n.saturating_sub(1) / 3
}
/// The supermajority threshold of validators which represents a subset
/// guaranteed to have at least f+1 honest validators.
pub fn supermajority_threshold(n: usize) -> usize {
n - byzantine_threshold(n)
}
#[cfg(test)]
mod tests {
use super::*;
@@ -1190,4 +1282,28 @@ mod tests {
&Hash::repeat_byte(4).into(),
);
}
#[test]
fn test_byzantine_threshold() {
assert_eq!(byzantine_threshold(0), 0);
assert_eq!(byzantine_threshold(1), 0);
assert_eq!(byzantine_threshold(2), 0);
assert_eq!(byzantine_threshold(3), 0);
assert_eq!(byzantine_threshold(4), 1);
assert_eq!(byzantine_threshold(5), 1);
assert_eq!(byzantine_threshold(6), 1);
assert_eq!(byzantine_threshold(7), 2);
}
#[test]
fn test_supermajority_threshold() {
assert_eq!(supermajority_threshold(0), 0);
assert_eq!(supermajority_threshold(1), 1);
assert_eq!(supermajority_threshold(2), 2);
assert_eq!(supermajority_threshold(3), 3);
assert_eq!(supermajority_threshold(4), 3);
assert_eq!(supermajority_threshold(5), 4);
assert_eq!(supermajority_threshold(6), 5);
assert_eq!(supermajority_threshold(7), 5);
}
}