Files
serai/substrate/tendermint/src/ext.rs

192 lines
7.2 KiB
Rust
Raw Normal View History

use core::{hash::Hash, fmt::Debug};
2022-10-16 07:30:11 -04:00
use std::sync::Arc;
use parity_scale_codec::{Encode, Decode};
use crate::{SignedMessage, commit_msg};
2022-10-16 07:30:11 -04:00
2022-10-17 08:07:23 -04:00
/// An alias for a series of traits required for a type to be usable as a validator ID,
/// automatically implemented for all types satisfying those traits.
pub trait ValidatorId:
Send + Sync + Clone + Copy + PartialEq + Eq + Hash + Debug + Encode + Decode
{
}
impl<V: Send + Sync + Clone + Copy + PartialEq + Eq + Hash + Debug + Encode + Decode> ValidatorId
for V
{
}
2022-10-17 08:07:23 -04:00
/// An alias for a series of traits required for a type to be usable as a signature,
/// automatically implemented for all types satisfying those traits.
pub trait Signature: Send + Sync + Clone + PartialEq + Debug + Encode + Decode {}
impl<S: Send + Sync + Clone + PartialEq + Debug + Encode + Decode> Signature for S {}
2022-10-16 03:55:39 -04:00
// Type aliases which are distinct according to the type system
2022-10-17 08:07:23 -04:00
/// A struct containing a Block Number, wrapped to have a distinct type.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode)]
2022-10-17 08:07:23 -04:00
pub struct BlockNumber(pub u64);
/// A struct containing a round number, wrapped to have a distinct type.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode)]
2022-10-17 08:07:23 -04:00
pub struct Round(pub u32);
2022-10-16 03:55:39 -04:00
2022-10-17 08:07:23 -04:00
/// A signature scheme used by validators.
pub trait SignatureScheme: Send + Sync {
2022-10-17 08:07:23 -04:00
// Type used to identify validators.
2022-10-16 09:42:33 -04:00
type ValidatorId: ValidatorId;
2022-10-17 08:07:23 -04:00
/// Signature type.
type Signature: Signature;
2022-10-17 08:07:23 -04:00
/// Type representing an aggregate signature. This would presumably be a BLS signature,
/// yet even with Schnorr signatures
/// [half-aggregation is possible](https://eprint.iacr.org/2021/350).
/// It could even be a threshold signature scheme, though that's currently unexpected.
type AggregateSignature: Signature;
2022-10-17 08:07:23 -04:00
/// Sign a signature with the current validator's private key.
2022-10-16 09:42:33 -04:00
fn sign(&self, msg: &[u8]) -> Self::Signature;
2022-10-17 08:07:23 -04:00
/// Verify a signature from the validator in question.
2022-10-16 09:42:33 -04:00
#[must_use]
2022-10-20 03:40:46 -04:00
fn verify(&self, validator: Self::ValidatorId, msg: &[u8], sig: &Self::Signature) -> bool;
2022-10-17 08:07:23 -04:00
/// Aggregate signatures.
fn aggregate(sigs: &[Self::Signature]) -> Self::AggregateSignature;
2022-10-17 08:07:23 -04:00
/// Verify an aggregate signature for the list of signers.
#[must_use]
fn verify_aggregate(
&self,
signers: &[Self::ValidatorId],
2022-10-20 03:40:46 -04:00
msg: &[u8],
2022-10-17 08:07:23 -04:00
sig: &Self::AggregateSignature,
) -> bool;
}
2022-10-17 08:07:23 -04:00
/// A commit for a specific block. The list of validators have weight exceeding the threshold for
/// a valid commit.
#[derive(Clone, PartialEq, Debug, Encode, Decode)]
pub struct Commit<S: SignatureScheme> {
/// Round which created this commit.
pub round: Round,
2022-10-17 08:07:23 -04:00
/// Validators participating in the signature.
pub validators: Vec<S::ValidatorId>,
2022-10-17 08:07:23 -04:00
/// Aggregate signature.
pub signature: S::AggregateSignature,
}
2022-10-17 08:07:23 -04:00
/// Weights for the validators present.
2022-10-16 07:30:11 -04:00
pub trait Weights: Send + Sync {
2022-10-16 03:55:39 -04:00
type ValidatorId: ValidatorId;
2022-10-17 08:07:23 -04:00
/// Total weight of all validators.
fn total_weight(&self) -> u64;
2022-10-17 08:07:23 -04:00
/// Weight for a specific validator.
2022-10-16 03:55:39 -04:00
fn weight(&self, validator: Self::ValidatorId) -> u64;
2022-10-17 08:07:23 -04:00
/// Threshold needed for BFT consensus.
fn threshold(&self) -> u64 {
((self.total_weight() * 2) / 3) + 1
}
2022-10-17 08:07:23 -04:00
/// Threshold preventing BFT consensus.
2022-10-16 07:30:11 -04:00
fn fault_thresold(&self) -> u64 {
(self.total_weight() - self.threshold()) + 1
}
2022-10-16 07:30:11 -04:00
/// Weighted round robin function.
2022-10-16 03:55:39 -04:00
fn proposer(&self, number: BlockNumber, round: Round) -> Self::ValidatorId;
2022-10-16 07:30:11 -04:00
}
2022-10-17 08:07:23 -04:00
/// Simplified error enum representing a block's validity.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode)]
2022-10-16 09:42:33 -04:00
pub enum BlockError {
2022-10-17 08:07:23 -04:00
/// Malformed block which is wholly invalid.
2022-10-16 09:42:33 -04:00
Fatal,
2022-10-17 08:07:23 -04:00
/// Valid block by syntax, with semantics which may or may not be valid yet are locally
/// considered invalid. If a block fails to validate with this, a slash will not be triggered.
2022-10-16 09:42:33 -04:00
Temporal,
}
2022-10-17 08:07:23 -04:00
/// Trait representing a Block.
pub trait Block: Send + Sync + Clone + PartialEq + Debug + Encode + Decode {
2022-10-17 08:07:23 -04:00
// Type used to identify blocks. Presumably a cryptographic hash of the block.
type Id: Send + Sync + Copy + Clone + PartialEq + AsRef<[u8]> + Debug + Encode + Decode;
2022-10-16 09:42:33 -04:00
2022-10-17 08:07:23 -04:00
/// Return the deterministic, unique ID for this block.
2022-10-16 09:42:33 -04:00
fn id(&self) -> Self::Id;
}
#[cfg(feature = "substrate")]
impl<B: sp_runtime::traits::Block> Block for B {
type Id = B::Hash;
fn id(&self) -> B::Hash {
self.hash()
}
}
2022-10-17 08:07:23 -04:00
/// Trait representing the distributed system Tendermint is providing consensus over.
2022-10-16 07:30:11 -04:00
#[async_trait::async_trait]
pub trait Network: Send + Sync {
2022-10-17 08:07:23 -04:00
// Type used to identify validators.
2022-10-16 07:30:11 -04:00
type ValidatorId: ValidatorId;
2022-10-17 08:07:23 -04:00
/// Signature scheme used by validators.
type SignatureScheme: SignatureScheme<ValidatorId = Self::ValidatorId>;
2022-10-17 08:07:23 -04:00
/// Object representing the weights of validators.
2022-10-16 07:30:11 -04:00
type Weights: Weights<ValidatorId = Self::ValidatorId>;
2022-10-17 08:07:23 -04:00
/// Type used for ordered blocks of information.
2022-10-16 07:30:11 -04:00
type Block: Block;
2022-10-16 07:54:07 -04:00
// Block time in seconds
const BLOCK_TIME: u32;
2022-10-17 08:07:23 -04:00
/// Return the signature scheme in use. The instance is expected to have the validators' public
/// keys, along with an instance of the private key of the current validator.
2022-10-16 09:42:33 -04:00
fn signature_scheme(&self) -> Arc<Self::SignatureScheme>;
2022-10-17 08:07:23 -04:00
/// Return a reference to the validators' weights.
2022-10-16 07:30:11 -04:00
fn weights(&self) -> Arc<Self::Weights>;
2022-10-17 08:07:23 -04:00
/// Verify a commit for a given block. Intended for use when syncing or when not an active
/// validator.
#[must_use]
fn verify_commit(
&self,
id: <Self::Block as Block>::Id,
2022-10-17 08:07:23 -04:00
commit: &Commit<Self::SignatureScheme>,
) -> bool {
2022-10-17 08:07:23 -04:00
if !self.signature_scheme().verify_aggregate(
&commit.validators,
2022-10-20 03:40:46 -04:00
&commit_msg(commit.round, id.as_ref()),
2022-10-17 08:07:23 -04:00
&commit.signature,
) {
return false;
}
let weights = self.weights();
commit.validators.iter().map(|v| weights.weight(*v)).sum::<u64>() >= weights.threshold()
}
2022-10-17 08:07:23 -04:00
/// Broadcast a message to the other validators. If authenticated channels have already been
/// established, this will double-authenticate. Switching to unauthenticated channels in a system
/// already providing authenticated channels is not recommended as this is a minor, temporal
/// inefficiency while downgrading channels may have wider implications.
async fn broadcast(
&mut self,
msg: SignedMessage<
Self::ValidatorId,
Self::Block,
<Self::SignatureScheme as SignatureScheme>::Signature,
>,
);
2022-10-16 07:30:11 -04:00
2022-10-17 08:07:23 -04:00
/// Trigger a slash for the validator in question who was definitively malicious.
/// The exact process of triggering a slash is undefined and left to the network as a whole.
// TODO: This is spammed right now.
2022-10-16 07:30:11 -04:00
async fn slash(&mut self, validator: Self::ValidatorId);
2022-10-16 03:55:39 -04:00
2022-10-17 08:07:23 -04:00
/// Validate a block.
2022-10-16 07:30:11 -04:00
fn validate(&mut self, block: &Self::Block) -> Result<(), BlockError>;
2022-10-17 08:07:23 -04:00
/// Add a block, returning the proposal for the next one. It's possible a block, which was never
/// validated or even failed validation, may be passed here if a supermajority of validators did
/// consider it valid and created a commit for it. This deviates from the paper which will have a
/// local node refuse to decide on a block it considers invalid. This library acknowledges the
/// network did decide on it, leaving handling of it to the network, and outside of this scope.
fn add_block(&mut self, block: Self::Block, commit: Commit<Self::SignatureScheme>)
-> Self::Block;
}