From 99f33d54d10e0497396b27f6877e2876d9325e57 Mon Sep 17 00:00:00 2001 From: Romain Ruetschi Date: Fri, 27 Oct 2023 12:15:59 +0200 Subject: [PATCH] Add facility for signing votes --- Code/common/Cargo.toml | 1 + Code/common/src/consensus.rs | 9 +- Code/common/src/lib.rs | 5 +- Code/common/src/public_key.rs | 22 +++++ Code/common/src/round.rs | 6 +- Code/common/src/validator_set.rs | 7 -- Code/consensus/src/executor.rs | 16 ++-- Code/consensus/src/signed_vote.rs | 17 ++-- Code/test/Cargo.toml | 15 ++-- Code/test/src/consensus.rs | 9 +- Code/test/src/lib.rs | 2 + Code/test/src/public_key.rs | 68 ++++++++++++++ Code/test/src/validator_set.rs | 85 ++++++++++-------- Code/test/src/value.rs | 4 + Code/test/src/vote.rs | 27 +++++- Code/test/tests/consensus_executor.rs | 125 ++++++++++++++------------ 16 files changed, 287 insertions(+), 131 deletions(-) create mode 100644 Code/common/src/public_key.rs create mode 100644 Code/test/src/public_key.rs diff --git a/Code/common/Cargo.toml b/Code/common/Cargo.toml index b46b5148e..7fad66bf0 100644 --- a/Code/common/Cargo.toml +++ b/Code/common/Cargo.toml @@ -5,3 +5,4 @@ edition = "2021" publish = false [dependencies] +signature = "2.1.0" diff --git a/Code/common/src/consensus.rs b/Code/common/src/consensus.rs index bcf906989..3aae8e3af 100644 --- a/Code/common/src/consensus.rs +++ b/Code/common/src/consensus.rs @@ -1,3 +1,4 @@ +use crate::public_key::PrivateKey; use crate::{ Address, Height, Proposal, PublicKey, Round, Validator, ValidatorSet, Value, ValueId, Vote, }; @@ -11,7 +12,8 @@ where type Address: Address; type Height: Height; type Proposal: Proposal; - type PublicKey: PublicKey; + type PrivateKey: PrivateKey; + type PublicKey: PublicKey::Signature>; type Validator: Validator; type ValidatorSet: ValidatorSet; type Value: Value; @@ -20,6 +22,11 @@ where // FIXME: Remove altogether const DUMMY_VALUE: Self::Value; + fn sign_vote( + vote: &Self::Vote, + private_key: &Self::PrivateKey, + ) -> ::Signature; + /// Build a new proposal for the given value at the given height, round and POL round. fn new_proposal( height: Self::Height, diff --git a/Code/common/src/lib.rs b/Code/common/src/lib.rs index 4da2130a2..fb4adadb3 100644 --- a/Code/common/src/lib.rs +++ b/Code/common/src/lib.rs @@ -1,5 +1,6 @@ //! Common data types and abstractions for the consensus engine. +#![no_std] #![forbid(unsafe_code)] #![deny(unused_crate_dependencies, trivial_casts, trivial_numeric_casts)] #![warn( @@ -13,6 +14,7 @@ mod consensus; mod height; mod proposal; +mod public_key; mod round; mod timeout; mod validator_set; @@ -25,8 +27,9 @@ pub type ValueId = <::Value as Value>::Id; pub use consensus::Consensus; pub use height::Height; pub use proposal::Proposal; +pub use public_key::{PrivateKey, PublicKey}; pub use round::Round; pub use timeout::{Timeout, TimeoutStep}; -pub use validator_set::{Address, PublicKey, Validator, ValidatorSet, VotingPower}; +pub use validator_set::{Address, Validator, ValidatorSet, VotingPower}; pub use value::Value; pub use vote::{Vote, VoteType}; diff --git a/Code/common/src/public_key.rs b/Code/common/src/public_key.rs new file mode 100644 index 000000000..7808f0c5c --- /dev/null +++ b/Code/common/src/public_key.rs @@ -0,0 +1,22 @@ +use core::fmt::Debug; + +use signature::{Signer, Verifier}; + +/// Defines the requirements for a private key type. +pub trait PrivateKey +where + Self: Clone + Debug + Signer, +{ + type Signature: Clone + Debug + PartialEq + Eq; + type PublicKey: PublicKey; + + fn public_key(&self) -> Self::PublicKey; +} + +/// Defines the requirements for a public key type. +pub trait PublicKey +where + Self: Clone + Debug + PartialEq + Eq + Verifier, +{ + type Signature: Clone + Debug + PartialEq + Eq; +} diff --git a/Code/common/src/round.rs b/Code/common/src/round.rs index a281a95c5..79fd567a6 100644 --- a/Code/common/src/round.rs +++ b/Code/common/src/round.rs @@ -1,3 +1,5 @@ +use core::cmp; + /// A round number. /// /// Can be either: @@ -69,13 +71,13 @@ impl Round { } impl PartialOrd for Round { - fn partial_cmp(&self, other: &Self) -> Option { + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for Round { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { + fn cmp(&self, other: &Self) -> cmp::Ordering { self.as_i64().cmp(&other.as_i64()) } } diff --git a/Code/common/src/validator_set.rs b/Code/common/src/validator_set.rs index cd58f1698..db3756e56 100644 --- a/Code/common/src/validator_set.rs +++ b/Code/common/src/validator_set.rs @@ -7,13 +7,6 @@ use crate::Consensus; /// TODO: Do we need to abstract over this as well? pub type VotingPower = u64; -/// Defines the requirements for a public key type. -pub trait PublicKey -where - Self: Clone + Debug + PartialEq + Eq, -{ -} - /// Defines the requirements for an address. /// /// TODO: Keep this trait or just add the bounds to Consensus::Address? diff --git a/Code/consensus/src/executor.rs b/Code/consensus/src/executor.rs index f9aef1d15..e139a5153 100644 --- a/Code/consensus/src/executor.rs +++ b/Code/consensus/src/executor.rs @@ -1,8 +1,8 @@ use std::collections::BTreeMap; use malachite_common::{ - Consensus, Proposal, Round, Timeout, TimeoutStep, Validator, ValidatorSet, Value, Vote, - VoteType, + Consensus, PrivateKey, Proposal, Round, Timeout, TimeoutStep, Validator, ValidatorSet, Value, + Vote, VoteType, }; use malachite_round::events::Event as RoundEvent; use malachite_round::message::Message as RoundMessage; @@ -19,7 +19,7 @@ where C: Consensus, { height: C::Height, - key: C::PublicKey, + key: C::PrivateKey, validator_set: C::ValidatorSet, round: Round, votes: VoteKeeper, @@ -30,7 +30,7 @@ impl Executor where C: Consensus, { - pub fn new(height: C::Height, validator_set: C::ValidatorSet, key: C::PublicKey) -> Self { + pub fn new(height: C::Height, validator_set: C::ValidatorSet, key: C::PrivateKey) -> Self { let votes = VoteKeeper::new( height.clone(), Round::INITIAL, @@ -76,12 +76,12 @@ where RoundMessage::Vote(vote) => { let address = self .validator_set - .get_by_public_key(&self.key)? + .get_by_public_key(&self.key.public_key())? .address() .clone(); - // TODO: sign the vote - let signed_vote = SignedVote::new(vote, address); + let signature = C::sign_vote(&vote, &self.key); + let signed_vote = SignedVote::new(vote, address, signature); Some(Message::Vote(signed_vote)) } @@ -110,7 +110,7 @@ where fn apply_new_round(&mut self, round: Round) -> Option> { let proposer = self.validator_set.get_proposer(); - let event = if proposer.public_key() == &self.key { + let event = if proposer.public_key() == &self.key.public_key() { let value = self.get_value(); RoundEvent::NewRoundProposer(value) } else { diff --git a/Code/consensus/src/signed_vote.rs b/Code/consensus/src/signed_vote.rs index 17797a3c9..57bfda0a1 100644 --- a/Code/consensus/src/signed_vote.rs +++ b/Code/consensus/src/signed_vote.rs @@ -1,4 +1,4 @@ -use malachite_common::Consensus; +use malachite_common::{Consensus, PublicKey}; // TODO: Do we need to abstract over `SignedVote` as well? @@ -9,15 +9,22 @@ where { pub vote: C::Vote, pub address: C::Address, - // TODO - // pub signature: C::Signature, + pub signature: ::Signature, } impl SignedVote where C: Consensus, { - pub fn new(vote: C::Vote, address: C::Address) -> Self { - Self { vote, address } + pub fn new( + vote: C::Vote, + address: C::Address, + signature: ::Signature, + ) -> Self { + Self { + vote, + address, + signature, + } } } diff --git a/Code/test/Cargo.toml b/Code/test/Cargo.toml index 43da1d089..536932fdb 100644 --- a/Code/test/Cargo.toml +++ b/Code/test/Cargo.toml @@ -1,12 +1,15 @@ [package] -name = "malachite-test" +name = "malachite-test" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] -malachite-common = { version = "0.1.0", path = "../common" } +malachite-common = { version = "0.1.0", path = "../common" } malachite-consensus = { version = "0.1.0", path = "../consensus" } -malachite-round = { version = "0.1.0", path = "../round" } -malachite-vote = { version = "0.1.0", path = "../vote" } +malachite-round = { version = "0.1.0", path = "../round" } +malachite-vote = { version = "0.1.0", path = "../vote" } + +ed25519-consensus = "2.1.0" +signature = "2.1.0" +rand = { version = "0.8.5", features = ["std_rng"] } +sha2 = "0.10.8" diff --git a/Code/test/src/consensus.rs b/Code/test/src/consensus.rs index 0dd72ac27..843fcb1b5 100644 --- a/Code/test/src/consensus.rs +++ b/Code/test/src/consensus.rs @@ -3,6 +3,7 @@ use malachite_common::Round; use crate::height::*; use crate::proposal::*; +use crate::public_key::{Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature}; use crate::validator_set::*; use crate::value::*; use crate::vote::*; @@ -14,7 +15,8 @@ impl Consensus for TestConsensus { type Address = Address; type Height = Height; type Proposal = Proposal; - type PublicKey = PublicKey; + type PublicKey = Ed25519PublicKey; + type PrivateKey = Ed25519PrivateKey; type ValidatorSet = ValidatorSet; type Validator = Validator; type Value = Value; @@ -22,6 +24,11 @@ impl Consensus for TestConsensus { const DUMMY_VALUE: Self::Value = Value::new(9999); + fn sign_vote(vote: &Self::Vote, private_key: &Self::PrivateKey) -> Ed25519Signature { + use signature::Signer; + private_key.sign(&vote.to_bytes()) + } + fn new_proposal(height: Height, round: Round, value: Value, pol_round: Round) -> Proposal { Proposal::new(height, round, value, pol_round) } diff --git a/Code/test/src/lib.rs b/Code/test/src/lib.rs index 4e9cd3aeb..c08d0d1de 100644 --- a/Code/test/src/lib.rs +++ b/Code/test/src/lib.rs @@ -4,6 +4,7 @@ mod consensus; mod height; mod proposal; +mod public_key; mod validator_set; mod value; mod vote; @@ -11,6 +12,7 @@ mod vote; pub use crate::consensus::*; pub use crate::height::*; pub use crate::proposal::*; +pub use crate::public_key::*; pub use crate::validator_set::*; pub use crate::value::*; pub use crate::vote::*; diff --git a/Code/test/src/public_key.rs b/Code/test/src/public_key.rs new file mode 100644 index 000000000..51a35b8a6 --- /dev/null +++ b/Code/test/src/public_key.rs @@ -0,0 +1,68 @@ +use ed25519_consensus::{Signature, SigningKey, VerificationKey}; + +use malachite_common::{PrivateKey, PublicKey}; +use rand::{CryptoRng, RngCore}; +use signature::{Signer, Verifier}; + +pub type Ed25519Signature = Signature; + +#[derive(Clone, Debug)] +pub struct Ed25519PrivateKey(SigningKey); + +impl Ed25519PrivateKey { + pub fn generate(rng: R) -> Self + where + R: RngCore + CryptoRng, + { + let signing_key = SigningKey::new(rng); + + Self(signing_key) + } + + pub fn public_key(&self) -> Ed25519PublicKey { + Ed25519PublicKey::new(self.0.verification_key()) + } +} + +impl PrivateKey for Ed25519PrivateKey { + type Signature = Signature; + type PublicKey = Ed25519PublicKey; + + fn public_key(&self) -> Self::PublicKey { + self.public_key() + } +} + +impl Signer for Ed25519PrivateKey { + fn try_sign(&self, msg: &[u8]) -> Result { + Ok(self.0.sign(msg)) + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Ed25519PublicKey(VerificationKey); + +impl Ed25519PublicKey { + pub fn new(key: impl Into) -> Self { + Self(key.into()) + } + + pub fn hash(&self) -> [u8; 32] { + use sha2::{Digest, Sha256}; + let mut hasher = Sha256::new(); + hasher.update(self.0.as_bytes()); + hasher.finalize().into() + } +} + +impl PublicKey for Ed25519PublicKey { + type Signature = Signature; +} + +impl Verifier for Ed25519PublicKey { + fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), signature::Error> { + self.0 + .verify(signature, msg) + .map_err(|_| signature::Error::new()) + } +} diff --git a/Code/test/src/validator_set.rs b/Code/test/src/validator_set.rs index e50b9a781..841042678 100644 --- a/Code/test/src/validator_set.rs +++ b/Code/test/src/validator_set.rs @@ -2,30 +2,24 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use malachite_common::VotingPower; -use crate::TestConsensus; - -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct PublicKey(Vec); - -impl PublicKey { - pub const fn new(value: Vec) -> Self { - Self(value) - } - - pub fn hash(&self) -> u64 { - self.0.iter().fold(0, |acc, x| acc ^ *x as u64) - } -} - -impl malachite_common::PublicKey for PublicKey {} +use crate::{Ed25519PublicKey, TestConsensus}; #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct Address(u64); +pub struct Address([u8; Self::LENGTH]); impl Address { - pub const fn new(value: u64) -> Self { + const LENGTH: usize = 20; + + pub const fn new(value: [u8; Self::LENGTH]) -> Self { Self(value) } + + pub fn from_public_key(public_key: &Ed25519PublicKey) -> Self { + let hash = public_key.hash(); + let mut address = [0; Self::LENGTH]; + address.copy_from_slice(&hash[..Self::LENGTH]); + Self(address) + } } impl malachite_common::Address for Address {} @@ -34,22 +28,18 @@ impl malachite_common::Address for Address {} #[derive(Clone, Debug, PartialEq, Eq)] pub struct Validator { pub address: Address, - pub public_key: PublicKey, + pub public_key: Ed25519PublicKey, pub voting_power: VotingPower, } impl Validator { - pub fn new(public_key: PublicKey, voting_power: VotingPower) -> Self { + pub fn new(public_key: Ed25519PublicKey, voting_power: VotingPower) -> Self { Self { - address: Address(public_key.hash()), + address: Address::from_public_key(&public_key), public_key, voting_power, } } - - pub fn hash(&self) -> u64 { - self.public_key.hash() // TODO - } } impl malachite_common::Validator for Validator { @@ -57,7 +47,7 @@ impl malachite_common::Validator for Validator { &self.address } - fn public_key(&self) -> &PublicKey { + fn public_key(&self) -> &Ed25519PublicKey { &self.public_key } @@ -108,9 +98,7 @@ impl ValidatorSet { v.voting_power = val.voting_power; } - dbg!(self.total_voting_power()); Self::sort_validators(&mut self.validators); - dbg!(self.total_voting_power()); } /// Remove a validator from the set @@ -125,17 +113,22 @@ impl ValidatorSet { self.validators.iter().find(|v| &v.address == address) } - pub fn get_by_public_key(&self, public_key: &PublicKey) -> Option<&Validator> { + pub fn get_by_public_key(&self, public_key: &Ed25519PublicKey) -> Option<&Validator> { self.validators.iter().find(|v| &v.public_key == public_key) } /// In place sort and deduplication of a list of validators fn sort_validators(vals: &mut Vec) { - use core::cmp::Reverse; - // Sort the validators according to the current Tendermint requirements + // + // use core::cmp::Reverse; + // // (v. 0.34 -> first by validator power, descending, then by address, ascending) - vals.sort_unstable_by_key(|v| (Reverse(v.voting_power), v.address)); + // vals.sort_unstable_by(|v1, v2| { + // let a = (Reverse(v1.voting_power), &v1.address); + // let b = (Reverse(v2.voting_power), &v2.address); + // a.cmp(&b) + // }); vals.dedup(); } @@ -154,7 +147,7 @@ impl malachite_common::ValidatorSet for ValidatorSet { self.total_voting_power() } - fn get_by_public_key(&self, public_key: &PublicKey) -> Option<&Validator> { + fn get_by_public_key(&self, public_key: &Ed25519PublicKey) -> Option<&Validator> { self.get_by_public_key(public_key) } @@ -169,22 +162,36 @@ impl malachite_common::ValidatorSet for ValidatorSet { #[cfg(test)] mod tests { + use rand::rngs::StdRng; + use rand::SeedableRng; + use super::*; + use crate::Ed25519PrivateKey; + #[test] fn add_update_remove() { - let v1 = Validator::new(PublicKey(vec![1]), 1); - let v2 = Validator::new(PublicKey(vec![2]), 2); - let v3 = Validator::new(PublicKey(vec![3]), 3); + let mut rng = StdRng::seed_from_u64(0x42); + + let sk1 = Ed25519PrivateKey::generate(&mut rng); + let sk2 = Ed25519PrivateKey::generate(&mut rng); + let sk3 = Ed25519PrivateKey::generate(&mut rng); + let sk4 = Ed25519PrivateKey::generate(&mut rng); + let sk5 = Ed25519PrivateKey::generate(&mut rng); + let sk6 = Ed25519PrivateKey::generate(&mut rng); + + let v1 = Validator::new(sk1.public_key(), 1); + let v2 = Validator::new(sk2.public_key(), 2); + let v3 = Validator::new(sk3.public_key(), 3); let mut vs = ValidatorSet::new(vec![v1, v2, v3]); assert_eq!(vs.total_voting_power(), 6); - let v4 = Validator::new(PublicKey(vec![4]), 4); + let v4 = Validator::new(sk4.public_key(), 4); vs.add(v4); assert_eq!(vs.total_voting_power(), 10); - let mut v5 = Validator::new(PublicKey(vec![5]), 5); + let mut v5 = Validator::new(sk5.public_key(), 5); vs.update(v5.clone()); // no effect assert_eq!(vs.total_voting_power(), 10); @@ -198,7 +205,7 @@ mod tests { vs.remove(&v5.address); assert_eq!(vs.total_voting_power(), 10); - let v6 = Validator::new(PublicKey(vec![6]), 6); + let v6 = Validator::new(sk6.public_key(), 6); vs.remove(&v6.address); // no effect assert_eq!(vs.total_voting_power(), 10); } diff --git a/Code/test/src/value.rs b/Code/test/src/value.rs index 2f45f6abe..3954d2868 100644 --- a/Code/test/src/value.rs +++ b/Code/test/src/value.rs @@ -5,6 +5,10 @@ impl ValueId { pub const fn new(id: u64) -> Self { Self(id) } + + pub const fn as_u64(&self) -> u64 { + self.0 + } } /// The value to decide on diff --git a/Code/test/src/vote.rs b/Code/test/src/vote.rs index ec883b28f..05b62270a 100644 --- a/Code/test/src/vote.rs +++ b/Code/test/src/vote.rs @@ -1,7 +1,8 @@ use malachite_common::{Round, VoteType}; use malachite_consensus::signed_vote::SignedVote; +use signature::Signer; -use crate::{Address, TestConsensus, ValueId}; +use crate::{Address, Ed25519PrivateKey, TestConsensus, ValueId}; /// A vote for a value in a round #[derive(Clone, Debug, PartialEq, Eq)] @@ -28,10 +29,32 @@ impl Vote { } } - pub fn signed(self, address: Address) -> SignedVote { + // TODO: Use the canonical vote + pub fn to_bytes(&self) -> Vec { + let vtpe = match self.typ { + VoteType::Prevote => 0, + VoteType::Precommit => 1, + }; + + let mut bytes = vec![vtpe]; + bytes.extend_from_slice(&self.round.as_i64().to_be_bytes()); + bytes.extend_from_slice( + &self + .value + .map(|v| v.as_u64().to_be_bytes()) + .unwrap_or_default(), + ); + bytes + } + + pub fn signed(self, private_key: &Ed25519PrivateKey) -> SignedVote { + let address = Address::from_public_key(&private_key.public_key()); + let signature = private_key.sign(&self.to_bytes()); + SignedVote { vote: self, address, + signature, } } } diff --git a/Code/test/tests/consensus_executor.rs b/Code/test/tests/consensus_executor.rs index c62b4c568..71cff7c76 100644 --- a/Code/test/tests/consensus_executor.rs +++ b/Code/test/tests/consensus_executor.rs @@ -3,7 +3,11 @@ use malachite_consensus::executor::Executor; use malachite_consensus::message::Message; use malachite_round::state::{RoundValue, State, Step}; -use malachite_test::{Height, Proposal, PublicKey, TestConsensus, Validator, ValidatorSet, Vote}; +use malachite_test::{ + Ed25519PrivateKey, Height, Proposal, TestConsensus, Validator, ValidatorSet, Vote, +}; +use rand::rngs::StdRng; +use rand::SeedableRng; struct TestStep { desc: &'static str, @@ -16,20 +20,26 @@ struct TestStep { fn executor_steps_proposer() { let value = TestConsensus::DUMMY_VALUE; // TODO: get value from external source let value_id = value.id(); - let v1 = Validator::new(PublicKey::new(vec![1]), 1); - let v2 = Validator::new(PublicKey::new(vec![2]), 1); - let v3 = Validator::new(PublicKey::new(vec![3]), 1); - let my_address = v1.address; - let key = v1.clone().public_key; // we are proposer + + let mut rng = StdRng::seed_from_u64(0x42); + + let sk1 = Ed25519PrivateKey::generate(&mut rng); + let sk2 = Ed25519PrivateKey::generate(&mut rng); + let sk3 = Ed25519PrivateKey::generate(&mut rng); + + let v1 = Validator::new(sk1.public_key(), 1); + let v2 = Validator::new(sk2.public_key(), 2); + let v3 = Validator::new(sk3.public_key(), 3); + + let my_sk = sk1; let vs = ValidatorSet::new(vec![v1, v2.clone(), v3.clone()]); - let mut executor = Executor::new(Height::new(1), vs, key.clone()); + let mut executor = Executor::new(Height::new(1), vs, my_sk.clone()); let proposal = Proposal::new(Height::new(1), Round::new(0), value.clone(), Round::new(-1)); let steps = vec![ - // Start round 0, we are proposer, propose value TestStep { desc: "Start round 0, we are proposer, propose value", input_message: Some(Message::NewRound(Round::new(0))), @@ -43,12 +53,11 @@ fn executor_steps_proposer() { valid: None, }, }, - // Receive our own proposal, prevote for it (v1) TestStep { desc: "Receive our own proposal, prevote for it (v1)", input_message: None, expected_output_message: Some(Message::Vote( - Vote::new_prevote(Round::new(0), Some(value_id)).signed(my_address), + Vote::new_prevote(Round::new(0), Some(value_id)).signed(&my_sk), )), new_state: State { height: Height::new(1), @@ -59,7 +68,6 @@ fn executor_steps_proposer() { valid: None, }, }, - // Receive our own prevote v1 TestStep { desc: "Receive our own prevote v1", input_message: None, @@ -73,11 +81,10 @@ fn executor_steps_proposer() { valid: None, }, }, - // v2 prevotes for our proposal TestStep { desc: "v2 prevotes for our proposal", input_message: Some(Message::Vote( - Vote::new_prevote(Round::new(0), Some(value_id)).signed(v2.address), + Vote::new_prevote(Round::new(0), Some(value_id)).signed(&sk2), )), expected_output_message: None, new_state: State { @@ -89,14 +96,13 @@ fn executor_steps_proposer() { valid: None, }, }, - // v3 prevotes for our proposal, we get +2/3 prevotes, precommit for it (v1) TestStep { desc: "v3 prevotes for our proposal, we get +2/3 prevotes, precommit for it (v1)", input_message: Some(Message::Vote( - Vote::new_prevote(Round::new(0), Some(value_id)).signed(v3.address), + Vote::new_prevote(Round::new(0), Some(value_id)).signed(&sk3), )), expected_output_message: Some(Message::Vote( - Vote::new_precommit(Round::new(0), Some(value_id)).signed(my_address), + Vote::new_precommit(Round::new(0), Some(value_id)).signed(&my_sk), )), new_state: State { height: Height::new(1), @@ -113,7 +119,6 @@ fn executor_steps_proposer() { }), }, }, - // v1 receives its own precommit TestStep { desc: "v1 receives its own precommit", input_message: None, @@ -133,11 +138,10 @@ fn executor_steps_proposer() { }), }, }, - // v2 precommits for our proposal TestStep { desc: "v2 precommits for our proposal", input_message: Some(Message::Vote( - Vote::new_precommit(Round::new(0), Some(value_id)).signed(v2.address), + Vote::new_precommit(Round::new(0), Some(value_id)).signed(&sk2), )), expected_output_message: None, new_state: State { @@ -155,11 +159,10 @@ fn executor_steps_proposer() { }), }, }, - // v3 precommits for our proposal, we get +2/3 precommits, decide it (v1) TestStep { desc: "v3 precommits for our proposal, we get +2/3 precommits, decide it (v1)", input_message: Some(Message::Vote( - Vote::new_precommit(Round::new(0), Some(value_id)).signed(v2.address), + Vote::new_precommit(Round::new(0), Some(value_id)).signed(&sk2), )), expected_output_message: None, new_state: State { @@ -182,6 +185,8 @@ fn executor_steps_proposer() { let mut previous_message = None; for step in steps { + println!("Step: {}", step.desc); + let execute_message = step .input_message .unwrap_or_else(|| previous_message.unwrap()); @@ -201,21 +206,25 @@ fn executor_steps_not_proposer() { let value = TestConsensus::DUMMY_VALUE; // TODO: get value from external source let value_id = value.id(); - let v1 = Validator::new(PublicKey::new(vec![1]), 1); - let v2 = Validator::new(PublicKey::new(vec![2]), 1); - let v3 = Validator::new(PublicKey::new(vec![3]), 1); + let mut rng = StdRng::seed_from_u64(0x42); + + let sk1 = Ed25519PrivateKey::generate(&mut rng); + let sk2 = Ed25519PrivateKey::generate(&mut rng); + let sk3 = Ed25519PrivateKey::generate(&mut rng); + + let v1 = Validator::new(sk1.public_key(), 1); + let v2 = Validator::new(sk2.public_key(), 2); + let v3 = Validator::new(sk3.public_key(), 3); // Proposer is v1, so we are not the proposer - let my_address = v2.address; - let my_key = v2.public_key.clone(); + let my_sk = &sk2; let vs = ValidatorSet::new(vec![v1.clone(), v2.clone(), v3.clone()]); - let mut executor = Executor::new(Height::new(1), vs, my_key); + let mut executor = Executor::new(Height::new(1), vs, my_sk.clone()); let proposal = Proposal::new(Height::new(1), Round::new(0), value.clone(), Round::new(-1)); let steps = vec![ - // Start round 0, we are not the proposer TestStep { desc: "Start round 0, we are not the proposer", input_message: Some(Message::NewRound(Round::new(0))), @@ -229,12 +238,11 @@ fn executor_steps_not_proposer() { valid: None, }, }, - // Receive a proposal, prevote for it (v1) TestStep { - desc: "Receive a proposal, prevote for it (v1)", + desc: "Receive a proposal, prevote for it (v2)", input_message: Some(Message::Proposal(proposal.clone())), expected_output_message: Some(Message::Vote( - Vote::new_prevote(Round::new(0), Some(value_id)).signed(my_address), + Vote::new_prevote(Round::new(0), Some(value_id)).signed(my_sk), )), new_state: State { height: Height::new(1), @@ -245,9 +253,8 @@ fn executor_steps_not_proposer() { valid: None, }, }, - // Receive our own prevote v1 TestStep { - desc: "Receive our own prevote v1", + desc: "Receive our own prevote", input_message: None, expected_output_message: None, new_state: State { @@ -259,11 +266,10 @@ fn executor_steps_not_proposer() { valid: None, }, }, - // v2 prevotes for its own proposal TestStep { desc: "v2 prevotes for its own proposal", input_message: Some(Message::Vote( - Vote::new_prevote(Round::new(0), Some(value_id)).signed(v2.address), + Vote::new_prevote(Round::new(0), Some(value_id)).signed(&sk2), )), expected_output_message: None, new_state: State { @@ -275,14 +281,13 @@ fn executor_steps_not_proposer() { valid: None, }, }, - // v3 prevotes for v2's proposal, it gets +2/3 prevotes, precommit for it (v1) TestStep { - desc: "v3 prevotes for v2's proposal, it gets +2/3 prevotes, precommit for it (v1)", + desc: "v3 prevotes for v2's proposal, it gets +2/3 prevotes, precommit for it (v2)", input_message: Some(Message::Vote( - Vote::new_prevote(Round::new(0), Some(value_id)).signed(v3.address), + Vote::new_prevote(Round::new(0), Some(value_id)).signed(&sk3), )), expected_output_message: Some(Message::Vote( - Vote::new_precommit(Round::new(0), Some(value_id)).signed(my_address), + Vote::new_precommit(Round::new(0), Some(value_id)).signed(my_sk), )), new_state: State { height: Height::new(1), @@ -299,9 +304,8 @@ fn executor_steps_not_proposer() { }), }, }, - // v1 receives its own precommit TestStep { - desc: "v1 receives its own precommit", + desc: "we receive our own precommit", input_message: None, expected_output_message: None, new_state: State { @@ -319,11 +323,10 @@ fn executor_steps_not_proposer() { }), }, }, - // v2 precommits its proposal TestStep { - desc: "v2 precommits its proposal", + desc: "v1 precommits its proposal", input_message: Some(Message::Vote( - Vote::new_precommit(Round::new(0), Some(value_id)).signed(v2.address), + Vote::new_precommit(Round::new(0), Some(value_id)).signed(&sk1), )), expected_output_message: None, new_state: State { @@ -341,11 +344,10 @@ fn executor_steps_not_proposer() { }), }, }, - // v3 precommits for v2's proposal, it gets +2/3 precommits, decide it (v1) TestStep { - desc: "v3 precommits for v2's proposal, it gets +2/3 precommits, decide it (v1)", + desc: "v3 precommits for v1's proposal, it gets +2/3 precommits, decide it", input_message: Some(Message::Vote( - Vote::new_precommit(Round::new(0), Some(value_id)).signed(v2.address), + Vote::new_precommit(Round::new(0), Some(value_id)).signed(&sk3), )), expected_output_message: None, new_state: State { @@ -387,16 +389,21 @@ fn executor_steps_not_proposer_timeout() { let value = TestConsensus::DUMMY_VALUE; // TODO: get value from external source let value_id = value.id(); - let v1 = Validator::new(PublicKey::new(vec![1]), 1); - let v2 = Validator::new(PublicKey::new(vec![2]), 1); - let v3 = Validator::new(PublicKey::new(vec![3]), 2); + let mut rng = StdRng::seed_from_u64(0x42); + + let sk1 = Ed25519PrivateKey::generate(&mut rng); + let sk2 = Ed25519PrivateKey::generate(&mut rng); + let sk3 = Ed25519PrivateKey::generate(&mut rng); + + let v1 = Validator::new(sk1.public_key(), 1); + let v2 = Validator::new(sk2.public_key(), 1); + let v3 = Validator::new(sk3.public_key(), 3); // Proposer is v1, so we are not the proposer - let my_address = v2.address; - let my_key = v2.public_key.clone(); + let my_sk = sk2; let vs = ValidatorSet::new(vec![v1.clone(), v2.clone(), v3.clone()]); - let mut executor = Executor::new(Height::new(1), vs, my_key); + let mut executor = Executor::new(Height::new(1), vs, my_sk.clone()); let steps = vec![ // Start round 0, we are not the proposer @@ -418,7 +425,7 @@ fn executor_steps_not_proposer_timeout() { desc: "Receive a propose timeout, prevote for nil (v1)", input_message: Some(Message::Timeout(Timeout::propose(Round::new(0)))), expected_output_message: Some(Message::Vote( - Vote::new_prevote(Round::new(0), None).signed(my_address), + Vote::new_prevote(Round::new(0), None).signed(&my_sk), )), new_state: State { height: Height::new(1), @@ -447,7 +454,7 @@ fn executor_steps_not_proposer_timeout() { TestStep { desc: "v2 prevotes for its own proposal", input_message: Some(Message::Vote( - Vote::new_prevote(Round::new(0), Some(value_id)).signed(v2.address), + Vote::new_prevote(Round::new(0), Some(value_id)).signed(&sk1), )), expected_output_message: None, new_state: State { @@ -463,10 +470,10 @@ fn executor_steps_not_proposer_timeout() { TestStep { desc: "v3 prevotes for nil, it gets +2/3 prevotes, precommit for it (v1)", input_message: Some(Message::Vote( - Vote::new_prevote(Round::new(0), None).signed(v3.address), + Vote::new_prevote(Round::new(0), None).signed(&sk3), )), expected_output_message: Some(Message::Vote( - Vote::new_precommit(Round::new(0), None).signed(my_address), + Vote::new_precommit(Round::new(0), None).signed(&my_sk), )), new_state: State { height: Height::new(1), @@ -495,7 +502,7 @@ fn executor_steps_not_proposer_timeout() { TestStep { desc: "v2 precommits its proposal", input_message: Some(Message::Vote( - Vote::new_precommit(Round::new(0), Some(value_id)).signed(v2.address), + Vote::new_precommit(Round::new(0), Some(value_id)).signed(&sk1), )), expected_output_message: None, new_state: State { @@ -511,7 +518,7 @@ fn executor_steps_not_proposer_timeout() { TestStep { desc: "v3 precommits for nil", input_message: Some(Message::Vote( - Vote::new_precommit(Round::new(0), None).signed(v3.address), + Vote::new_precommit(Round::new(0), None).signed(&sk3), )), expected_output_message: None, new_state: State {