mirror of
https://github.com/serai-dex/serai.git
synced 2025-03-15 16:12:44 +00:00
Document tendermint
This commit is contained in:
parent
0501ff259e
commit
f28d412f78
4 changed files with 127 additions and 35 deletions
|
@ -3,10 +3,34 @@
|
||||||
An implementation of the Tendermint state machine in Rust.
|
An implementation of the Tendermint state machine in Rust.
|
||||||
|
|
||||||
This is solely the state machine, intended to be mapped to any arbitrary system.
|
This is solely the state machine, intended to be mapped to any arbitrary system.
|
||||||
It supports an arbitrary hash function, signature protocol, and block
|
It supports an arbitrary signature scheme, weighting, and block definition
|
||||||
definition accordingly. It is not intended to work with the Cosmos SDK, solely
|
accordingly. It is not intended to work with the Cosmos SDK, solely to be an
|
||||||
be an implementation of the
|
implementation of the [academic protocol](https://arxiv.org/pdf/1807.04938.pdf).
|
||||||
[academic protocol](https://arxiv.org/pdf/1807.04938.pdf).
|
|
||||||
|
### Caveats
|
||||||
|
|
||||||
|
- Only SCALE serialization is supported currently. Ideally, everything from
|
||||||
|
SCALE to borsh to bincode would be supported. SCALE was chosen due to this
|
||||||
|
being under Serai, which uses Substrate, which uses SCALE. Accordingly, when
|
||||||
|
deciding which of the three (mutually incompatible) options to support...
|
||||||
|
|
||||||
|
- tokio is explicitly used for the asynchronous task which runs the Tendermint
|
||||||
|
machine. Ideally, `futures-rs` would be used enabling any async runtime to be
|
||||||
|
used.
|
||||||
|
|
||||||
|
- It is possible for `add_block` to be called on a block which failed (or never
|
||||||
|
went through in the first place) validation. This is a break from the paper
|
||||||
|
which is accepted here. This is for two reasons.
|
||||||
|
|
||||||
|
1) Serai needing this functionality.
|
||||||
|
2) If a block is committed which is invalid, either there's a malicious
|
||||||
|
majority now defining consensus OR the local node is malicious by virtue of
|
||||||
|
being faulty. Considering how either represents a fatal circumstance,
|
||||||
|
except with regards to system like Serai which have their own logic for
|
||||||
|
pseudo-valid blocks, it is accepted as a possible behavior with the caveat
|
||||||
|
any consumers must be aware of it. No machine will vote nor precommit to a
|
||||||
|
block it considers invalid, so for a network with an honest majority, this
|
||||||
|
is a non-issue.
|
||||||
|
|
||||||
### Paper
|
### Paper
|
||||||
|
|
||||||
|
@ -41,3 +65,6 @@ The included pseudocode segments can be minimally described as follows:
|
||||||
61-64 on timeout prevote
|
61-64 on timeout prevote
|
||||||
65-67 on timeout precommit
|
65-67 on timeout precommit
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The corresponding Rust code implementing these tasks are marked with their
|
||||||
|
related line numbers.
|
||||||
|
|
|
@ -5,6 +5,8 @@ use parity_scale_codec::{Encode, Decode};
|
||||||
|
|
||||||
use crate::SignedMessage;
|
use crate::SignedMessage;
|
||||||
|
|
||||||
|
/// 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:
|
pub trait ValidatorId:
|
||||||
Send + Sync + Clone + Copy + PartialEq + Eq + Hash + Debug + Encode + Decode
|
Send + Sync + Clone + Copy + PartialEq + Eq + Hash + Debug + Encode + Decode
|
||||||
{
|
{
|
||||||
|
@ -14,48 +16,73 @@ impl<V: Send + Sync + Clone + Copy + PartialEq + Eq + Hash + Debug + Encode + De
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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 {}
|
pub trait Signature: Send + Sync + Clone + PartialEq + Debug + Encode + Decode {}
|
||||||
impl<S: Send + Sync + Clone + PartialEq + Debug + Encode + Decode> Signature for S {}
|
impl<S: Send + Sync + Clone + PartialEq + Debug + Encode + Decode> Signature for S {}
|
||||||
|
|
||||||
// Type aliases which are distinct according to the type system
|
// Type aliases which are distinct according to the type system
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode)]
|
|
||||||
pub struct BlockNumber(pub u32);
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode)]
|
|
||||||
pub struct Round(pub u16);
|
|
||||||
|
|
||||||
|
/// A struct containing a Block Number, wrapped to have a distinct type.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode)]
|
||||||
|
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)]
|
||||||
|
pub struct Round(pub u32);
|
||||||
|
|
||||||
|
/// A signature scheme used by validators.
|
||||||
pub trait SignatureScheme: Send + Sync {
|
pub trait SignatureScheme: Send + Sync {
|
||||||
|
// Type used to identify validators.
|
||||||
type ValidatorId: ValidatorId;
|
type ValidatorId: ValidatorId;
|
||||||
|
/// Signature type.
|
||||||
type Signature: Signature;
|
type Signature: Signature;
|
||||||
|
/// 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;
|
type AggregateSignature: Signature;
|
||||||
|
|
||||||
|
/// Sign a signature with the current validator's private key.
|
||||||
fn sign(&self, msg: &[u8]) -> Self::Signature;
|
fn sign(&self, msg: &[u8]) -> Self::Signature;
|
||||||
|
/// Verify a signature from the validator in question.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn verify(&self, validator: Self::ValidatorId, msg: &[u8], sig: Self::Signature) -> bool;
|
fn verify(&self, validator: Self::ValidatorId, msg: &[u8], sig: Self::Signature) -> bool;
|
||||||
|
|
||||||
|
/// Aggregate signatures.
|
||||||
fn aggregate(sigs: &[Self::Signature]) -> Self::AggregateSignature;
|
fn aggregate(sigs: &[Self::Signature]) -> Self::AggregateSignature;
|
||||||
|
/// Verify an aggregate signature for the list of signers.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn verify_aggregate(
|
fn verify_aggregate(
|
||||||
&self,
|
&self,
|
||||||
msg: &[u8],
|
msg: &[u8],
|
||||||
signers: &[Self::ValidatorId],
|
signers: &[Self::ValidatorId],
|
||||||
sig: Self::AggregateSignature,
|
sig: &Self::AggregateSignature,
|
||||||
) -> bool;
|
) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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)]
|
#[derive(Clone, PartialEq, Debug, Encode, Decode)]
|
||||||
pub struct Commit<S: SignatureScheme> {
|
pub struct Commit<S: SignatureScheme> {
|
||||||
|
/// Validators participating in the signature.
|
||||||
pub validators: Vec<S::ValidatorId>,
|
pub validators: Vec<S::ValidatorId>,
|
||||||
|
/// Aggregate signature.
|
||||||
pub signature: S::AggregateSignature,
|
pub signature: S::AggregateSignature,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Weights for the validators present.
|
||||||
pub trait Weights: Send + Sync {
|
pub trait Weights: Send + Sync {
|
||||||
type ValidatorId: ValidatorId;
|
type ValidatorId: ValidatorId;
|
||||||
|
|
||||||
|
/// Total weight of all validators.
|
||||||
fn total_weight(&self) -> u64;
|
fn total_weight(&self) -> u64;
|
||||||
|
/// Weight for a specific validator.
|
||||||
fn weight(&self, validator: Self::ValidatorId) -> u64;
|
fn weight(&self, validator: Self::ValidatorId) -> u64;
|
||||||
|
/// Threshold needed for BFT consensus.
|
||||||
fn threshold(&self) -> u64 {
|
fn threshold(&self) -> u64 {
|
||||||
((self.total_weight() * 2) / 3) + 1
|
((self.total_weight() * 2) / 3) + 1
|
||||||
}
|
}
|
||||||
|
/// Threshold preventing BFT consensus.
|
||||||
fn fault_thresold(&self) -> u64 {
|
fn fault_thresold(&self) -> u64 {
|
||||||
(self.total_weight() - self.threshold()) + 1
|
(self.total_weight() - self.threshold()) + 1
|
||||||
}
|
}
|
||||||
|
@ -64,41 +91,59 @@ pub trait Weights: Send + Sync {
|
||||||
fn proposer(&self, number: BlockNumber, round: Round) -> Self::ValidatorId;
|
fn proposer(&self, number: BlockNumber, round: Round) -> Self::ValidatorId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Simplified error enum representing a block's validity.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode)]
|
||||||
pub enum BlockError {
|
pub enum BlockError {
|
||||||
// Invalid behavior entirely
|
/// Malformed block which is wholly invalid.
|
||||||
Fatal,
|
Fatal,
|
||||||
// Potentially valid behavior dependent on unsynchronized state
|
/// 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.
|
||||||
Temporal,
|
Temporal,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Trait representing a Block.
|
||||||
pub trait Block: Send + Sync + Clone + PartialEq + Debug + Encode + Decode {
|
pub trait Block: Send + Sync + Clone + PartialEq + Debug + Encode + Decode {
|
||||||
|
// Type used to identify blocks. Presumably a cryptographic hash of the block.
|
||||||
type Id: Send + Sync + Copy + Clone + PartialEq + Debug + Encode + Decode;
|
type Id: Send + Sync + Copy + Clone + PartialEq + Debug + Encode + Decode;
|
||||||
|
|
||||||
|
/// Return the deterministic, unique ID for this block.
|
||||||
fn id(&self) -> Self::Id;
|
fn id(&self) -> Self::Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Trait representing the distributed system Tendermint is providing consensus over.
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait Network: Send + Sync {
|
pub trait Network: Send + Sync {
|
||||||
|
// Type used to identify validators.
|
||||||
type ValidatorId: ValidatorId;
|
type ValidatorId: ValidatorId;
|
||||||
|
/// Signature scheme used by validators.
|
||||||
type SignatureScheme: SignatureScheme<ValidatorId = Self::ValidatorId>;
|
type SignatureScheme: SignatureScheme<ValidatorId = Self::ValidatorId>;
|
||||||
|
/// Object representing the weights of validators.
|
||||||
type Weights: Weights<ValidatorId = Self::ValidatorId>;
|
type Weights: Weights<ValidatorId = Self::ValidatorId>;
|
||||||
|
/// Type used for ordered blocks of information.
|
||||||
type Block: Block;
|
type Block: Block;
|
||||||
|
|
||||||
// Block time in seconds
|
// Block time in seconds
|
||||||
const BLOCK_TIME: u32;
|
const BLOCK_TIME: u32;
|
||||||
|
|
||||||
|
/// 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.
|
||||||
fn signature_scheme(&self) -> Arc<Self::SignatureScheme>;
|
fn signature_scheme(&self) -> Arc<Self::SignatureScheme>;
|
||||||
|
/// Return a reference to the validators' weights.
|
||||||
fn weights(&self) -> Arc<Self::Weights>;
|
fn weights(&self) -> Arc<Self::Weights>;
|
||||||
|
|
||||||
|
/// Verify a commit for a given block. Intended for use when syncing or when not an active
|
||||||
|
/// validator.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn verify_commit(
|
fn verify_commit(
|
||||||
&self,
|
&self,
|
||||||
id: <Self::Block as Block>::Id,
|
id: <Self::Block as Block>::Id,
|
||||||
commit: Commit<Self::SignatureScheme>,
|
commit: &Commit<Self::SignatureScheme>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if !self.signature_scheme().verify_aggregate(&id.encode(), &commit.validators, commit.signature)
|
if !self.signature_scheme().verify_aggregate(
|
||||||
{
|
&id.encode(),
|
||||||
|
&commit.validators,
|
||||||
|
&commit.signature,
|
||||||
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,6 +151,10 @@ pub trait Network: Send + Sync {
|
||||||
commit.validators.iter().map(|v| weights.weight(*v)).sum::<u64>() >= weights.threshold()
|
commit.validators.iter().map(|v| weights.weight(*v)).sum::<u64>() >= weights.threshold()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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(
|
async fn broadcast(
|
||||||
&mut self,
|
&mut self,
|
||||||
msg: SignedMessage<
|
msg: SignedMessage<
|
||||||
|
@ -115,11 +164,17 @@ pub trait Network: Send + Sync {
|
||||||
>,
|
>,
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: Should this take a verifiable reason?
|
/// 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.
|
||||||
async fn slash(&mut self, validator: Self::ValidatorId);
|
async fn slash(&mut self, validator: Self::ValidatorId);
|
||||||
|
|
||||||
|
/// Validate a block.
|
||||||
fn validate(&mut self, block: &Self::Block) -> Result<(), BlockError>;
|
fn validate(&mut self, block: &Self::Block) -> Result<(), BlockError>;
|
||||||
// Add a block and return the proposal for the next one
|
/// 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>)
|
fn add_block(&mut self, block: Self::Block, commit: Commit<Self::SignatureScheme>)
|
||||||
-> Self::Block;
|
-> Self::Block;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ use tokio::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Traits and types of the external network being integrated with to provide consensus over.
|
||||||
pub mod ext;
|
pub mod ext;
|
||||||
use ext::*;
|
use ext::*;
|
||||||
|
|
||||||
|
@ -59,7 +60,7 @@ impl<B: Block, S: Signature> Data<B, S> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug, Encode, Decode)]
|
#[derive(Clone, PartialEq, Debug, Encode, Decode)]
|
||||||
pub struct Message<V: ValidatorId, B: Block, S: Signature> {
|
struct Message<V: ValidatorId, B: Block, S: Signature> {
|
||||||
sender: V,
|
sender: V,
|
||||||
|
|
||||||
number: BlockNumber,
|
number: BlockNumber,
|
||||||
|
@ -68,18 +69,20 @@ pub struct Message<V: ValidatorId, B: Block, S: Signature> {
|
||||||
data: Data<B, S>,
|
data: Data<B, S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A signed Tendermint consensus message to be broadcast to the other validators.
|
||||||
#[derive(Clone, PartialEq, Debug, Encode, Decode)]
|
#[derive(Clone, PartialEq, Debug, Encode, Decode)]
|
||||||
pub struct SignedMessage<V: ValidatorId, B: Block, S: Signature> {
|
pub struct SignedMessage<V: ValidatorId, B: Block, S: Signature> {
|
||||||
msg: Message<V, B, S>,
|
msg: Message<V, B, S>,
|
||||||
sig: S,
|
sig: S,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
pub enum TendermintError<V: ValidatorId> {
|
enum TendermintError<V: ValidatorId> {
|
||||||
Malicious(V),
|
Malicious(V),
|
||||||
Temporal,
|
Temporal,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A machine executing the Tendermint protocol.
|
||||||
pub struct TendermintMachine<N: Network> {
|
pub struct TendermintMachine<N: Network> {
|
||||||
network: Arc<RwLock<N>>,
|
network: Arc<RwLock<N>>,
|
||||||
signer: Arc<N::SignatureScheme>,
|
signer: Arc<N::SignatureScheme>,
|
||||||
|
@ -100,19 +103,21 @@ pub struct TendermintMachine<N: Network> {
|
||||||
timeouts: HashMap<Step, Instant>,
|
timeouts: HashMap<Step, Instant>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A handle to an asynchronous task, along with a channel to inform of it of messages received.
|
||||||
pub struct TendermintHandle<N: Network> {
|
pub struct TendermintHandle<N: Network> {
|
||||||
// Messages received
|
/// Channel to send messages received from the P2P layer.
|
||||||
pub messages: mpsc::Sender<
|
pub messages: mpsc::Sender<
|
||||||
SignedMessage<N::ValidatorId, N::Block, <N::SignatureScheme as SignatureScheme>::Signature>,
|
SignedMessage<N::ValidatorId, N::Block, <N::SignatureScheme as SignatureScheme>::Signature>,
|
||||||
>,
|
>,
|
||||||
// Async task executing the machine
|
/// Handle for the asynchronous task executing the machine. The task will automatically exit
|
||||||
|
/// when the channel is dropped.
|
||||||
pub handle: JoinHandle<()>,
|
pub handle: JoinHandle<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: Network + 'static> TendermintMachine<N> {
|
impl<N: Network + 'static> TendermintMachine<N> {
|
||||||
fn timeout(&self, step: Step) -> Instant {
|
fn timeout(&self, step: Step) -> Instant {
|
||||||
let mut round_time = Duration::from_secs(N::BLOCK_TIME.into());
|
let mut round_time = Duration::from_secs(N::BLOCK_TIME.into());
|
||||||
round_time *= (self.round.0 + 1).into();
|
round_time *= self.round.0 + 1;
|
||||||
let step_time = round_time / 3;
|
let step_time = round_time / 3;
|
||||||
|
|
||||||
let offset = match step {
|
let offset = match step {
|
||||||
|
@ -183,6 +188,8 @@ impl<N: Network + 'static> TendermintMachine<N> {
|
||||||
self.round(Round(0)).await;
|
self.round(Round(0)).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new Tendermint machine, for the specified proposer, from the specified block, with
|
||||||
|
/// the specified block as the one to propose next, returning a handle for the machine.
|
||||||
// 10
|
// 10
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
@ -262,10 +269,10 @@ impl<N: Network + 'static> TendermintMachine<N> {
|
||||||
sigs.push(sig);
|
sigs.push(sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
let proposal = machine.network.write().await.add_block(
|
let commit =
|
||||||
block,
|
Commit { validators, signature: N::SignatureScheme::aggregate(&sigs) };
|
||||||
Commit { validators, signature: N::SignatureScheme::aggregate(&sigs) },
|
debug_assert!(machine.network.read().await.verify_commit(block.id(), &commit));
|
||||||
);
|
let proposal = machine.network.write().await.add_block(block, commit);
|
||||||
machine.reset(proposal).await
|
machine.reset(proposal).await
|
||||||
}
|
}
|
||||||
Err(TendermintError::Malicious(validator)) => {
|
Err(TendermintError::Malicious(validator)) => {
|
||||||
|
@ -385,12 +392,10 @@ impl<N: Network + 'static> TendermintMachine<N> {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 22-27
|
// 22-27
|
||||||
self
|
self.network.write().await.validate(block).map_err(|e| match e {
|
||||||
.network
|
BlockError::Temporal => TendermintError::Temporal,
|
||||||
.write()
|
BlockError::Fatal => TendermintError::Malicious(msg.sender),
|
||||||
.await
|
})?;
|
||||||
.validate(block)
|
|
||||||
.map_err(|_| TendermintError::Malicious(msg.sender))?;
|
|
||||||
debug_assert!(self
|
debug_assert!(self
|
||||||
.broadcast(Data::Prevote(Some(block.id()).filter(|_| self.locked.is_none() ||
|
.broadcast(Data::Prevote(Some(block.id()).filter(|_| self.locked.is_none() ||
|
||||||
self.locked.as_ref().map(|locked| locked.1.id()) == Some(block.id()))))
|
self.locked.as_ref().map(|locked| locked.1.id()) == Some(block.id()))))
|
||||||
|
|
|
@ -32,7 +32,12 @@ impl SignatureScheme for TestSignatureScheme {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn verify_aggregate(&self, msg: &[u8], signers: &[TestValidatorId], sigs: Vec<[u8; 32]>) -> bool {
|
fn verify_aggregate(
|
||||||
|
&self,
|
||||||
|
msg: &[u8],
|
||||||
|
signers: &[TestValidatorId],
|
||||||
|
sigs: &Vec<[u8; 32]>,
|
||||||
|
) -> bool {
|
||||||
assert_eq!(signers.len(), sigs.len());
|
assert_eq!(signers.len(), sigs.len());
|
||||||
for sig in signers.iter().zip(sigs.iter()) {
|
for sig in signers.iter().zip(sigs.iter()) {
|
||||||
assert!(self.verify(*sig.0, msg, *sig.1));
|
assert!(self.verify(*sig.0, msg, *sig.1));
|
||||||
|
@ -53,7 +58,7 @@ impl Weights for TestWeights {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn proposer(&self, number: BlockNumber, round: Round) -> TestValidatorId {
|
fn proposer(&self, number: BlockNumber, round: Round) -> TestValidatorId {
|
||||||
TestValidatorId::try_from((number.0 + u32::from(round.0)) % 4).unwrap()
|
TestValidatorId::try_from((number.0 + u64::from(round.0)) % 4).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +113,7 @@ impl Network for TestNetwork {
|
||||||
fn add_block(&mut self, block: TestBlock, commit: Commit<TestSignatureScheme>) -> TestBlock {
|
fn add_block(&mut self, block: TestBlock, commit: Commit<TestSignatureScheme>) -> TestBlock {
|
||||||
dbg!("Adding ", &block);
|
dbg!("Adding ", &block);
|
||||||
assert!(block.valid.is_ok());
|
assert!(block.valid.is_ok());
|
||||||
assert!(self.verify_commit(block.id(), commit));
|
assert!(self.verify_commit(block.id(), &commit));
|
||||||
TestBlock { id: block.id + 1, valid: Ok(()) }
|
TestBlock { id: block.id + 1, valid: Ok(()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue