From 0e47e4f67576d4dbd44c4c9455c76e8944a9978c Mon Sep 17 00:00:00 2001 From: Daniel Cason Date: Thu, 12 Feb 2026 17:57:47 -0800 Subject: [PATCH 1/9] votekeeper: PrecommitValue and SkipRound test --- .../core-votekeeper/tests/vote_keeper.rs | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/code/crates/core-votekeeper/tests/vote_keeper.rs b/code/crates/core-votekeeper/tests/vote_keeper.rs index 2196a144b..13730c4ba 100644 --- a/code/crates/core-votekeeper/tests/vote_keeper.rs +++ b/code/crates/core-votekeeper/tests/vote_keeper.rs @@ -1,6 +1,7 @@ -use malachitebft_core_types::{NilOrVal, Round, SignedVote}; +use malachitebft_core_types::{NilOrVal, Round, SignedVote, VoteType}; use informalsystems_malachitebft_core_votekeeper::keeper::{Output, VoteKeeper}; +use informalsystems_malachitebft_core_votekeeper::Threshold; use malachitebft_test::{ Address, Height, PrivateKey, Signature, TestContext, Validator, ValidatorSet, ValueId, Vote, @@ -249,6 +250,30 @@ fn no_skip_round_full_quorum_with_same_val() { assert_eq!(msg, None); } +#[test] +fn skip_round_and_precommit_value_future_round() { + let ([addr1, addr2, ..], mut keeper) = setup([2, 3, 2]); + + let id = ValueId::new(1); + let val = NilOrVal::Val(id); + let height = Height::new(1); + let cur_round = Round::new(0); + let fut_round = Round::new(1); + + let vote = new_signed_precommit(height, fut_round, val, addr1); + let msg = keeper.apply_vote(vote, cur_round); + assert_eq!(msg, None); + + let vote = new_signed_precommit(height, fut_round, val, addr2); + let msg = keeper.apply_vote(vote, cur_round); + assert_eq!(msg, Some(Output::SkipRound(fut_round))); + + // A PrecommitValue(id) could be produced + assert!(keeper.is_threshold_met(&fut_round, VoteType::Precommit, Threshold::Value(id))); + // FIXME: should we instead expect it to be produced? + // assert_eq!(msg, Some(Output::PrecommitValue(id))); +} + #[test] fn same_votes() { let ([addr1, ..], mut keeper) = setup([1, 1]); From ea630cd7c0a3325cd3559dcda9cf2c7688d63ffc Mon Sep 17 00:00:00 2001 From: Daniel Cason Date: Sun, 15 Feb 2026 03:02:44 -0800 Subject: [PATCH 2/9] vote-keeper: PrecommitValue output for future round --- code/crates/core-votekeeper/tests/vote_keeper.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/code/crates/core-votekeeper/tests/vote_keeper.rs b/code/crates/core-votekeeper/tests/vote_keeper.rs index 13730c4ba..f0a8a7f29 100644 --- a/code/crates/core-votekeeper/tests/vote_keeper.rs +++ b/code/crates/core-votekeeper/tests/vote_keeper.rs @@ -1,7 +1,6 @@ -use malachitebft_core_types::{NilOrVal, Round, SignedVote, VoteType}; +use malachitebft_core_types::{NilOrVal, Round, SignedVote}; use informalsystems_malachitebft_core_votekeeper::keeper::{Output, VoteKeeper}; -use informalsystems_malachitebft_core_votekeeper::Threshold; use malachitebft_test::{ Address, Height, PrivateKey, Signature, TestContext, Validator, ValidatorSet, ValueId, Vote, @@ -266,12 +265,7 @@ fn skip_round_and_precommit_value_future_round() { let vote = new_signed_precommit(height, fut_round, val, addr2); let msg = keeper.apply_vote(vote, cur_round); - assert_eq!(msg, Some(Output::SkipRound(fut_round))); - - // A PrecommitValue(id) could be produced - assert!(keeper.is_threshold_met(&fut_round, VoteType::Precommit, Threshold::Value(id))); - // FIXME: should we instead expect it to be produced? - // assert_eq!(msg, Some(Output::PrecommitValue(id))); + assert_eq!(msg, Some(Output::PrecommitValue(id))); } #[test] From 1311aab4a180a30d7705078b06d80fbd000d06b2 Mon Sep 17 00:00:00 2001 From: Daniel Cason Date: Sun, 15 Feb 2026 03:08:04 -0800 Subject: [PATCH 3/9] vote-keeper: SkipRound() when no threshould is met --- code/crates/core-votekeeper/src/keeper.rs | 43 +++++++++++++---------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/code/crates/core-votekeeper/src/keeper.rs b/code/crates/core-votekeeper/src/keeper.rs index 0ab34745f..5174b4f69 100644 --- a/code/crates/core-votekeeper/src/keeper.rs +++ b/code/crates/core-votekeeper/src/keeper.rs @@ -267,21 +267,6 @@ where } } - if vote.round() > round { - let combined_weight = per_round.addresses_weights.sum(); - - let skip_round = self - .threshold_params - .honest - .is_met(combined_weight, total_weight); - - if skip_round { - let output = Output::SkipRound(vote.round()); - per_round.emitted_outputs.insert(output.clone()); - return Some(output); - } - } - let threshold = compute_threshold( vote.vote_type(), per_round, @@ -290,7 +275,18 @@ where total_weight, ); - let output = threshold_to_output(vote.vote_type(), threshold); + let skip_round = if vote.round() > round + && self + .threshold_params + .honest + .is_met(per_round.addresses_weights.sum(), total_weight) + { + Some(vote.round()) + } else { + None + }; + + let output = threshold_to_output(vote.vote_type(), threshold, skip_round); match output { // Ensure we do not output the same message twice @@ -359,9 +355,20 @@ where } /// Map a vote type and a threshold to a state machine output. -fn threshold_to_output(typ: VoteType, threshold: Threshold) -> Option> { +/// Also considers a potential honest threshold for a future round. +fn threshold_to_output( + typ: VoteType, + threshold: Threshold, + future_round: Option, +) -> Option> { match (typ, threshold) { - (_, Threshold::Unreached) => None, + (_, Threshold::Unreached) => { + if future_round.is_some() { + Some(Output::SkipRound(future_round.unwrap())) + } else { + None + } + } (VoteType::Prevote, Threshold::Any) => Some(Output::PolkaAny), (VoteType::Prevote, Threshold::Nil) => Some(Output::PolkaNil), From 3faff28676ed1324920c0c714fb8b5e78f3e8c95 Mon Sep 17 00:00:00 2001 From: Daniel Cason Date: Sun, 15 Feb 2026 03:27:39 -0800 Subject: [PATCH 4/9] keeper: only PrecommitValue overcomes SkipRound --- code/crates/core-votekeeper/src/keeper.rs | 34 +++++++++++++---------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/code/crates/core-votekeeper/src/keeper.rs b/code/crates/core-votekeeper/src/keeper.rs index 5174b4f69..e13db66a0 100644 --- a/code/crates/core-votekeeper/src/keeper.rs +++ b/code/crates/core-votekeeper/src/keeper.rs @@ -355,27 +355,31 @@ where } /// Map a vote type and a threshold to a state machine output. -/// Also considers a potential honest threshold for a future round. +/// Also considers an optional honest threshold for a future round. fn threshold_to_output( typ: VoteType, threshold: Threshold, future_round: Option, ) -> Option> { - match (typ, threshold) { - (_, Threshold::Unreached) => { - if future_round.is_some() { - Some(Output::SkipRound(future_round.unwrap())) - } else { - None - } + if future_round.is_none() { + // Thresholds for the current round + match (typ, threshold) { + (_, Threshold::Unreached) => None, + + (VoteType::Prevote, Threshold::Any) => Some(Output::PolkaAny), + (VoteType::Prevote, Threshold::Nil) => Some(Output::PolkaNil), + (VoteType::Prevote, Threshold::Value(v)) => Some(Output::PolkaValue(v)), + + (VoteType::Precommit, Threshold::Any) => Some(Output::PrecommitAny), + (VoteType::Precommit, Threshold::Nil) => Some(Output::PrecommitAny), + (VoteType::Precommit, Threshold::Value(v)) => Some(Output::PrecommitValue(v)), } + } else { + // Only PrecommitValue(v) has larger priority than SkipRound(r) + match (typ, threshold) { + (VoteType::Precommit, Threshold::Value(v)) => Some(Output::PrecommitValue(v)), - (VoteType::Prevote, Threshold::Any) => Some(Output::PolkaAny), - (VoteType::Prevote, Threshold::Nil) => Some(Output::PolkaNil), - (VoteType::Prevote, Threshold::Value(v)) => Some(Output::PolkaValue(v)), - - (VoteType::Precommit, Threshold::Any) => Some(Output::PrecommitAny), - (VoteType::Precommit, Threshold::Nil) => Some(Output::PrecommitAny), - (VoteType::Precommit, Threshold::Value(v)) => Some(Output::PrecommitValue(v)), + (_, _) => Some(Output::SkipRound(future_round.unwrap())), + } } } From 452eae4aa5c9bfca79e8949a91570248d39dea43 Mon Sep 17 00:00:00 2001 From: Daniel Cason Date: Thu, 12 Feb 2026 18:33:08 -0800 Subject: [PATCH 5/9] driver: test for round-1 decision during round 0 * Currently, it fails when the decision is reached with votes * It works when the decision is reached with a commit certificate --- code/crates/core-driver/tests/it/extra.rs | 107 ++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/code/crates/core-driver/tests/it/extra.rs b/code/crates/core-driver/tests/it/extra.rs index 21ec981a6..55f03bde5 100644 --- a/code/crates/core-driver/tests/it/extra.rs +++ b/code/crates/core-driver/tests/it/extra.rs @@ -3646,6 +3646,113 @@ fn sync_decision_certificate_then_proposal() { run_steps(&mut driver, steps); } +#[test] +fn round_1_decision_during_round_0() { + let value = Value::new(9999); + + let [(v1, _sk1), (v2, _sk2), (v3, sk3)] = make_validators([2, 3, 2]); + let (_my_sk, my_addr) = (sk3.clone(), v3.address); + + let height = Height::new(1); + let ctx = TestContext::new(); + let vs = ValidatorSet::new(vec![v1.clone(), v2.clone(), v3.clone()]); + + let proposal = Proposal::new( + Height::new(1), + Round::new(1), + value.clone(), + Round::Nil, + v1.address, + ); + + let mut driver = Driver::new(ctx, height, vs, my_addr, Default::default()); + + let steps = vec![ + TestStep { + desc: "Start round 0, we are not the proposer", + input: new_round_input(Round::new(0), v1.address), + expected_outputs: vec![start_propose_timer_output(Round::new(0))], + expected_round: Round::new(0), + new_state: propose_state(Round::new(0)), + }, + TestStep { + desc: "v1 precommits a proposal in round 1", + input: precommit_input(Round::new(1), value.clone(), &v1.address), + expected_outputs: vec![], + expected_round: Round::new(0), + new_state: propose_state(Round::new(0)), + }, + TestStep { + desc: "Receive proposal from round 1", + input: proposal_input_from_proposal(proposal.clone(), Validity::Valid), + expected_outputs: vec![], + expected_round: Round::new(0), + new_state: propose_state(Round::new(0)), + }, + TestStep { + desc: "v2 precommits the same round 1 proposal, we have a decision", + input: precommit_input(Round::new(1), value.clone(), &v2.address), + expected_outputs: vec![decide_output(Round::new(0), proposal)], + expected_round: Round::new(0), + new_state: decided_state(Round::new(0), Round::new(1), value), + }, + ]; + + run_steps(&mut driver, steps); +} + +#[test] +fn round_1_decision_during_round_0_via_certificate() { + let value = Value::new(9999); + + let [(v1, _sk1), (v2, _sk2), (v3, sk3)] = make_validators([2, 3, 2]); + let (_my_sk, my_addr) = (sk3.clone(), v3.address); + + let height = Height::new(1); + let ctx = TestContext::new(); + let vs = ValidatorSet::new(vec![v1.clone(), v2.clone(), v3.clone()]); + + let proposal = Proposal::new( + Height::new(1), + Round::new(1), + value.clone(), + Round::Nil, + v1.address, + ); + + let mut driver = Driver::new(ctx, height, vs, my_addr, Default::default()); + + let steps = vec![ + TestStep { + desc: "Start round 0, we are not the proposer", + input: new_round_input(Round::new(0), v1.address), + expected_outputs: vec![start_propose_timer_output(Round::new(0))], + expected_round: Round::new(0), + new_state: propose_state(Round::new(0)), + }, + TestStep { + desc: "we get a commit certificate for v at round 1", + input: commit_certificate_input_at( + Round::new(1), + value.clone(), + &[v1.address, v2.address], + ), + expected_outputs: vec![], + expected_round: Round::new(0), + new_state: propose_state(Round::new(0)), + }, + TestStep { + desc: "Receive proposal from round 1", + input: proposal_input_from_proposal(proposal.clone(), Validity::Valid), + expected_outputs: vec![decide_output(Round::new(0), proposal)], + expected_round: Round::new(0), + new_state: decided_state(Round::new(0), Round::new(1), value), + }, + ]; + + run_steps(&mut driver, steps); +} + fn run_steps(driver: &mut Driver, steps: Vec) { let mut last_step = Step::Unstarted; for step in steps { From 4331bf591c37cfa3cbea00fcda648c345fd1f75d Mon Sep 17 00:00:00 2001 From: Daniel Cason Date: Thu, 12 Feb 2026 19:15:53 -0800 Subject: [PATCH 6/9] driver: same tests but with invalid proposals --- code/crates/core-driver/tests/it/extra.rs | 107 ++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/code/crates/core-driver/tests/it/extra.rs b/code/crates/core-driver/tests/it/extra.rs index 55f03bde5..03b9ed541 100644 --- a/code/crates/core-driver/tests/it/extra.rs +++ b/code/crates/core-driver/tests/it/extra.rs @@ -3753,6 +3753,113 @@ fn round_1_decision_during_round_0_via_certificate() { run_steps(&mut driver, steps); } +#[test] +fn round_1_invalid_decision_during_round_0() { + let value = Value::new(9999); + + let [(v1, _sk1), (v2, _sk2), (v3, sk3)] = make_validators([2, 3, 2]); + let (_my_sk, my_addr) = (sk3.clone(), v3.address); + + let height = Height::new(1); + let ctx = TestContext::new(); + let vs = ValidatorSet::new(vec![v1.clone(), v2.clone(), v3.clone()]); + + let proposal = Proposal::new( + Height::new(1), + Round::new(1), + value.clone(), + Round::Nil, + v1.address, + ); + + let mut driver = Driver::new(ctx, height, vs, my_addr, Default::default()); + + let steps = vec![ + TestStep { + desc: "Start round 0, we are not the proposer", + input: new_round_input(Round::new(0), v1.address), + expected_outputs: vec![start_propose_timer_output(Round::new(0))], + expected_round: Round::new(0), + new_state: propose_state(Round::new(0)), + }, + TestStep { + desc: "v1 precommits a proposal in round 1", + input: precommit_input(Round::new(1), value.clone(), &v1.address), + expected_outputs: vec![], + expected_round: Round::new(0), + new_state: propose_state(Round::new(0)), + }, + TestStep { + desc: "Receive proposal from round 1, which is invalid", + input: proposal_input_from_proposal(proposal.clone(), Validity::Invalid), + expected_outputs: vec![], + expected_round: Round::new(0), + new_state: propose_state(Round::new(0)), + }, + TestStep { + desc: "v2 precommits the same round 1 proposal, but we don't decide", + input: precommit_input(Round::new(1), value.clone(), &v2.address), + expected_outputs: vec![new_round_output(Round::new(1))], + expected_round: Round::new(1), + new_state: new_round(Round::new(1)), + }, + ]; + + run_steps(&mut driver, steps); +} + +#[test] +fn round_1_invalid_decision_during_round_0_via_certificate() { + let value = Value::new(9999); + + let [(v1, _sk1), (v2, _sk2), (v3, sk3)] = make_validators([2, 3, 2]); + let (_my_sk, my_addr) = (sk3.clone(), v3.address); + + let height = Height::new(1); + let ctx = TestContext::new(); + let vs = ValidatorSet::new(vec![v1.clone(), v2.clone(), v3.clone()]); + + let proposal = Proposal::new( + Height::new(1), + Round::new(1), + value.clone(), + Round::Nil, + v1.address, + ); + + let mut driver = Driver::new(ctx, height, vs, my_addr, Default::default()); + + let steps = vec![ + TestStep { + desc: "Start round 0, we are not the proposer", + input: new_round_input(Round::new(0), v1.address), + expected_outputs: vec![start_propose_timer_output(Round::new(0))], + expected_round: Round::new(0), + new_state: propose_state(Round::new(0)), + }, + TestStep { + desc: "we get a commit certificate for v at round 1", + input: commit_certificate_input_at( + Round::new(1), + value.clone(), + &[v1.address, v2.address], + ), + expected_outputs: vec![], + expected_round: Round::new(0), + new_state: propose_state(Round::new(0)), + }, + TestStep { + desc: "Receive proposal from round 1, which is invalid", + input: proposal_input_from_proposal(proposal.clone(), Validity::Invalid), + expected_outputs: vec![], + expected_round: Round::new(0), + new_state: propose_state(Round::new(0)), + }, + ]; + + run_steps(&mut driver, steps); +} + fn run_steps(driver: &mut Driver, steps: Vec) { let mut last_step = Step::Unstarted; for step in steps { From 139354a789e99bbe70696b7a3c0e6d26cf6d260c Mon Sep 17 00:00:00 2001 From: Daniel Cason Date: Thu, 12 Feb 2026 18:42:33 -0800 Subject: [PATCH 7/9] driver: introduced test replaces existing one --- code/crates/core-driver/tests/it/extra.rs | 75 +---------------------- 1 file changed, 3 insertions(+), 72 deletions(-) diff --git a/code/crates/core-driver/tests/it/extra.rs b/code/crates/core-driver/tests/it/extra.rs index 03b9ed541..3b450dc03 100644 --- a/code/crates/core-driver/tests/it/extra.rs +++ b/code/crates/core-driver/tests/it/extra.rs @@ -2120,78 +2120,6 @@ fn driver_step_change_mux_with_proposal_and_polka() { run_steps(&mut driver, steps); } -#[test] -fn driver_step_change_mux_with_proposal_and_commit_quorum() { - let value: Value = Value::new(9999); - - let [(v1, _sk1), (v2, _sk2), (v3, sk3)] = make_validators([2, 3, 2]); - let (_my_sk, my_addr) = (sk3.clone(), v3.address); - - let height = Height::new(1); - let ctx = TestContext::new(); - let vs = ValidatorSet::new(vec![v1.clone(), v2.clone(), v3.clone()]); - - let proposal = Proposal::new( - Height::new(1), - Round::new(1), - value.clone(), - Round::Nil, - v1.address, - ); - - let mut driver = Driver::new(ctx, height, vs, my_addr, Default::default()); - - let steps = vec![ - TestStep { - desc: "Start round 0, we, v3, are not the proposer, start timeout propose", - input: new_round_input(Round::new(0), v1.address), - expected_outputs: vec![start_propose_timer_output(Round::new(0))], - expected_round: Round::new(0), - new_state: propose_state(Round::new(0)), - }, - TestStep { - desc: "Receive proposal for round 1, store", - input: proposal_input( - Round::new(1), - value.clone(), - Round::Nil, - Validity::Valid, - v1.address, - ), - expected_outputs: vec![], - expected_round: Round::new(0), - new_state: propose_state(Round::new(0)), - }, - TestStep { - desc: "v1 precommits value for round 1", - input: precommit_input_at(Round::new(1), value.clone(), &v1.address), - expected_outputs: vec![], - expected_round: Round::new(0), - new_state: propose_state(Round::new(0)), - }, - TestStep { - desc: "v2 precommits value for round 1, we hit f+1 threshold, move to round 1", - input: precommit_input_at(Round::new(1), value.clone(), &v2.address), - expected_outputs: vec![new_round_output(Round::new(1))], - expected_round: Round::new(1), - new_state: new_round(Round::new(1)), - }, - TestStep { - desc: - "Start round 1, change step to propose, start propose timer, mux proposal, decide", - input: new_round_input(Round::new(1), v2.address), - expected_outputs: vec![ - start_propose_timer_output(Round::new(1)), - decide_output(Round::new(1), proposal), - ], - expected_round: Round::new(1), - new_state: decided_state(Round::new(1), Round::new(1), value), - }, - ]; - - run_steps(&mut driver, steps); -} - #[test] fn proposal_mux_with_polka() { let value: Value = Value::new(9999); @@ -3646,6 +3574,9 @@ fn sync_decision_certificate_then_proposal() { run_steps(&mut driver, steps); } +/// Replaces the old driver_step_change_mux_with_proposal_and_commit_quorum. +/// When a node has SkipVote(r) and (r, PrecommitValue(v)) possible events at the same time, +/// the latter must have precedence: we decide as soon as possible, without starting rounds. #[test] fn round_1_decision_during_round_0() { let value = Value::new(9999); From 75773ece8527dd2dcd95944f8f3331156bf9f561 Mon Sep 17 00:00:00 2001 From: Daniel Cason Date: Tue, 17 Feb 2026 09:06:42 +0100 Subject: [PATCH 8/9] driver: SkipRound(r) when PrecommitValue(v, r) fails --- code/crates/core-driver/src/mux.rs | 4 ++++ code/crates/core-driver/tests/it/extra.rs | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/code/crates/core-driver/src/mux.rs b/code/crates/core-driver/src/mux.rs index a9f61d172..f62e94a23 100644 --- a/code/crates/core-driver/src/mux.rs +++ b/code/crates/core-driver/src/mux.rs @@ -310,9 +310,13 @@ where threshold_round, RoundInput::ProposalAndPrecommitValue(proposal.message.clone()), ) + } else if threshold_round > self.round() { + (threshold_round, RoundInput::SkipRound(threshold_round)) } else { (threshold_round, RoundInput::PrecommitAny) } + } else if threshold_round > self.round() { + (threshold_round, RoundInput::SkipRound(threshold_round)) } else { (threshold_round, RoundInput::PrecommitAny) } diff --git a/code/crates/core-driver/tests/it/extra.rs b/code/crates/core-driver/tests/it/extra.rs index 3b450dc03..033342b12 100644 --- a/code/crates/core-driver/tests/it/extra.rs +++ b/code/crates/core-driver/tests/it/extra.rs @@ -3574,9 +3574,9 @@ fn sync_decision_certificate_then_proposal() { run_steps(&mut driver, steps); } -/// Replaces the old driver_step_change_mux_with_proposal_and_commit_quorum. -/// When a node has SkipVote(r) and (r, PrecommitValue(v)) possible events at the same time, -/// the latter must have precedence: we decide as soon as possible, without starting rounds. +// Replaces the old driver_step_change_mux_with_proposal_and_commit_quorum. +// When a node has SkipVote(r) and (r, PrecommitValue(v)) possible events at the same time, +// the latter must have precedence: we decide as soon as possible, without starting rounds. #[test] fn round_1_decision_during_round_0() { let value = Value::new(9999); From 5d481d08498512efafbdd40be53f0a1d8fce68e9 Mon Sep 17 00:00:00 2001 From: Daniel Cason Date: Thu, 12 Feb 2026 19:18:33 -0800 Subject: [PATCH 9/9] driver: valid_proposal_for_round_and_value method * A bit less atrocious solution for the multiplex method --- code/crates/core-driver/src/driver.rs | 16 ++++++++++++++++ code/crates/core-driver/src/mux.rs | 17 +++++------------ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/code/crates/core-driver/src/driver.rs b/code/crates/core-driver/src/driver.rs index 869bb37a7..edd1f39b0 100644 --- a/code/crates/core-driver/src/driver.rs +++ b/code/crates/core-driver/src/driver.rs @@ -303,6 +303,22 @@ where .get_proposal_and_validity_for_round_and_value(round, value_id) } + /// Returns a valid proposal for the given round and value_id, if any. + pub fn valid_proposal_for_round_and_value( + &self, + round: Round, + value_id: ValueId, + ) -> Option<&SignedProposal> { + if let Some((proposal, validity)) = + self.proposal_and_validity_for_round_and_value(round, value_id) + { + if validity.is_valid() { + return Some(proposal); + } + } + None + } + /// Returns the proposals and their validities for the given round, if any. pub fn proposals_and_validities_for_round( &self, diff --git a/code/crates/core-driver/src/mux.rs b/code/crates/core-driver/src/mux.rs index f62e94a23..53ed47c1f 100644 --- a/code/crates/core-driver/src/mux.rs +++ b/code/crates/core-driver/src/mux.rs @@ -302,19 +302,12 @@ where VKOutput::PrecommitAny => (threshold_round, RoundInput::PrecommitAny), VKOutput::SkipRound(r) => (threshold_round, RoundInput::SkipRound(r)), VKOutput::PrecommitValue(v) => { - if let Some((proposal, validity)) = - self.proposal_and_validity_for_round_and_value(threshold_round, v) + if let Some(proposal) = self.valid_proposal_for_round_and_value(threshold_round, v) { - if validity.is_valid() { - ( - threshold_round, - RoundInput::ProposalAndPrecommitValue(proposal.message.clone()), - ) - } else if threshold_round > self.round() { - (threshold_round, RoundInput::SkipRound(threshold_round)) - } else { - (threshold_round, RoundInput::PrecommitAny) - } + ( + threshold_round, + RoundInput::ProposalAndPrecommitValue(proposal.message.clone()), + ) } else if threshold_round > self.round() { (threshold_round, RoundInput::SkipRound(threshold_round)) } else {