From 757ab72d4e25e34ce585e278d1fa41bb8a9e5225 Mon Sep 17 00:00:00 2001 From: Romain Ruetschi Date: Wed, 25 Oct 2023 12:00:39 +0200 Subject: [PATCH 1/6] Introduce `SignedVote` type and remove address from round `Vote` --- Code/QUESTIONS.md | 1 - Code/TODO.md | 4 +- Code/common/src/consensus.rs | 15 +-- Code/common/src/vote.rs | 4 - Code/consensus/src/executor.rs | 37 +++---- Code/consensus/src/lib.rs | 2 + Code/consensus/src/message.rs | 15 +++ Code/consensus/src/signed_vote.rs | 23 ++++ Code/round/src/message.rs | 8 +- Code/round/src/state_machine.rs | 8 +- Code/test/src/consensus.rs | 10 +- Code/test/src/vote.rs | 21 ++-- Code/test/tests/consensus_executor.rs | 147 ++++++++++---------------- Code/test/tests/round.rs | 8 +- Code/test/tests/vote_count.rs | 14 +-- Code/test/tests/vote_keeper.rs | 14 +-- 16 files changed, 151 insertions(+), 180 deletions(-) create mode 100644 Code/consensus/src/message.rs create mode 100644 Code/consensus/src/signed_vote.rs diff --git a/Code/QUESTIONS.md b/Code/QUESTIONS.md index cd292d13e..700c9fae4 100644 --- a/Code/QUESTIONS.md +++ b/Code/QUESTIONS.md @@ -1,2 +1 @@ - How do we deal with errors? -- How do we parametrize over values, id(v), etc. diff --git a/Code/TODO.md b/Code/TODO.md index 44d4c2a0b..8a7878e9e 100644 --- a/Code/TODO.md +++ b/Code/TODO.md @@ -8,6 +8,4 @@ if complete proposal from a past round => to current one if we have some threshold (f+1) of votes for a future round => skip to that round context (get proposer, get value) -signing contextt - -abstract over values and validator sets +signing context diff --git a/Code/common/src/consensus.rs b/Code/common/src/consensus.rs index 0fc01eaac..bcf906989 100644 --- a/Code/common/src/consensus.rs +++ b/Code/common/src/consensus.rs @@ -17,9 +17,6 @@ where type Value: Value; type Vote: Vote; - // FIXME: Remove this and thread it through where necessary - const DUMMY_ADDRESS: Self::Address; - // FIXME: Remove altogether const DUMMY_VALUE: Self::Value; @@ -33,17 +30,9 @@ where /// Build a new prevote vote by the validator with the given address, /// for the value identified by the given value id, at the given round. - fn new_prevote( - round: Round, - value_id: Option>, - address: Self::Address, - ) -> Self::Vote; + fn new_prevote(round: Round, value_id: Option>) -> Self::Vote; /// Build a new precommit vote by the validator with the given address, /// for the value identified by the given value id, at the given round. - fn new_precommit( - round: Round, - value_id: Option>, - address: Self::Address, - ) -> Self::Vote; + fn new_precommit(round: Round, value_id: Option>) -> Self::Vote; } diff --git a/Code/common/src/vote.rs b/Code/common/src/vote.rs index 6dd388d13..9a593ca05 100644 --- a/Code/common/src/vote.rs +++ b/Code/common/src/vote.rs @@ -28,8 +28,4 @@ where /// The type of vote. fn vote_type(&self) -> VoteType; - - // FIXME: round message votes should not include address - fn address(&self) -> &C::Address; - fn set_address(&mut self, address: C::Address); } diff --git a/Code/consensus/src/executor.rs b/Code/consensus/src/executor.rs index 7add9bee1..ac9408547 100644 --- a/Code/consensus/src/executor.rs +++ b/Code/consensus/src/executor.rs @@ -10,6 +10,9 @@ use malachite_round::state::State as RoundState; use malachite_vote::count::Threshold; use malachite_vote::keeper::VoteKeeper; +use crate::message::Message; +use crate::signed_vote::SignedVote; + #[derive(Clone, Debug)] pub struct Executor where @@ -23,17 +26,6 @@ where round_states: BTreeMap>, } -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum Message -where - C: Consensus, -{ - NewRound(Round), - Proposal(C::Proposal), - Vote(C::Vote), - Timeout(Timeout), -} - impl Executor where C: Consensus, @@ -81,19 +73,17 @@ where Some(Message::Proposal(p)) } - RoundMessage::Vote(mut v) => { - // sign the vote - - // FIXME: round message votes should not include address + RoundMessage::Vote(vote) => { let address = self .validator_set .get_by_public_key(&self.key)? .address() .clone(); - v.set_address(address); + // TODO: sign the vote + let signed_vote = SignedVote::new(vote, address); - Some(Message::Vote(v)) + Some(Message::Vote(signed_vote)) } RoundMessage::Timeout(_) => { @@ -112,7 +102,7 @@ where match msg { Message::NewRound(round) => self.apply_new_round(round), Message::Proposal(proposal) => self.apply_proposal(proposal), - Message::Vote(vote) => self.apply_vote(vote), + Message::Vote(signed_vote) => self.apply_vote(signed_vote), Message::Timeout(timeout) => self.apply_timeout(timeout), } } @@ -177,15 +167,18 @@ where } } - fn apply_vote(&mut self, vote: C::Vote) -> Option> { - let Some(validator) = self.validator_set.get_by_address(vote.address()) else { + fn apply_vote(&mut self, signed_vote: SignedVote) -> Option> { + let Some(validator) = self.validator_set.get_by_address(&signed_vote.address) else { // TODO: Is this the correct behavior? How to log such "errors"? return None; }; - let round = vote.round(); + let round = signed_vote.vote.round(); - let event = match self.votes.apply_vote(vote, validator.voting_power()) { + let event = match self + .votes + .apply_vote(signed_vote.vote, validator.voting_power()) + { Some(event) => event, None => return None, }; diff --git a/Code/consensus/src/lib.rs b/Code/consensus/src/lib.rs index 9f5b7756d..652688fa0 100644 --- a/Code/consensus/src/lib.rs +++ b/Code/consensus/src/lib.rs @@ -11,3 +11,5 @@ #![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::panic))] pub mod executor; +pub mod message; +pub mod signed_vote; diff --git a/Code/consensus/src/message.rs b/Code/consensus/src/message.rs new file mode 100644 index 000000000..f76cc44b4 --- /dev/null +++ b/Code/consensus/src/message.rs @@ -0,0 +1,15 @@ +use malachite_common::{Consensus, Round, Timeout}; + +use crate::signed_vote::SignedVote; + +/// Messages that can be received and broadcast by the consensus executor. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Message +where + C: Consensus, +{ + NewRound(Round), + Proposal(C::Proposal), + Vote(SignedVote), + Timeout(Timeout), +} diff --git a/Code/consensus/src/signed_vote.rs b/Code/consensus/src/signed_vote.rs new file mode 100644 index 000000000..17797a3c9 --- /dev/null +++ b/Code/consensus/src/signed_vote.rs @@ -0,0 +1,23 @@ +use malachite_common::Consensus; + +// TODO: Do we need to abstract over `SignedVote` as well? + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SignedVote +where + C: Consensus, +{ + pub vote: C::Vote, + pub address: C::Address, + // TODO + // pub signature: C::Signature, +} + +impl SignedVote +where + C: Consensus, +{ + pub fn new(vote: C::Vote, address: C::Address) -> Self { + Self { vote, address } + } +} diff --git a/Code/round/src/message.rs b/Code/round/src/message.rs index 2fb422cf1..35021ac38 100644 --- a/Code/round/src/message.rs +++ b/Code/round/src/message.rs @@ -34,12 +34,12 @@ where Message::Proposal(C::new_proposal(height, round, value, pol_round)) } - pub fn prevote(round: Round, value_id: Option>, address: C::Address) -> Self { - Message::Vote(C::new_prevote(round, value_id, address)) + pub fn prevote(round: Round, value_id: Option>) -> Self { + Message::Vote(C::new_prevote(round, value_id)) } - pub fn precommit(round: Round, value_id: Option>, address: C::Address) -> Self { - Message::Vote(C::new_precommit(round, value_id, address)) + pub fn precommit(round: Round, value_id: Option>) -> Self { + Message::Vote(C::new_precommit(round, value_id)) } pub fn timeout(round: Round, step: TimeoutStep) -> Self { diff --git a/Code/round/src/state_machine.rs b/Code/round/src/state_machine.rs index 884029d30..bc8bc6b69 100644 --- a/Code/round/src/state_machine.rs +++ b/Code/round/src/state_machine.rs @@ -171,7 +171,7 @@ where None => Some(proposed), // not locked, prevote the value }; - let message = Message::prevote(state.round, value, C::DUMMY_ADDRESS); + let message = Message::prevote(state.round, value); Transition::to(state.next_step()).with_message(message) } @@ -182,7 +182,7 @@ pub fn prevote_nil(state: State) -> Transition where C: Consensus, { - let message = Message::prevote(state.round, None, C::DUMMY_ADDRESS); + let message = Message::prevote(state.round, None); Transition::to(state.next_step()).with_message(message) } @@ -200,7 +200,7 @@ pub fn precommit(state: State, value_id: ValueId) -> Transition where C: Consensus, { - let message = Message::precommit(state.round, Some(value_id), C::DUMMY_ADDRESS); + let message = Message::precommit(state.round, Some(value_id)); let Some(value) = state .proposal @@ -223,7 +223,7 @@ pub fn precommit_nil(state: State) -> Transition where C: Consensus, { - let message = Message::precommit(state.round, None, C::DUMMY_ADDRESS); + let message = Message::precommit(state.round, None); Transition::to(state.next_step()).with_message(message) } diff --git a/Code/test/src/consensus.rs b/Code/test/src/consensus.rs index ebe110c5d..0dd72ac27 100644 --- a/Code/test/src/consensus.rs +++ b/Code/test/src/consensus.rs @@ -20,19 +20,17 @@ impl Consensus for TestConsensus { type Value = Value; type Vote = Vote; - const DUMMY_ADDRESS: Address = Address::new(42); - const DUMMY_VALUE: Self::Value = Value::new(9999); fn new_proposal(height: Height, round: Round, value: Value, pol_round: Round) -> Proposal { Proposal::new(height, round, value, pol_round) } - fn new_prevote(round: Round, value_id: Option, address: Address) -> Vote { - Vote::new_prevote(round, value_id, address) + fn new_prevote(round: Round, value_id: Option) -> Vote { + Vote::new_prevote(round, value_id) } - fn new_precommit(round: Round, value_id: Option, address: Address) -> Vote { - Vote::new_precommit(round, value_id, address) + fn new_precommit(round: Round, value_id: Option) -> Vote { + Vote::new_precommit(round, value_id) } } diff --git a/Code/test/src/vote.rs b/Code/test/src/vote.rs index 761101aeb..7f4424ecf 100644 --- a/Code/test/src/vote.rs +++ b/Code/test/src/vote.rs @@ -1,4 +1,5 @@ use malachite_common::{Round, VoteType}; +use malachite_consensus::signed_vote::SignedVote; use crate::{Address, TestConsensus, ValueId}; @@ -8,24 +9,28 @@ pub struct Vote { pub typ: VoteType, pub round: Round, pub value: Option, - pub address: Address, } impl Vote { - pub fn new_prevote(round: Round, value: Option, address: Address) -> Self { + pub fn new_prevote(round: Round, value: Option) -> Self { Self { typ: VoteType::Prevote, round, value, - address, } } - pub fn new_precommit(round: Round, value: Option, address: Address) -> Self { + pub fn new_precommit(round: Round, value: Option) -> Self { Self { typ: VoteType::Precommit, round, value, + } + } + + pub fn signed(self, address: Address) -> SignedVote { + SignedVote { + vote: self, address, } } @@ -43,12 +48,4 @@ impl malachite_common::Vote for Vote { fn vote_type(&self) -> VoteType { self.typ } - - fn address(&self) -> &Address { - &self.address - } - - fn set_address(&mut self, address: Address) { - self.address = address; - } } diff --git a/Code/test/tests/consensus_executor.rs b/Code/test/tests/consensus_executor.rs index fc26fe778..c62b4c568 100644 --- a/Code/test/tests/consensus_executor.rs +++ b/Code/test/tests/consensus_executor.rs @@ -1,5 +1,6 @@ use malachite_common::{Consensus, Round, Timeout}; -use malachite_consensus::executor::{Executor, Message}; +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}; @@ -46,11 +47,9 @@ fn executor_steps_proposer() { 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), - my_address, - ))), + expected_output_message: Some(Message::Vote( + Vote::new_prevote(Round::new(0), Some(value_id)).signed(my_address), + )), new_state: State { height: Height::new(1), round: Round::new(0), @@ -77,11 +76,9 @@ fn executor_steps_proposer() { // 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), - v2.address, - ))), + input_message: Some(Message::Vote( + Vote::new_prevote(Round::new(0), Some(value_id)).signed(v2.address), + )), expected_output_message: None, new_state: State { height: Height::new(1), @@ -95,16 +92,12 @@ fn executor_steps_proposer() { // 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), - v3.address, - ))), - expected_output_message: Some(Message::Vote(Vote::new_precommit( - Round::new(0), - Some(value_id), - my_address, - ))), + input_message: Some(Message::Vote( + Vote::new_prevote(Round::new(0), Some(value_id)).signed(v3.address), + )), + expected_output_message: Some(Message::Vote( + Vote::new_precommit(Round::new(0), Some(value_id)).signed(my_address), + )), new_state: State { height: Height::new(1), round: Round::new(0), @@ -143,11 +136,9 @@ 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), - v2.address, - ))), + input_message: Some(Message::Vote( + Vote::new_precommit(Round::new(0), Some(value_id)).signed(v2.address), + )), expected_output_message: None, new_state: State { height: Height::new(1), @@ -167,11 +158,9 @@ 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), - v2.address, - ))), + input_message: Some(Message::Vote( + Vote::new_precommit(Round::new(0), Some(value_id)).signed(v2.address), + )), expected_output_message: None, new_state: State { height: Height::new(1), @@ -244,11 +233,9 @@ fn executor_steps_not_proposer() { TestStep { desc: "Receive a proposal, prevote for it (v1)", input_message: Some(Message::Proposal(proposal.clone())), - expected_output_message: Some(Message::Vote(Vote::new_prevote( - Round::new(0), - Some(value_id), - my_address, - ))), + expected_output_message: Some(Message::Vote( + Vote::new_prevote(Round::new(0), Some(value_id)).signed(my_address), + )), new_state: State { height: Height::new(1), round: Round::new(0), @@ -275,11 +262,9 @@ fn executor_steps_not_proposer() { // 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), - v2.address, - ))), + input_message: Some(Message::Vote( + Vote::new_prevote(Round::new(0), Some(value_id)).signed(v2.address), + )), expected_output_message: None, new_state: State { height: Height::new(1), @@ -293,16 +278,12 @@ fn executor_steps_not_proposer() { // 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)", - input_message: Some(Message::Vote(Vote::new_prevote( - Round::new(0), - Some(value_id), - v3.address, - ))), - expected_output_message: Some(Message::Vote(Vote::new_precommit( - Round::new(0), - Some(value_id), - my_address, - ))), + input_message: Some(Message::Vote( + Vote::new_prevote(Round::new(0), Some(value_id)).signed(v3.address), + )), + expected_output_message: Some(Message::Vote( + Vote::new_precommit(Round::new(0), Some(value_id)).signed(my_address), + )), new_state: State { height: Height::new(1), round: Round::new(0), @@ -341,11 +322,9 @@ fn executor_steps_not_proposer() { // v2 precommits its proposal TestStep { desc: "v2 precommits its proposal", - input_message: Some(Message::Vote(Vote::new_precommit( - Round::new(0), - Some(value_id), - v2.address, - ))), + input_message: Some(Message::Vote( + Vote::new_precommit(Round::new(0), Some(value_id)).signed(v2.address), + )), expected_output_message: None, new_state: State { height: Height::new(1), @@ -365,11 +344,9 @@ 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)", - input_message: Some(Message::Vote(Vote::new_precommit( - Round::new(0), - Some(value_id), - v2.address, - ))), + input_message: Some(Message::Vote( + Vote::new_precommit(Round::new(0), Some(value_id)).signed(v2.address), + )), expected_output_message: None, new_state: State { height: Height::new(1), @@ -440,11 +417,9 @@ fn executor_steps_not_proposer_timeout() { TestStep { 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, - my_address, - ))), + expected_output_message: Some(Message::Vote( + Vote::new_prevote(Round::new(0), None).signed(my_address), + )), new_state: State { height: Height::new(1), round: Round::new(0), @@ -471,11 +446,9 @@ fn executor_steps_not_proposer_timeout() { // 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), - v2.address, - ))), + input_message: Some(Message::Vote( + Vote::new_prevote(Round::new(0), Some(value_id)).signed(v2.address), + )), expected_output_message: None, new_state: State { height: Height::new(1), @@ -489,16 +462,12 @@ fn executor_steps_not_proposer_timeout() { // v3 prevotes for nil, it gets +2/3 prevotes, precommit for it (v1) 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, - v3.address, - ))), - expected_output_message: Some(Message::Vote(Vote::new_precommit( - Round::new(0), - None, - my_address, - ))), + input_message: Some(Message::Vote( + Vote::new_prevote(Round::new(0), None).signed(v3.address), + )), + expected_output_message: Some(Message::Vote( + Vote::new_precommit(Round::new(0), None).signed(my_address), + )), new_state: State { height: Height::new(1), round: Round::new(0), @@ -525,11 +494,9 @@ fn executor_steps_not_proposer_timeout() { // v2 precommits its proposal TestStep { desc: "v2 precommits its proposal", - input_message: Some(Message::Vote(Vote::new_precommit( - Round::new(0), - Some(value_id), - v2.address, - ))), + input_message: Some(Message::Vote( + Vote::new_precommit(Round::new(0), Some(value_id)).signed(v2.address), + )), expected_output_message: None, new_state: State { height: Height::new(1), @@ -543,11 +510,9 @@ fn executor_steps_not_proposer_timeout() { // v3 precommits for nil TestStep { desc: "v3 precommits for nil", - input_message: Some(Message::Vote(Vote::new_precommit( - Round::new(0), - None, - v3.address, - ))), + input_message: Some(Message::Vote( + Vote::new_precommit(Round::new(0), None).signed(v3.address), + )), expected_output_message: None, new_state: State { height: Height::new(1), diff --git a/Code/test/tests/round.rs b/Code/test/tests/round.rs index 238e35b89..44757b921 100644 --- a/Code/test/tests/round.rs +++ b/Code/test/tests/round.rs @@ -1,6 +1,6 @@ use malachite_test::{Height, Proposal, TestConsensus, Value}; -use malachite_common::{Consensus, Round, Timeout, TimeoutStep}; +use malachite_common::{Round, Timeout, TimeoutStep}; use malachite_round::events::Event; use malachite_round::message::Message; use malachite_round::state::{State, Step}; @@ -54,10 +54,6 @@ fn test_prevote() { assert_eq!(transition.state.step, Step::Prevote); assert_eq!( transition.message.unwrap(), - Message::prevote( - Round::new(1), - Some(value.id()), - TestConsensus::DUMMY_ADDRESS - ) + Message::prevote(Round::new(1), Some(value.id()),) ); } diff --git a/Code/test/tests/vote_count.rs b/Code/test/tests/vote_count.rs index e1367e1c4..fcd164fcd 100644 --- a/Code/test/tests/vote_count.rs +++ b/Code/test/tests/vote_count.rs @@ -2,7 +2,7 @@ use malachite_common::Round; use malachite_vote::count::Threshold; use malachite_vote::RoundVotes; -use malachite_test::{Address, Height, TestConsensus, ValueId, Vote}; +use malachite_test::{Height, TestConsensus, ValueId, Vote}; #[test] fn add_votes_nil() { @@ -12,7 +12,7 @@ fn add_votes_nil() { RoundVotes::new(Height::new(1), Round::new(0), total); // add a vote for nil. nothing changes. - let vote = Vote::new_prevote(Round::new(0), None, Address::new(1)); + let vote = Vote::new_prevote(Round::new(0), None); let thresh = round_votes.add_vote(vote.clone(), 1); assert_eq!(thresh, Threshold::Init); @@ -36,7 +36,7 @@ fn add_votes_single_value() { RoundVotes::new(Height::new(1), Round::new(0), total); // add a vote. nothing changes. - let vote = Vote::new_prevote(Round::new(0), val, Address::new(1)); + let vote = Vote::new_prevote(Round::new(0), val); let thresh = round_votes.add_vote(vote.clone(), weight); assert_eq!(thresh, Threshold::Init); @@ -45,7 +45,7 @@ fn add_votes_single_value() { assert_eq!(thresh, Threshold::Init); // add a vote for nil, get Thresh::Any - let vote_nil = Vote::new_prevote(Round::new(0), None, Address::new(2)); + let vote_nil = Vote::new_prevote(Round::new(0), None); let thresh = round_votes.add_vote(vote_nil, weight); assert_eq!(thresh, Threshold::Any); @@ -66,17 +66,17 @@ fn add_votes_multi_values() { RoundVotes::new(Height::new(1), Round::new(0), total); // add a vote for v1. nothing changes. - let vote1 = Vote::new_precommit(Round::new(0), val1, Address::new(1)); + let vote1 = Vote::new_precommit(Round::new(0), val1); let thresh = round_votes.add_vote(vote1.clone(), 1); assert_eq!(thresh, Threshold::Init); // add a vote for v2. nothing changes. - let vote2 = Vote::new_precommit(Round::new(0), val2, Address::new(2)); + let vote2 = Vote::new_precommit(Round::new(0), val2); let thresh = round_votes.add_vote(vote2.clone(), 1); assert_eq!(thresh, Threshold::Init); // add a vote for nil. nothing changes. - let vote_nil = Vote::new_precommit(Round::new(0), None, Address::new(3)); + let vote_nil = Vote::new_precommit(Round::new(0), None); let thresh = round_votes.add_vote(vote_nil.clone(), 1); assert_eq!(thresh, Threshold::Init); diff --git a/Code/test/tests/vote_keeper.rs b/Code/test/tests/vote_keeper.rs index c5c207edb..ed976b9e5 100644 --- a/Code/test/tests/vote_keeper.rs +++ b/Code/test/tests/vote_keeper.rs @@ -2,13 +2,13 @@ use malachite_common::Round; use malachite_round::events::Event; use malachite_vote::keeper::VoteKeeper; -use malachite_test::{Address, Height, TestConsensus, ValueId, Vote}; +use malachite_test::{Height, TestConsensus, ValueId, Vote}; #[test] fn prevote_apply_nil() { let mut keeper: VoteKeeper = VoteKeeper::new(Height::new(1), Round::INITIAL, 3); - let vote = Vote::new_prevote(Round::new(0), None, Address::new(1)); + let vote = Vote::new_prevote(Round::new(0), None); let event = keeper.apply_vote(vote.clone(), 1); assert_eq!(event, None); @@ -24,7 +24,7 @@ fn prevote_apply_nil() { fn precommit_apply_nil() { let mut keeper: VoteKeeper = VoteKeeper::new(Height::new(1), Round::INITIAL, 3); - let vote = Vote::new_precommit(Round::new(0), None, Address::new(1)); + let vote = Vote::new_precommit(Round::new(0), None); let event = keeper.apply_vote(vote.clone(), 1); assert_eq!(event, None); @@ -42,7 +42,7 @@ fn prevote_apply_single_value() { let v = ValueId::new(1); let val = Some(v); - let vote = Vote::new_prevote(Round::new(0), val, Address::new(1)); + let vote = Vote::new_prevote(Round::new(0), val); let event = keeper.apply_vote(vote.clone(), 1); assert_eq!(event, None); @@ -50,7 +50,7 @@ fn prevote_apply_single_value() { let event = keeper.apply_vote(vote.clone(), 1); assert_eq!(event, None); - let vote_nil = Vote::new_prevote(Round::new(0), None, Address::new(2)); + let vote_nil = Vote::new_prevote(Round::new(0), None); let event = keeper.apply_vote(vote_nil, 1); assert_eq!(event, Some(Event::PolkaAny)); @@ -64,7 +64,7 @@ fn precommit_apply_single_value() { let v = ValueId::new(1); let val = Some(v); - let vote = Vote::new_precommit(Round::new(0), val, Address::new(1)); + let vote = Vote::new_precommit(Round::new(0), val); let event = keeper.apply_vote(vote.clone(), 1); assert_eq!(event, None); @@ -72,7 +72,7 @@ fn precommit_apply_single_value() { let event = keeper.apply_vote(vote.clone(), 1); assert_eq!(event, None); - let vote_nil = Vote::new_precommit(Round::new(0), None, Address::new(2)); + let vote_nil = Vote::new_precommit(Round::new(0), None); let event = keeper.apply_vote(vote_nil, 1); assert_eq!(event, Some(Event::PrecommitAny)); From f6f72aa374e7a5380de7eb1b5cada43b6bf4583b Mon Sep 17 00:00:00 2001 From: Romain Ruetschi Date: Fri, 27 Oct 2023 12:17:19 +0200 Subject: [PATCH 2/6] 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 | 18 ++-- 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, 289 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..a41d8ff7a 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 { @@ -173,6 +173,8 @@ where return None; }; + // TODO: Verify the vote's signature + let round = signed_vote.vote.round(); let event = match self 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 { From 723a389614deacbdf9a57f8cd7f339fabd756f1d Mon Sep 17 00:00:00 2001 From: Romain Ruetschi Date: Fri, 27 Oct 2023 14:38:20 +0200 Subject: [PATCH 3/6] Verify votes signatures --- Code/common/src/consensus.rs | 13 +++++----- Code/common/src/lib.rs | 3 +++ Code/{consensus => common}/src/signed_vote.rs | 2 +- Code/consensus/src/executor.rs | 24 ++++++++----------- Code/consensus/src/lib.rs | 1 - Code/consensus/src/message.rs | 4 +--- Code/test/src/consensus.rs | 8 +++++++ Code/test/src/vote.rs | 3 +-- 8 files changed, 30 insertions(+), 28 deletions(-) rename Code/{consensus => common}/src/signed_vote.rs (92%) diff --git a/Code/common/src/consensus.rs b/Code/common/src/consensus.rs index 3aae8e3af..c38f99653 100644 --- a/Code/common/src/consensus.rs +++ b/Code/common/src/consensus.rs @@ -1,6 +1,6 @@ -use crate::public_key::PrivateKey; use crate::{ - Address, Height, Proposal, PublicKey, Round, Validator, ValidatorSet, Value, ValueId, Vote, + Address, Height, PrivateKey, Proposal, PublicKey, Round, Signature, SignedVote, Validator, + ValidatorSet, Value, ValueId, Vote, }; /// This trait allows to abstract over the various datatypes @@ -13,7 +13,7 @@ where type Height: Height; type Proposal: Proposal; type PrivateKey: PrivateKey; - type PublicKey: PublicKey::Signature>; + type PublicKey: PublicKey>; type Validator: Validator; type ValidatorSet: ValidatorSet; type Value: Value; @@ -22,10 +22,9 @@ where // FIXME: Remove altogether const DUMMY_VALUE: Self::Value; - fn sign_vote( - vote: &Self::Vote, - private_key: &Self::PrivateKey, - ) -> ::Signature; + fn sign_vote(vote: &Self::Vote, private_key: &Self::PrivateKey) -> Signature; + + fn verify_signed_vote(signed_vote: &SignedVote, public_key: &Self::PublicKey) -> bool; /// Build a new proposal for the given value at the given height, round and POL round. fn new_proposal( diff --git a/Code/common/src/lib.rs b/Code/common/src/lib.rs index fb4adadb3..6cf1245bb 100644 --- a/Code/common/src/lib.rs +++ b/Code/common/src/lib.rs @@ -16,6 +16,7 @@ mod height; mod proposal; mod public_key; mod round; +mod signed_vote; mod timeout; mod validator_set; mod value; @@ -23,12 +24,14 @@ mod vote; /// Type alias to make it easier to refer the `ValueId` type of a given `Consensus` engine. pub type ValueId = <::Value as Value>::Id; +pub type Signature = <::PrivateKey as PrivateKey>::Signature; pub use consensus::Consensus; pub use height::Height; pub use proposal::Proposal; pub use public_key::{PrivateKey, PublicKey}; pub use round::Round; +pub use signed_vote::SignedVote; pub use timeout::{Timeout, TimeoutStep}; pub use validator_set::{Address, Validator, ValidatorSet, VotingPower}; pub use value::Value; diff --git a/Code/consensus/src/signed_vote.rs b/Code/common/src/signed_vote.rs similarity index 92% rename from Code/consensus/src/signed_vote.rs rename to Code/common/src/signed_vote.rs index 57bfda0a1..a59e729a5 100644 --- a/Code/consensus/src/signed_vote.rs +++ b/Code/common/src/signed_vote.rs @@ -1,4 +1,4 @@ -use malachite_common::{Consensus, PublicKey}; +use crate::{Consensus, PublicKey}; // TODO: Do we need to abstract over `SignedVote` as well? diff --git a/Code/consensus/src/executor.rs b/Code/consensus/src/executor.rs index a41d8ff7a..d004e6df5 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, PrivateKey, Proposal, Round, Timeout, TimeoutStep, Validator, ValidatorSet, Value, - Vote, VoteType, + Consensus, PrivateKey, Proposal, Round, SignedVote, Timeout, TimeoutStep, Validator, + ValidatorSet, Value, Vote, VoteType, }; use malachite_round::events::Event as RoundEvent; use malachite_round::message::Message as RoundMessage; @@ -11,7 +11,6 @@ use malachite_vote::count::Threshold; use malachite_vote::keeper::VoteKeeper; use crate::message::Message; -use crate::signed_vote::SignedVote; #[derive(Clone, Debug)] pub struct Executor @@ -168,22 +167,19 @@ where } fn apply_vote(&mut self, signed_vote: SignedVote) -> Option> { - let Some(validator) = self.validator_set.get_by_address(&signed_vote.address) else { - // TODO: Is this the correct behavior? How to log such "errors"? - return None; - }; + // TODO: How to handle missing validator? + let validator = self.validator_set.get_by_address(&signed_vote.address)?; - // TODO: Verify the vote's signature + if !C::verify_signed_vote(&signed_vote, validator.public_key()) { + // TODO: How to handle invalid votes? + return None; + } let round = signed_vote.vote.round(); - let event = match self + let event = self .votes - .apply_vote(signed_vote.vote, validator.voting_power()) - { - Some(event) => event, - None => return None, - }; + .apply_vote(signed_vote.vote, validator.voting_power())?; self.apply_event(round, event) } diff --git a/Code/consensus/src/lib.rs b/Code/consensus/src/lib.rs index 652688fa0..8c9bb9172 100644 --- a/Code/consensus/src/lib.rs +++ b/Code/consensus/src/lib.rs @@ -12,4 +12,3 @@ pub mod executor; pub mod message; -pub mod signed_vote; diff --git a/Code/consensus/src/message.rs b/Code/consensus/src/message.rs index f76cc44b4..a82aa0de3 100644 --- a/Code/consensus/src/message.rs +++ b/Code/consensus/src/message.rs @@ -1,6 +1,4 @@ -use malachite_common::{Consensus, Round, Timeout}; - -use crate::signed_vote::SignedVote; +use malachite_common::{Consensus, Round, SignedVote, Timeout}; /// Messages that can be received and broadcast by the consensus executor. #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/Code/test/src/consensus.rs b/Code/test/src/consensus.rs index 843fcb1b5..ecccbc810 100644 --- a/Code/test/src/consensus.rs +++ b/Code/test/src/consensus.rs @@ -1,5 +1,6 @@ use malachite_common::Consensus; use malachite_common::Round; +use malachite_common::SignedVote; use crate::height::*; use crate::proposal::*; @@ -29,6 +30,13 @@ impl Consensus for TestConsensus { private_key.sign(&vote.to_bytes()) } + fn verify_signed_vote(signed_vote: &SignedVote, public_key: &Ed25519PublicKey) -> bool { + use signature::Verifier; + public_key + .verify(&signed_vote.vote.to_bytes(), &signed_vote.signature) + .is_ok() + } + 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/vote.rs b/Code/test/src/vote.rs index 05b62270a..9532b6123 100644 --- a/Code/test/src/vote.rs +++ b/Code/test/src/vote.rs @@ -1,5 +1,4 @@ -use malachite_common::{Round, VoteType}; -use malachite_consensus::signed_vote::SignedVote; +use malachite_common::{Round, SignedVote, VoteType}; use signature::Signer; use crate::{Address, Ed25519PrivateKey, TestConsensus, ValueId}; From c35650e2a2bb0671ed497624447c555a5d685240 Mon Sep 17 00:00:00 2001 From: Romain Ruetschi Date: Fri, 27 Oct 2023 14:39:23 +0200 Subject: [PATCH 4/6] Better name for the module --- Code/common/src/lib.rs | 4 ++-- Code/common/src/{public_key.rs => signing.rs} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename Code/common/src/{public_key.rs => signing.rs} (100%) diff --git a/Code/common/src/lib.rs b/Code/common/src/lib.rs index 6cf1245bb..adef2b18a 100644 --- a/Code/common/src/lib.rs +++ b/Code/common/src/lib.rs @@ -14,9 +14,9 @@ mod consensus; mod height; mod proposal; -mod public_key; mod round; mod signed_vote; +mod signing; mod timeout; mod validator_set; mod value; @@ -29,9 +29,9 @@ pub type Signature = <::PrivateKey as PrivateKey>::Signature; pub use consensus::Consensus; pub use height::Height; pub use proposal::Proposal; -pub use public_key::{PrivateKey, PublicKey}; pub use round::Round; pub use signed_vote::SignedVote; +pub use signing::{PrivateKey, PublicKey}; pub use timeout::{Timeout, TimeoutStep}; pub use validator_set::{Address, Validator, ValidatorSet, VotingPower}; pub use value::Value; diff --git a/Code/common/src/public_key.rs b/Code/common/src/signing.rs similarity index 100% rename from Code/common/src/public_key.rs rename to Code/common/src/signing.rs From 0882e2689b60c9aaaeaf68d481ba3a374fda20d2 Mon Sep 17 00:00:00 2001 From: Romain Ruetschi Date: Fri, 27 Oct 2023 14:40:04 +0200 Subject: [PATCH 5/6] Doc comments --- Code/common/src/consensus.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Code/common/src/consensus.rs b/Code/common/src/consensus.rs index c38f99653..0c4e1e24d 100644 --- a/Code/common/src/consensus.rs +++ b/Code/common/src/consensus.rs @@ -22,8 +22,10 @@ where // FIXME: Remove altogether const DUMMY_VALUE: Self::Value; + /// Sign the given vote using the given private key. fn sign_vote(vote: &Self::Vote, private_key: &Self::PrivateKey) -> Signature; + /// Verify the given vote's signature using the given public key. fn verify_signed_vote(signed_vote: &SignedVote, public_key: &Self::PublicKey) -> bool; /// Build a new proposal for the given value at the given height, round and POL round. From 62c47dfd84b4d66355093303bf1a15f69eabca24 Mon Sep 17 00:00:00 2001 From: Romain Ruetschi Date: Fri, 27 Oct 2023 15:25:25 +0200 Subject: [PATCH 6/6] Refactor signing facility into a `SigningScheme` --- Code/common/Cargo.toml | 1 + Code/common/src/consensus.rs | 13 ++-- Code/common/src/lib.rs | 9 ++- Code/common/src/signed_vote.rs | 10 +-- Code/common/src/signing.rs | 26 ++++---- Code/common/src/validator_set.rs | 6 +- Code/consensus/Cargo.toml | 1 + Code/consensus/src/executor.rs | 15 +++-- Code/test/Cargo.toml | 1 + Code/test/src/consensus.rs | 9 ++- Code/test/src/lib.rs | 4 +- Code/test/src/public_key.rs | 68 -------------------- Code/test/src/signing.rs | 89 +++++++++++++++++++++++++++ Code/test/src/validator_set.rs | 28 ++++----- Code/test/src/vote.rs | 4 +- Code/test/tests/consensus_executor.rs | 22 +++---- 16 files changed, 165 insertions(+), 141 deletions(-) delete mode 100644 Code/test/src/public_key.rs create mode 100644 Code/test/src/signing.rs diff --git a/Code/common/Cargo.toml b/Code/common/Cargo.toml index 7fad66bf0..37a2afea2 100644 --- a/Code/common/Cargo.toml +++ b/Code/common/Cargo.toml @@ -5,4 +5,5 @@ edition = "2021" publish = false [dependencies] +secrecy = "0.8.0" signature = "2.1.0" diff --git a/Code/common/src/consensus.rs b/Code/common/src/consensus.rs index 0c4e1e24d..7666c2eb1 100644 --- a/Code/common/src/consensus.rs +++ b/Code/common/src/consensus.rs @@ -1,6 +1,6 @@ use crate::{ - Address, Height, PrivateKey, Proposal, PublicKey, Round, Signature, SignedVote, Validator, - ValidatorSet, Value, ValueId, Vote, + Address, Height, PrivateKey, Proposal, PublicKey, Round, Signature, SignedVote, SigningScheme, + Validator, ValidatorSet, Value, ValueId, Vote, }; /// This trait allows to abstract over the various datatypes @@ -12,21 +12,22 @@ where type Address: Address; type Height: Height; type Proposal: Proposal; - type PrivateKey: PrivateKey; - type PublicKey: PublicKey>; type Validator: Validator; type ValidatorSet: ValidatorSet; type Value: Value; type Vote: Vote; + type SigningScheme: SigningScheme; // TODO: Do we need to support multiple signing schemes? // FIXME: Remove altogether const DUMMY_VALUE: Self::Value; /// Sign the given vote using the given private key. - fn sign_vote(vote: &Self::Vote, private_key: &Self::PrivateKey) -> Signature; + /// TODO: Maybe move this as concrete methods in `SignedVote`? + fn sign_vote(vote: &Self::Vote, private_key: &PrivateKey) -> Signature; /// Verify the given vote's signature using the given public key. - fn verify_signed_vote(signed_vote: &SignedVote, public_key: &Self::PublicKey) -> bool; + /// TODO: Maybe move this as concrete methods in `SignedVote`? + fn verify_signed_vote(signed_vote: &SignedVote, public_key: &PublicKey) -> bool; /// Build a new proposal for the given value at the given height, round and POL round. fn new_proposal( diff --git a/Code/common/src/lib.rs b/Code/common/src/lib.rs index adef2b18a..9a0b76501 100644 --- a/Code/common/src/lib.rs +++ b/Code/common/src/lib.rs @@ -22,16 +22,21 @@ mod validator_set; mod value; mod vote; +// Re-export `signature` crate for convenience +pub use ::signature; + /// Type alias to make it easier to refer the `ValueId` type of a given `Consensus` engine. pub type ValueId = <::Value as Value>::Id; -pub type Signature = <::PrivateKey as PrivateKey>::Signature; +pub type PublicKey = <::SigningScheme as SigningScheme>::PublicKey; +pub type PrivateKey = <::SigningScheme as SigningScheme>::PrivateKey; +pub type Signature = <::SigningScheme as SigningScheme>::Signature; pub use consensus::Consensus; pub use height::Height; pub use proposal::Proposal; pub use round::Round; pub use signed_vote::SignedVote; -pub use signing::{PrivateKey, PublicKey}; +pub use signing::SigningScheme; pub use timeout::{Timeout, TimeoutStep}; pub use validator_set::{Address, Validator, ValidatorSet, VotingPower}; pub use value::Value; diff --git a/Code/common/src/signed_vote.rs b/Code/common/src/signed_vote.rs index a59e729a5..adfd4a268 100644 --- a/Code/common/src/signed_vote.rs +++ b/Code/common/src/signed_vote.rs @@ -1,4 +1,4 @@ -use crate::{Consensus, PublicKey}; +use crate::{Consensus, Signature}; // TODO: Do we need to abstract over `SignedVote` as well? @@ -9,18 +9,14 @@ where { pub vote: C::Vote, pub address: C::Address, - pub signature: ::Signature, + pub signature: Signature, } impl SignedVote where C: Consensus, { - pub fn new( - vote: C::Vote, - address: C::Address, - signature: ::Signature, - ) -> Self { + pub fn new(vote: C::Vote, address: C::Address, signature: Signature) -> Self { Self { vote, address, diff --git a/Code/common/src/signing.rs b/Code/common/src/signing.rs index 7808f0c5c..07abc4ae5 100644 --- a/Code/common/src/signing.rs +++ b/Code/common/src/signing.rs @@ -1,22 +1,20 @@ use core::fmt::Debug; -use signature::{Signer, Verifier}; +use secrecy::{CloneableSecret, DebugSecret, Zeroize}; +use signature::{Keypair, Signer, Verifier}; -/// Defines the requirements for a private key type. -pub trait PrivateKey +pub trait SigningScheme where - Self: Clone + Debug + Signer, + Self: Clone + Debug + Eq, { - type Signature: Clone + Debug + PartialEq + Eq; - type PublicKey: PublicKey; + type Signature: Clone + Debug + Eq; - fn public_key(&self) -> Self::PublicKey; -} + type PublicKey: Clone + Debug + Eq + Verifier; -/// Defines the requirements for a public key type. -pub trait PublicKey -where - Self: Clone + Debug + PartialEq + Eq + Verifier, -{ - type Signature: Clone + Debug + PartialEq + Eq; + type PrivateKey: Clone + + Signer + + Keypair + + Zeroize + + DebugSecret + + CloneableSecret; } diff --git a/Code/common/src/validator_set.rs b/Code/common/src/validator_set.rs index db3756e56..29b9b8c50 100644 --- a/Code/common/src/validator_set.rs +++ b/Code/common/src/validator_set.rs @@ -1,6 +1,6 @@ use core::fmt::Debug; -use crate::Consensus; +use crate::{Consensus, PublicKey}; /// Voting power held by a validator. /// @@ -26,7 +26,7 @@ where fn address(&self) -> &C::Address; /// The public key of the validator, used to verify signatures. - fn public_key(&self) -> &C::PublicKey; + fn public_key(&self) -> &PublicKey; /// The voting power held by the validaror. fn voting_power(&self) -> VotingPower; @@ -46,7 +46,7 @@ where fn get_proposer(&self) -> C::Validator; /// Get the validator with the given public key. - fn get_by_public_key(&self, public_key: &C::PublicKey) -> Option<&C::Validator>; + fn get_by_public_key(&self, public_key: &PublicKey) -> Option<&C::Validator>; /// Get the validator with the given address. fn get_by_address(&self, address: &C::Address) -> Option<&C::Validator>; diff --git a/Code/consensus/Cargo.toml b/Code/consensus/Cargo.toml index 90fda0325..01b40c00a 100644 --- a/Code/consensus/Cargo.toml +++ b/Code/consensus/Cargo.toml @@ -8,3 +8,4 @@ publish = false malachite-common = { version = "0.1.0", path = "../common" } malachite-round = { version = "0.1.0", path = "../round" } malachite-vote = { version = "0.1.0", path = "../vote" } +secrecy = "0.8.0" diff --git a/Code/consensus/src/executor.rs b/Code/consensus/src/executor.rs index d004e6df5..54496e892 100644 --- a/Code/consensus/src/executor.rs +++ b/Code/consensus/src/executor.rs @@ -1,5 +1,8 @@ use std::collections::BTreeMap; +use secrecy::{ExposeSecret, Secret}; + +use malachite_common::signature::Keypair; use malachite_common::{ Consensus, PrivateKey, Proposal, Round, SignedVote, Timeout, TimeoutStep, Validator, ValidatorSet, Value, Vote, VoteType, @@ -18,7 +21,7 @@ where C: Consensus, { height: C::Height, - key: C::PrivateKey, + key: Secret>, validator_set: C::ValidatorSet, round: Round, votes: VoteKeeper, @@ -29,7 +32,7 @@ impl Executor where C: Consensus, { - pub fn new(height: C::Height, validator_set: C::ValidatorSet, key: C::PrivateKey) -> Self { + pub fn new(height: C::Height, validator_set: C::ValidatorSet, key: PrivateKey) -> Self { let votes = VoteKeeper::new( height.clone(), Round::INITIAL, @@ -38,7 +41,7 @@ where Self { height, - key, + key: Secret::new(key), validator_set, round: Round::INITIAL, votes, @@ -75,11 +78,11 @@ where RoundMessage::Vote(vote) => { let address = self .validator_set - .get_by_public_key(&self.key.public_key())? + .get_by_public_key(&self.key.expose_secret().verifying_key())? .address() .clone(); - let signature = C::sign_vote(&vote, &self.key); + let signature = C::sign_vote(&vote, self.key.expose_secret()); let signed_vote = SignedVote::new(vote, address, signature); Some(Message::Vote(signed_vote)) @@ -109,7 +112,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.public_key() { + let event = if proposer.public_key() == &self.key.expose_secret().verifying_key() { let value = self.get_value(); RoundEvent::NewRoundProposer(value) } else { diff --git a/Code/test/Cargo.toml b/Code/test/Cargo.toml index 536932fdb..6e1e33b87 100644 --- a/Code/test/Cargo.toml +++ b/Code/test/Cargo.toml @@ -13,3 +13,4 @@ ed25519-consensus = "2.1.0" signature = "2.1.0" rand = { version = "0.8.5", features = ["std_rng"] } sha2 = "0.10.8" +secrecy = "0.8.0" diff --git a/Code/test/src/consensus.rs b/Code/test/src/consensus.rs index ecccbc810..80abb1035 100644 --- a/Code/test/src/consensus.rs +++ b/Code/test/src/consensus.rs @@ -4,7 +4,7 @@ use malachite_common::SignedVote; use crate::height::*; use crate::proposal::*; -use crate::public_key::{Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature}; +use crate::signing::{Ed25519, PrivateKey, PublicKey, Signature}; use crate::validator_set::*; use crate::value::*; use crate::vote::*; @@ -16,21 +16,20 @@ impl Consensus for TestConsensus { type Address = Address; type Height = Height; type Proposal = Proposal; - type PublicKey = Ed25519PublicKey; - type PrivateKey = Ed25519PrivateKey; type ValidatorSet = ValidatorSet; type Validator = Validator; type Value = Value; type Vote = Vote; + type SigningScheme = Ed25519; const DUMMY_VALUE: Self::Value = Value::new(9999); - fn sign_vote(vote: &Self::Vote, private_key: &Self::PrivateKey) -> Ed25519Signature { + fn sign_vote(vote: &Self::Vote, private_key: &PrivateKey) -> Signature { use signature::Signer; private_key.sign(&vote.to_bytes()) } - fn verify_signed_vote(signed_vote: &SignedVote, public_key: &Ed25519PublicKey) -> bool { + fn verify_signed_vote(signed_vote: &SignedVote, public_key: &PublicKey) -> bool { use signature::Verifier; public_key .verify(&signed_vote.vote.to_bytes(), &signed_vote.signature) diff --git a/Code/test/src/lib.rs b/Code/test/src/lib.rs index c08d0d1de..9da94495e 100644 --- a/Code/test/src/lib.rs +++ b/Code/test/src/lib.rs @@ -4,7 +4,7 @@ mod consensus; mod height; mod proposal; -mod public_key; +mod signing; mod validator_set; mod value; mod vote; @@ -12,7 +12,7 @@ mod vote; pub use crate::consensus::*; pub use crate::height::*; pub use crate::proposal::*; -pub use crate::public_key::*; +pub use crate::signing::*; 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 deleted file mode 100644 index 51a35b8a6..000000000 --- a/Code/test/src/public_key.rs +++ /dev/null @@ -1,68 +0,0 @@ -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/signing.rs b/Code/test/src/signing.rs new file mode 100644 index 000000000..af120d071 --- /dev/null +++ b/Code/test/src/signing.rs @@ -0,0 +1,89 @@ +use malachite_common::SigningScheme; +use rand::{CryptoRng, RngCore}; +use secrecy::{CloneableSecret, DebugSecret, Zeroize}; +use signature::{Keypair, Signer, Verifier}; + +pub use ed25519_consensus::Signature; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Ed25519; + +impl Ed25519 { + pub fn generate_keypair(rng: R) -> PrivateKey + where + R: RngCore + CryptoRng, + { + PrivateKey::generate(rng) + } +} + +impl SigningScheme for Ed25519 { + type Signature = Signature; + type PublicKey = PublicKey; + type PrivateKey = PrivateKey; +} + +#[derive(Clone, Debug)] +pub struct PrivateKey(ed25519_consensus::SigningKey); + +impl PrivateKey { + pub fn generate(rng: R) -> Self + where + R: RngCore + CryptoRng, + { + let signing_key = ed25519_consensus::SigningKey::new(rng); + + Self(signing_key) + } + + pub fn public_key(&self) -> PublicKey { + PublicKey::new(self.0.verification_key()) + } +} + +impl Signer for PrivateKey { + fn try_sign(&self, msg: &[u8]) -> Result { + Ok(self.0.sign(msg)) + } +} + +impl Keypair for PrivateKey { + type VerifyingKey = PublicKey; + + fn verifying_key(&self) -> Self::VerifyingKey { + self.public_key() + } +} + +impl Zeroize for PrivateKey { + fn zeroize(&mut self) { + self.0.zeroize() + } +} + +impl DebugSecret for PrivateKey {} +impl CloneableSecret for PrivateKey {} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct PublicKey(ed25519_consensus::VerificationKey); + +impl PublicKey { + 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 Verifier for PublicKey { + 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 841042678..985da7d23 100644 --- a/Code/test/src/validator_set.rs +++ b/Code/test/src/validator_set.rs @@ -2,7 +2,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use malachite_common::VotingPower; -use crate::{Ed25519PublicKey, TestConsensus}; +use crate::{signing::PublicKey, TestConsensus}; #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Address([u8; Self::LENGTH]); @@ -14,7 +14,7 @@ impl Address { Self(value) } - pub fn from_public_key(public_key: &Ed25519PublicKey) -> Self { + pub fn from_public_key(public_key: &PublicKey) -> Self { let hash = public_key.hash(); let mut address = [0; Self::LENGTH]; address.copy_from_slice(&hash[..Self::LENGTH]); @@ -28,12 +28,12 @@ impl malachite_common::Address for Address {} #[derive(Clone, Debug, PartialEq, Eq)] pub struct Validator { pub address: Address, - pub public_key: Ed25519PublicKey, + pub public_key: PublicKey, pub voting_power: VotingPower, } impl Validator { - pub fn new(public_key: Ed25519PublicKey, voting_power: VotingPower) -> Self { + pub fn new(public_key: PublicKey, voting_power: VotingPower) -> Self { Self { address: Address::from_public_key(&public_key), public_key, @@ -47,7 +47,7 @@ impl malachite_common::Validator for Validator { &self.address } - fn public_key(&self) -> &Ed25519PublicKey { + fn public_key(&self) -> &PublicKey { &self.public_key } @@ -113,7 +113,7 @@ impl ValidatorSet { self.validators.iter().find(|v| &v.address == address) } - pub fn get_by_public_key(&self, public_key: &Ed25519PublicKey) -> Option<&Validator> { + pub fn get_by_public_key(&self, public_key: &PublicKey) -> Option<&Validator> { self.validators.iter().find(|v| &v.public_key == public_key) } @@ -147,7 +147,7 @@ impl malachite_common::ValidatorSet for ValidatorSet { self.total_voting_power() } - fn get_by_public_key(&self, public_key: &Ed25519PublicKey) -> Option<&Validator> { + fn get_by_public_key(&self, public_key: &PublicKey) -> Option<&Validator> { self.get_by_public_key(public_key) } @@ -167,18 +167,18 @@ mod tests { use super::*; - use crate::Ed25519PrivateKey; + use crate::PrivateKey; #[test] fn add_update_remove() { 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 sk1 = PrivateKey::generate(&mut rng); + let sk2 = PrivateKey::generate(&mut rng); + let sk3 = PrivateKey::generate(&mut rng); + let sk4 = PrivateKey::generate(&mut rng); + let sk5 = PrivateKey::generate(&mut rng); + let sk6 = PrivateKey::generate(&mut rng); let v1 = Validator::new(sk1.public_key(), 1); let v2 = Validator::new(sk2.public_key(), 2); diff --git a/Code/test/src/vote.rs b/Code/test/src/vote.rs index 9532b6123..5601b8a7e 100644 --- a/Code/test/src/vote.rs +++ b/Code/test/src/vote.rs @@ -1,7 +1,7 @@ use malachite_common::{Round, SignedVote, VoteType}; use signature::Signer; -use crate::{Address, Ed25519PrivateKey, TestConsensus, ValueId}; +use crate::{Address, PrivateKey, TestConsensus, ValueId}; /// A vote for a value in a round #[derive(Clone, Debug, PartialEq, Eq)] @@ -46,7 +46,7 @@ impl Vote { bytes } - pub fn signed(self, private_key: &Ed25519PrivateKey) -> SignedVote { + pub fn signed(self, private_key: &PrivateKey) -> SignedVote { let address = Address::from_public_key(&private_key.public_key()); let signature = private_key.sign(&self.to_bytes()); diff --git a/Code/test/tests/consensus_executor.rs b/Code/test/tests/consensus_executor.rs index 71cff7c76..e617ece38 100644 --- a/Code/test/tests/consensus_executor.rs +++ b/Code/test/tests/consensus_executor.rs @@ -3,9 +3,7 @@ use malachite_consensus::executor::Executor; use malachite_consensus::message::Message; use malachite_round::state::{RoundValue, State, Step}; -use malachite_test::{ - Ed25519PrivateKey, Height, Proposal, TestConsensus, Validator, ValidatorSet, Vote, -}; +use malachite_test::{Height, PrivateKey, Proposal, TestConsensus, Validator, ValidatorSet, Vote}; use rand::rngs::StdRng; use rand::SeedableRng; @@ -23,9 +21,9 @@ fn executor_steps_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 sk1 = PrivateKey::generate(&mut rng); + let sk2 = PrivateKey::generate(&mut rng); + let sk3 = PrivateKey::generate(&mut rng); let v1 = Validator::new(sk1.public_key(), 1); let v2 = Validator::new(sk2.public_key(), 2); @@ -208,9 +206,9 @@ fn executor_steps_not_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 sk1 = PrivateKey::generate(&mut rng); + let sk2 = PrivateKey::generate(&mut rng); + let sk3 = PrivateKey::generate(&mut rng); let v1 = Validator::new(sk1.public_key(), 1); let v2 = Validator::new(sk2.public_key(), 2); @@ -391,9 +389,9 @@ fn executor_steps_not_proposer_timeout() { 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 sk1 = PrivateKey::generate(&mut rng); + let sk2 = PrivateKey::generate(&mut rng); + let sk3 = PrivateKey::generate(&mut rng); let v1 = Validator::new(sk1.public_key(), 1); let v2 = Validator::new(sk2.public_key(), 1);