mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 19:21:13 +00:00
candidate statement importing
This commit is contained in:
Generated
+8
@@ -612,6 +612,14 @@ dependencies = [
|
||||
"polkadot-cli 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polkadot-candidate-agreement"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"polkadot-primitives 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polkadot-cli"
|
||||
version = "0.1.0"
|
||||
|
||||
@@ -9,6 +9,7 @@ polkadot-cli = { path = "cli", version = "0.1" }
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"candidate-agreement",
|
||||
"client",
|
||||
"contracts",
|
||||
"primitives",
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "polkadot-candidate-agreement"
|
||||
version = "0.1.0"
|
||||
authors = ["Robert Habermeier <rphmeier@gmail.com>"]
|
||||
|
||||
[dependencies]
|
||||
futures = "0.1"
|
||||
polkadot-primitives = { path = "../primitives" }
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2017 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/>.
|
||||
|
||||
//! Propagation and agreement of candidates.
|
||||
//!
|
||||
//! This is parameterized by 3 numbers:
|
||||
//! N: the number of validators total
|
||||
//! P: the number of parachains
|
||||
//! F: the number of faulty nodes (s.t. 3F + 1 <= N)
|
||||
//! We also define G as the number of validators per parachain (N/P)
|
||||
//!
|
||||
//! Validators are split into groups by parachain, and each validator might come
|
||||
//! up its own candidate for their parachain. Within groups, validators pass around
|
||||
//! their candidates and produce statements of validity.
|
||||
//!
|
||||
//! Any candidate that receives majority approval by the validators in a group
|
||||
//! may be subject to inclusion.
|
||||
|
||||
extern crate futures;
|
||||
extern crate polkadot_primitives as primitives;
|
||||
|
||||
use primitives::parachain;
|
||||
|
||||
mod table;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn it_works() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
// Copyright 2017 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/>.
|
||||
|
||||
//! The statement table.
|
||||
//!
|
||||
//! This stores messages other validators issue about candidates.
|
||||
//!
|
||||
//! These messages are used to create a proposal submitted to a BFT consensus process.
|
||||
//!
|
||||
//! Proposals can either be sets of candidates for inclusion or a special value indicating
|
||||
//! that some unspecified misbehavior has occurred.
|
||||
//!
|
||||
//! Each parachain is associated with two sets of validators: those which can
|
||||
//! propose and attest to validity of candidates, and those who can only attest
|
||||
//! to availability.
|
||||
|
||||
use std::collections::{HashSet, HashMap};
|
||||
use std::hash::Hash;
|
||||
|
||||
/// Statements circulated among peers.
|
||||
pub enum Statement<C: Context + ?Sized> {
|
||||
/// Broadcast by a validator to indicate that this is his candidate for
|
||||
/// inclusion.
|
||||
///
|
||||
/// Broadcasting two different candidate messages per round is not allowed.
|
||||
Candidate(C::Candidate),
|
||||
/// Broadcast by a validator to attest that the candidate with given digest
|
||||
/// is valid.
|
||||
Valid(C::Digest),
|
||||
/// Broadcast by a validator to attest that the auxiliary data for a candidate
|
||||
/// with given digest is available.
|
||||
Available(C::Digest),
|
||||
/// Broadcast by a validator to attest that the candidate with given digest
|
||||
/// is invalid.
|
||||
Invalid(C::Digest),
|
||||
}
|
||||
|
||||
/// A signed statement.
|
||||
pub struct SignedStatement<C: Context + ?Sized> {
|
||||
/// The statement.
|
||||
pub statement: Statement<C>,
|
||||
/// The signature.
|
||||
pub signature: C::Signature,
|
||||
}
|
||||
|
||||
/// Context for the statement table.
|
||||
pub trait Context {
|
||||
/// A validator ID
|
||||
type ValidatorId: Hash + Eq + Clone;
|
||||
/// The digest (hash or other unique attribute) of a candidate.
|
||||
type Digest: Hash + Eq + Clone;
|
||||
/// Candidate type.
|
||||
type Candidate: Ord + Clone;
|
||||
/// The group ID type
|
||||
type GroupId: Hash + Eq + Clone;
|
||||
/// A signature type.
|
||||
type Signature: Clone;
|
||||
|
||||
/// get the digest of a candidate.
|
||||
fn candidate_digest(&self, candidate: &Self::Candidate) -> Self::Digest;
|
||||
|
||||
/// get the group of a candidate.
|
||||
fn candidate_group(&self, candidate: &Self::Candidate) -> Self::GroupId;
|
||||
|
||||
/// Whether a validator is a member of a group.
|
||||
/// Members are meant to submit candidates and vote on validity.
|
||||
fn is_member_of(&self, validator: &Self::ValidatorId, group: &Self::GroupId) -> bool;
|
||||
|
||||
/// Whether a validator is an availability guarantor of a group.
|
||||
/// Guarantors are meant to vote on availability for candidates submitted
|
||||
/// in a group.
|
||||
fn is_availability_guarantor_of(
|
||||
&self,
|
||||
validator: &Self::ValidatorId,
|
||||
group: &Self::GroupId,
|
||||
) -> bool;
|
||||
|
||||
// recover signer of statement.
|
||||
fn statement_signer(
|
||||
&self,
|
||||
statement: &SignedStatement<Self>,
|
||||
) -> Option<Self::ValidatorId>;
|
||||
|
||||
// sign a statement with the local key.
|
||||
fn sign_statement(
|
||||
&self,
|
||||
statement: Statement<Self>,
|
||||
) -> SignedStatement<Self>;
|
||||
}
|
||||
|
||||
/// Misbehavior: voting both ways on candidate validity.
|
||||
pub struct ValidityDoubleVote<C: Context> {
|
||||
/// The candidate digest
|
||||
pub digest: C::Digest,
|
||||
/// The signature on the true vote.
|
||||
pub t_signature: C::Signature,
|
||||
/// The signature on the false vote.
|
||||
pub f_signature: C::Signature,
|
||||
}
|
||||
|
||||
/// Misbehavior: declaring multiple candidates.
|
||||
pub struct MultipleCandidates<C: Context> {
|
||||
/// The first candidate seen.
|
||||
pub first: (C::Candidate, C::Signature),
|
||||
/// The second candidate seen.
|
||||
pub second: (C::Candidate, C::Signature),
|
||||
}
|
||||
|
||||
/// Misbehavior: submitted statement for wrong group.
|
||||
pub struct UnauthorizedStatement<C: Context> {
|
||||
/// A signed statement which was submitted without proper authority.
|
||||
pub statement: SignedStatement<C>,
|
||||
}
|
||||
|
||||
/// Different kinds of misbehavior. All of these kinds of malicious misbehavior
|
||||
/// are easily provable and extremely disincentivized.
|
||||
pub enum Misbehavior<C: Context> {
|
||||
/// Voted invalid and valid on validity.
|
||||
ValidityDoubleVote(ValidityDoubleVote<C>),
|
||||
/// Submitted multiple candidates.
|
||||
MultipleCandidates(MultipleCandidates<C>),
|
||||
/// Submitted a message withou
|
||||
UnauthorizedStatement(UnauthorizedStatement<C>),
|
||||
}
|
||||
|
||||
// Votes on a specific candidate.
|
||||
struct CandidateData<C: Context> {
|
||||
group_id: C::GroupId,
|
||||
candidate: C::Candidate,
|
||||
validity_votes: HashMap<C::ValidatorId, (bool, C::Signature)>,
|
||||
indicated_bad_by: Vec<C::ValidatorId>,
|
||||
}
|
||||
|
||||
/// Stores votes
|
||||
pub struct Table<C: Context> {
|
||||
proposed_candidates: HashMap<C::ValidatorId, (C::Digest, C::Signature)>,
|
||||
detected_misbehavior: HashMap<C::ValidatorId, Misbehavior<C>>,
|
||||
candidate_votes: HashMap<C::Digest, CandidateData<C>>,
|
||||
}
|
||||
|
||||
impl<C: Context> Table<C> {
|
||||
/// Import a signed statement
|
||||
pub fn import_statement(&mut self, context: &C, statement: SignedStatement<C>) {
|
||||
let signer = match context.statement_signer(&statement) {
|
||||
None => return,
|
||||
Some(signer) => signer,
|
||||
};
|
||||
|
||||
let maybe_misbehavior = match statement.statement {
|
||||
Statement::Candidate(candidate) => self.import_candidate(
|
||||
context,
|
||||
signer.clone(),
|
||||
candidate,
|
||||
statement.signature
|
||||
),
|
||||
Statement::Valid(digest) => unimplemented!(),
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
|
||||
if let Some(misbehavior) = maybe_misbehavior {
|
||||
// all misbehavior in agreement is provable and actively malicious.
|
||||
// punishments are not cumulative.
|
||||
self.detected_misbehavior.insert(validator, misbehavior);
|
||||
}
|
||||
}
|
||||
|
||||
fn import_candidate(
|
||||
&mut self,
|
||||
context: &C,
|
||||
from: C::ValidatorId,
|
||||
candidate: C::Candidate,
|
||||
signature: C::Signature,
|
||||
) -> Option<Misbehavior<C>> {
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
let group = context.candidate_group(&candidate);
|
||||
if !context.is_member_of(&from, &group) {
|
||||
return Some(Misbehavior::UnauthorizedStatement(UnauthorizedStatement {
|
||||
statement: SignedStatement {
|
||||
signature,
|
||||
statement: Statement::Candidate(candidate),
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
// check that validator hasn't already specified another candidate.
|
||||
let digest = context.candidate_digest(&candidate);
|
||||
|
||||
match self.proposed_candidates.entry(from.clone()) {
|
||||
Entry::Occupied(occ) => {
|
||||
// if digest is different, fetch candidate and
|
||||
// note misbehavior.
|
||||
let old_digest = &occ.get().0;
|
||||
if old_digest != &digest {
|
||||
let old_candidate = self.candidate_votes.get(old_digest)
|
||||
.expect("proposed digest implies existence of votes entry; qed")
|
||||
.candidate
|
||||
.clone();
|
||||
|
||||
return Some(Misbehavior::MultipleCandidates(MultipleCandidates {
|
||||
first: (old_candidate, occ.get().1.clone()),
|
||||
second: (candidate, signature),
|
||||
}));
|
||||
}
|
||||
}
|
||||
Entry::Vacant(vacant) => {
|
||||
vacant.insert((digest.clone(), signature));
|
||||
|
||||
// TODO: seed validity votes with issuer here?
|
||||
self.candidate_votes.entry(digest).or_insert_with(move || CandidateData {
|
||||
group_id: group,
|
||||
candidate: candidate,
|
||||
validity_votes: HashMap::new(),
|
||||
indicated_bad_by: Vec::new(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user