diff --git a/substrate/tendermint/Cargo.toml b/substrate/tendermint/Cargo.toml index 1c7d65cb..a6899672 100644 --- a/substrate/tendermint/Cargo.toml +++ b/substrate/tendermint/Cargo.toml @@ -11,4 +11,4 @@ edition = "2021" parity-scale-codec = { version = "3.2", features = ["derive"] } async-trait = "0.1" -tokio = { version = "1", features = ["macros", "rt", "sync"] } +tokio = { version = "1", features = ["macros", "sync", "time", "rt"] } diff --git a/substrate/tendermint/src/ext.rs b/substrate/tendermint/src/ext.rs index 9ede7216..e2f047eb 100644 --- a/substrate/tendermint/src/ext.rs +++ b/substrate/tendermint/src/ext.rs @@ -3,7 +3,7 @@ use std::sync::Arc; 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, /// automatically implemented for all types satisfying those traits. @@ -64,6 +64,8 @@ pub trait SignatureScheme: Send + Sync { /// a valid commit. #[derive(Clone, PartialEq, Debug, Encode, Decode)] pub struct Commit { + /// Round which created this commit. + pub round: Round, /// Validators participating in the signature. pub validators: Vec, /// Aggregate signature. @@ -140,7 +142,7 @@ pub trait Network: Send + Sync { commit: &Commit, ) -> bool { if !self.signature_scheme().verify_aggregate( - &id.encode(), + &commit_msg(commit.round, id.as_ref()), &commit.validators, &commit.signature, ) { diff --git a/substrate/tendermint/src/lib.rs b/substrate/tendermint/src/lib.rs index 4e3d44f2..5472ce6d 100644 --- a/substrate/tendermint/src/lib.rs +++ b/substrate/tendermint/src/lib.rs @@ -2,7 +2,7 @@ use core::fmt::Debug; use std::{ sync::Arc, - time::{Instant, Duration}, + time::{SystemTime, Instant, Duration}, collections::HashMap, }; @@ -14,6 +14,7 @@ use tokio::{ RwLock, mpsc::{self, error::TryRecvError}, }, + time::sleep, }; /// Traits and types of the external network being integrated with to provide consensus over. @@ -23,6 +24,10 @@ use ext::*; mod message_log; use message_log::MessageLog; +pub(crate) fn commit_msg(round: Round, id: &[u8]) -> Vec { + [&round.0.to_le_bytes(), id].concat().to_vec() +} + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode)] enum Step { Propose, @@ -163,8 +168,6 @@ impl TendermintMachine { } fn round(&mut self, round: Round) -> bool { - dbg!(round); - // Correct the start time for _ in self.round.0 .. round.0 { self.start_time = self.timeout(Step::Precommit); @@ -181,10 +184,16 @@ impl TendermintMachine { // 53-54 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.start_time = Instant::now(); + self.start_time = round_end; 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.locked = None; @@ -199,9 +208,17 @@ impl TendermintMachine { pub fn new( network: N, proposer: N::ValidatorId, - number: BlockNumber, + start: (BlockNumber, SystemTime), proposal: N::Block, ) -> TendermintHandle { + // 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 TendermintHandle { messages: msg_send, @@ -216,9 +233,8 @@ impl TendermintMachine { weights: weights.clone(), proposer, - number, - // TODO: Use a non-local start time - start_time: Instant::now(), + number: start.0, + start_time, personal_proposal: proposal, queue: vec![], @@ -295,7 +311,11 @@ impl TendermintMachine { 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)); let proposal = machine.network.write().await.add_block(block, commit); @@ -325,7 +345,7 @@ impl TendermintMachine { ) -> Result, TendermintError> { // Verify the signature if this is a precommit 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 Err(TendermintError::Malicious(msg.sender))?; } @@ -469,7 +489,7 @@ impl TendermintMachine { self.locked = Some((self.round, block.id())); self.broadcast(Data::Precommit(Some(( block.id(), - self.signer.sign(block.id().as_ref()), + self.signer.sign(&commit_msg(self.round, block.id().as_ref())), )))); return Ok(None); } diff --git a/substrate/tendermint/tests/ext.rs b/substrate/tendermint/tests/ext.rs index 21e35187..51751d9e 100644 --- a/substrate/tendermint/tests/ext.rs +++ b/substrate/tendermint/tests/ext.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{sync::Arc, time::SystemTime}; use parity_scale_codec::{Encode, Decode}; @@ -128,7 +128,7 @@ impl TestNetwork { write.push(TendermintMachine::new( TestNetwork(i, arc.clone()), i, - BlockNumber(1), + (BlockNumber(1), SystemTime::now()), TestBlock { id: 1u32.to_le_bytes(), valid: Ok(()) }, )); }