Skip to main content

tendermint_machine/
ext.rs

1use core::{hash::Hash, fmt::Debug};
2use std::{sync::Arc, collections::HashSet};
3
4use async_trait::async_trait;
5use thiserror::Error;
6
7use parity_scale_codec::{Encode, Decode};
8
9use crate::{SignedMessageFor, commit_msg};
10
11/// An alias for a series of traits required for a type to be usable as a validator ID,
12/// automatically implemented for all types satisfying those traits.
13pub trait ValidatorId:
14  Send + Sync + Clone + Copy + PartialEq + Eq + Hash + Debug + Encode + Decode
15{
16}
17impl<V: Send + Sync + Clone + Copy + PartialEq + Eq + Hash + Debug + Encode + Decode> ValidatorId
18  for V
19{
20}
21
22/// An alias for a series of traits required for a type to be usable as a signature,
23/// automatically implemented for all types satisfying those traits.
24pub trait Signature: Send + Sync + Clone + PartialEq + Debug + Encode + Decode {}
25impl<S: Send + Sync + Clone + PartialEq + Debug + Encode + Decode> Signature for S {}
26
27// Type aliases which are distinct according to the type system
28
29/// A struct containing a Block Number, wrapped to have a distinct type.
30#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode)]
31pub struct BlockNumber(pub u64);
32/// A struct containing a round number, wrapped to have a distinct type.
33#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode)]
34pub struct RoundNumber(pub u32);
35
36/// A signer for a validator.
37#[async_trait]
38pub trait Signer: Send + Sync {
39  // Type used to identify validators.
40  type ValidatorId: ValidatorId;
41  /// Signature type.
42  type Signature: Signature;
43
44  /// Returns the validator's current ID. Returns None if they aren't a current validator.
45  async fn validator_id(&self) -> Option<Self::ValidatorId>;
46  /// Sign a signature with the current validator's private key.
47  async fn sign(&self, msg: &[u8]) -> Self::Signature;
48}
49
50#[async_trait]
51impl<S: Signer> Signer for Arc<S> {
52  type ValidatorId = S::ValidatorId;
53  type Signature = S::Signature;
54
55  async fn validator_id(&self) -> Option<Self::ValidatorId> {
56    self.as_ref().validator_id().await
57  }
58
59  async fn sign(&self, msg: &[u8]) -> Self::Signature {
60    self.as_ref().sign(msg).await
61  }
62}
63
64/// A signature scheme used by validators.
65pub trait SignatureScheme: Send + Sync {
66  // Type used to identify validators.
67  type ValidatorId: ValidatorId;
68  /// Signature type.
69  type Signature: Signature;
70  /// Type representing an aggregate signature. This would presumably be a BLS signature,
71  /// yet even with Schnorr signatures
72  /// [half-aggregation is possible](https://eprint.iacr.org/2021/350).
73  /// It could even be a threshold signature scheme, though that's currently unexpected.
74  type AggregateSignature: Signature;
75
76  /// Type representing a signer of this scheme.
77  type Signer: Signer<ValidatorId = Self::ValidatorId, Signature = Self::Signature>;
78
79  /// Verify a signature from the validator in question.
80  #[must_use]
81  fn verify(&self, validator: Self::ValidatorId, msg: &[u8], sig: &Self::Signature) -> bool;
82
83  /// Aggregate signatures.
84  fn aggregate(sigs: &[Self::Signature]) -> Self::AggregateSignature;
85  /// Verify an aggregate signature for the list of signers.
86  #[must_use]
87  fn verify_aggregate(
88    &self,
89    signers: &[Self::ValidatorId],
90    msg: &[u8],
91    sig: &Self::AggregateSignature,
92  ) -> bool;
93}
94
95impl<S: SignatureScheme> SignatureScheme for Arc<S> {
96  type ValidatorId = S::ValidatorId;
97  type Signature = S::Signature;
98  type AggregateSignature = S::AggregateSignature;
99  type Signer = S::Signer;
100
101  fn verify(&self, validator: Self::ValidatorId, msg: &[u8], sig: &Self::Signature) -> bool {
102    self.as_ref().verify(validator, msg, sig)
103  }
104
105  fn aggregate(sigs: &[Self::Signature]) -> Self::AggregateSignature {
106    S::aggregate(sigs)
107  }
108
109  #[must_use]
110  fn verify_aggregate(
111    &self,
112    signers: &[Self::ValidatorId],
113    msg: &[u8],
114    sig: &Self::AggregateSignature,
115  ) -> bool {
116    self.as_ref().verify_aggregate(signers, msg, sig)
117  }
118}
119
120/// A commit for a specific block. The list of validators have weight exceeding the threshold for
121/// a valid commit.
122#[derive(Clone, PartialEq, Debug, Encode, Decode)]
123pub struct Commit<S: SignatureScheme> {
124  /// End time of the round which created this commit, used as the start time of the next block.
125  pub end_time: u64,
126  /// Validators participating in the signature.
127  pub validators: Vec<S::ValidatorId>,
128  /// Aggregate signature.
129  pub signature: S::AggregateSignature,
130}
131
132/// Weights for the validators present.
133pub trait Weights: Send + Sync {
134  type ValidatorId: ValidatorId;
135
136  /// Total weight of all validators.
137  fn total_weight(&self) -> u64;
138  /// Weight for a specific validator.
139  fn weight(&self, validator: Self::ValidatorId) -> u64;
140  /// Threshold needed for BFT consensus.
141  fn threshold(&self) -> u64 {
142    ((self.total_weight() * 2) / 3) + 1
143  }
144  /// Threshold preventing BFT consensus.
145  fn fault_thresold(&self) -> u64 {
146    (self.total_weight() - self.threshold()) + 1
147  }
148
149  /// Weighted round robin function.
150  fn proposer(&self, block: BlockNumber, round: RoundNumber) -> Self::ValidatorId;
151}
152
153impl<W: Weights> Weights for Arc<W> {
154  type ValidatorId = W::ValidatorId;
155
156  fn total_weight(&self) -> u64 {
157    self.as_ref().total_weight()
158  }
159
160  fn weight(&self, validator: Self::ValidatorId) -> u64 {
161    self.as_ref().weight(validator)
162  }
163
164  fn proposer(&self, block: BlockNumber, round: RoundNumber) -> Self::ValidatorId {
165    self.as_ref().proposer(block, round)
166  }
167}
168
169/// Simplified error enum representing a block's validity.
170#[derive(Clone, Copy, PartialEq, Eq, Debug, Error, Encode, Decode)]
171pub enum BlockError {
172  /// Malformed block which is wholly invalid.
173  #[error("invalid block")]
174  Fatal,
175  /// Valid block by syntax, with semantics which may or may not be valid yet are locally
176  /// considered invalid. If a block fails to validate with this, a slash will not be triggered.
177  #[error("invalid block under local view")]
178  Temporal,
179}
180
181/// Trait representing a Block.
182pub trait Block: Send + Sync + Clone + PartialEq + Debug + Encode + Decode {
183  // Type used to identify blocks. Presumably a cryptographic hash of the block.
184  type Id: Send + Sync + Copy + Clone + PartialEq + AsRef<[u8]> + Debug + Encode + Decode;
185
186  /// Return the deterministic, unique ID for this block.
187  fn id(&self) -> Self::Id;
188}
189
190#[cfg(feature = "substrate")]
191impl<B: sp_runtime::traits::Block> Block for B {
192  type Id = B::Hash;
193  fn id(&self) -> B::Hash {
194    self.hash()
195  }
196}
197
198/// Trait representing the distributed system Tendermint is providing consensus over.
199#[async_trait]
200pub trait Network: Send + Sync {
201  // Type used to identify validators.
202  type ValidatorId: ValidatorId;
203  /// Signature scheme used by validators.
204  type SignatureScheme: SignatureScheme<ValidatorId = Self::ValidatorId>;
205  /// Object representing the weights of validators.
206  type Weights: Weights<ValidatorId = Self::ValidatorId>;
207  /// Type used for ordered blocks of information.
208  type Block: Block;
209
210  /// Maximum block processing time in seconds. This should include both the actual processing time
211  /// and the time to download the block.
212  const BLOCK_PROCESSING_TIME: u32;
213  /// Network latency time in seconds.
214  const LATENCY_TIME: u32;
215
216  /// The block time is defined as the processing time plus three times the latency.
217  fn block_time() -> u32 {
218    Self::BLOCK_PROCESSING_TIME + (3 * Self::LATENCY_TIME)
219  }
220
221  /// Return a handle on the signer in use, usable for the entire lifetime of the machine.
222  fn signer(&self) -> <Self::SignatureScheme as SignatureScheme>::Signer;
223  /// Return a handle on the signing scheme in use, usable for the entire lifetime of the machine.
224  fn signature_scheme(&self) -> Self::SignatureScheme;
225  /// Return a handle on the validators' weights, usable for the entire lifetime of the machine.
226  fn weights(&self) -> Self::Weights;
227
228  /// Verify a commit for a given block. Intended for use when syncing or when not an active
229  /// validator.
230  #[must_use]
231  fn verify_commit(
232    &self,
233    id: <Self::Block as Block>::Id,
234    commit: &Commit<Self::SignatureScheme>,
235  ) -> bool {
236    if commit.validators.iter().collect::<HashSet<_>>().len() != commit.validators.len() {
237      return false;
238    }
239
240    if !self.signature_scheme().verify_aggregate(
241      &commit.validators,
242      &commit_msg(commit.end_time, id.as_ref()),
243      &commit.signature,
244    ) {
245      return false;
246    }
247
248    let weights = self.weights();
249    commit.validators.iter().map(|v| weights.weight(*v)).sum::<u64>() >= weights.threshold()
250  }
251
252  /// Broadcast a message to the other validators. If authenticated channels have already been
253  /// established, this will double-authenticate. Switching to unauthenticated channels in a system
254  /// already providing authenticated channels is not recommended as this is a minor, temporal
255  /// inefficiency while downgrading channels may have wider implications.
256  async fn broadcast(&mut self, msg: SignedMessageFor<Self>);
257
258  /// Trigger a slash for the validator in question who was definitively malicious.
259  /// The exact process of triggering a slash is undefined and left to the network as a whole.
260  async fn slash(&mut self, validator: Self::ValidatorId);
261
262  /// Validate a block.
263  async fn validate(&mut self, block: &Self::Block) -> Result<(), BlockError>;
264  /// Add a block, returning the proposal for the next one. It's possible a block, which was never
265  /// validated or even failed validation, may be passed here if a supermajority of validators did
266  /// consider it valid and created a commit for it. This deviates from the paper which will have a
267  /// local node refuse to decide on a block it considers invalid. This library acknowledges the
268  /// network did decide on it, leaving handling of it to the network, and outside of this scope.
269  async fn add_block(
270    &mut self,
271    block: Self::Block,
272    commit: Commit<Self::SignatureScheme>,
273  ) -> Self::Block;
274}