Correct timing issues

1) Commit didn't include the round, leaving the clock in question.

2) Machines started with a local time, instead of a proper start time.

3) Machines immediately started the next block instead of waiting for 
the block time.
This commit is contained in:
Luke Parker 2022-10-20 00:21:14 -04:00
parent 6b56510da9
commit ff41e9f031
No known key found for this signature in database
GPG key ID: F9F1386DB1E119B6
4 changed files with 38 additions and 16 deletions

View file

@ -11,4 +11,4 @@ edition = "2021"
parity-scale-codec = { version = "3.2", features = ["derive"] } parity-scale-codec = { version = "3.2", features = ["derive"] }
async-trait = "0.1" async-trait = "0.1"
tokio = { version = "1", features = ["macros", "rt", "sync"] } tokio = { version = "1", features = ["macros", "sync", "time", "rt"] }

View file

@ -3,7 +3,7 @@ use std::sync::Arc;
use parity_scale_codec::{Encode, Decode}; use parity_scale_codec::{Encode, Decode};
use crate::SignedMessage; use crate::{SignedMessage, commit_msg};
/// An alias for a series of traits required for a type to be usable as a validator ID, /// 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. /// automatically implemented for all types satisfying those traits.
@ -64,6 +64,8 @@ pub trait SignatureScheme: Send + Sync {
/// a valid commit. /// 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> {
/// Round which created this commit.
pub round: Round,
/// Validators participating in the signature. /// Validators participating in the signature.
pub validators: Vec<S::ValidatorId>, pub validators: Vec<S::ValidatorId>,
/// Aggregate signature. /// Aggregate signature.
@ -140,7 +142,7 @@ pub trait Network: Send + Sync {
commit: &Commit<Self::SignatureScheme>, commit: &Commit<Self::SignatureScheme>,
) -> bool { ) -> bool {
if !self.signature_scheme().verify_aggregate( if !self.signature_scheme().verify_aggregate(
&id.encode(), &commit_msg(commit.round, id.as_ref()),
&commit.validators, &commit.validators,
&commit.signature, &commit.signature,
) { ) {

View file

@ -2,7 +2,7 @@ use core::fmt::Debug;
use std::{ use std::{
sync::Arc, sync::Arc,
time::{Instant, Duration}, time::{SystemTime, Instant, Duration},
collections::HashMap, collections::HashMap,
}; };
@ -14,6 +14,7 @@ use tokio::{
RwLock, RwLock,
mpsc::{self, error::TryRecvError}, mpsc::{self, error::TryRecvError},
}, },
time::sleep,
}; };
/// Traits and types of the external network being integrated with to provide consensus over. /// Traits and types of the external network being integrated with to provide consensus over.
@ -23,6 +24,10 @@ use ext::*;
mod message_log; mod message_log;
use message_log::MessageLog; use message_log::MessageLog;
pub(crate) fn commit_msg(round: Round, id: &[u8]) -> Vec<u8> {
[&round.0.to_le_bytes(), id].concat().to_vec()
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode)] #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode)]
enum Step { enum Step {
Propose, Propose,
@ -163,8 +168,6 @@ impl<N: Network + 'static> TendermintMachine<N> {
} }
fn round(&mut self, round: Round) -> bool { fn round(&mut self, round: Round) -> bool {
dbg!(round);
// Correct the start time // Correct the start time
for _ in self.round.0 .. round.0 { for _ in self.round.0 .. round.0 {
self.start_time = self.timeout(Step::Precommit); self.start_time = self.timeout(Step::Precommit);
@ -181,10 +184,16 @@ impl<N: Network + 'static> TendermintMachine<N> {
// 53-54 // 53-54
async fn reset(&mut self, proposal: N::Block) { async fn reset(&mut self, proposal: N::Block) {
// Wait for the next block interval
let round_end = self.timeout(Step::Precommit);
sleep(round_end - Instant::now()).await;
self.number.0 += 1; self.number.0 += 1;
self.start_time = Instant::now(); self.start_time = round_end;
self.personal_proposal = proposal; self.personal_proposal = proposal;
self.queue = self.queue.drain(..).filter(|msg| msg.1.number == self.number).collect();
self.log = MessageLog::new(self.network.read().await.weights()); self.log = MessageLog::new(self.network.read().await.weights());
self.locked = None; self.locked = None;
@ -199,9 +208,17 @@ impl<N: Network + 'static> TendermintMachine<N> {
pub fn new( pub fn new(
network: N, network: N,
proposer: N::ValidatorId, proposer: N::ValidatorId,
number: BlockNumber, start: (BlockNumber, SystemTime),
proposal: N::Block, proposal: N::Block,
) -> TendermintHandle<N> { ) -> TendermintHandle<N> {
// Convert the start time to an instant
// This is imprecise yet should be precise enough
let start_time = {
let instant_now = Instant::now();
let sys_now = SystemTime::now();
instant_now - sys_now.duration_since(start.1).unwrap_or(Duration::ZERO)
};
let (msg_send, mut msg_recv) = mpsc::channel(100); // Backlog to accept. Currently arbitrary let (msg_send, mut msg_recv) = mpsc::channel(100); // Backlog to accept. Currently arbitrary
TendermintHandle { TendermintHandle {
messages: msg_send, messages: msg_send,
@ -216,9 +233,8 @@ impl<N: Network + 'static> TendermintMachine<N> {
weights: weights.clone(), weights: weights.clone(),
proposer, proposer,
number, number: start.0,
// TODO: Use a non-local start time start_time,
start_time: Instant::now(),
personal_proposal: proposal, personal_proposal: proposal,
queue: vec![], queue: vec![],
@ -295,7 +311,11 @@ impl<N: Network + 'static> TendermintMachine<N> {
sigs.push(sig); sigs.push(sig);
} }
let commit = Commit { validators, signature: N::SignatureScheme::aggregate(&sigs) }; let commit = Commit {
round: msg.round,
validators,
signature: N::SignatureScheme::aggregate(&sigs),
};
debug_assert!(machine.network.read().await.verify_commit(block.id(), &commit)); debug_assert!(machine.network.read().await.verify_commit(block.id(), &commit));
let proposal = machine.network.write().await.add_block(block, commit); let proposal = machine.network.write().await.add_block(block, commit);
@ -325,7 +345,7 @@ impl<N: Network + 'static> TendermintMachine<N> {
) -> Result<Option<N::Block>, TendermintError<N::ValidatorId>> { ) -> Result<Option<N::Block>, TendermintError<N::ValidatorId>> {
// Verify the signature if this is a precommit // Verify the signature if this is a precommit
if let Data::Precommit(Some((id, sig))) = &msg.data { if let Data::Precommit(Some((id, sig))) = &msg.data {
if !self.signer.verify(msg.sender, id.as_ref(), sig.clone()) { if !self.signer.verify(msg.sender, &commit_msg(msg.round, id.as_ref()), sig.clone()) {
// Since we verified this validator actually sent the message, they're malicious // Since we verified this validator actually sent the message, they're malicious
Err(TendermintError::Malicious(msg.sender))?; Err(TendermintError::Malicious(msg.sender))?;
} }
@ -469,7 +489,7 @@ impl<N: Network + 'static> TendermintMachine<N> {
self.locked = Some((self.round, block.id())); self.locked = Some((self.round, block.id()));
self.broadcast(Data::Precommit(Some(( self.broadcast(Data::Precommit(Some((
block.id(), block.id(),
self.signer.sign(block.id().as_ref()), self.signer.sign(&commit_msg(self.round, block.id().as_ref())),
)))); ))));
return Ok(None); return Ok(None);
} }

View file

@ -1,4 +1,4 @@
use std::sync::Arc; use std::{sync::Arc, time::SystemTime};
use parity_scale_codec::{Encode, Decode}; use parity_scale_codec::{Encode, Decode};
@ -128,7 +128,7 @@ impl TestNetwork {
write.push(TendermintMachine::new( write.push(TendermintMachine::new(
TestNetwork(i, arc.clone()), TestNetwork(i, arc.clone()),
i, i,
BlockNumber(1), (BlockNumber(1), SystemTime::now()),
TestBlock { id: 1u32.to_le_bytes(), valid: Ok(()) }, TestBlock { id: 1u32.to_le_bytes(), valid: Ok(()) },
)); ));
} }