diff --git a/node/src/components/consensus/protocols/zug.rs b/node/src/components/consensus/protocols/zug.rs index 121169410b..7c81b488e4 100644 --- a/node/src/components/consensus/protocols/zug.rs +++ b/node/src/components/consensus/protocols/zug.rs @@ -1523,12 +1523,18 @@ impl Zug { /// Adds a signed message content to the state. /// Does not call `update` and does not detect faults. fn add_content(&mut self, signed_msg: SignedMessage) -> bool { - if self.active[signed_msg.validator_idx].is_none() { + if self.active[signed_msg.validator_idx] + .as_ref() + .map_or(true, |old_msg| old_msg.round_id < signed_msg.round_id) + { + if self.active[signed_msg.validator_idx].is_none() { + // We considered this validator inactive until now, and didn't accept proposals that + // didn't have them in the `inactive` field. Mark all relevant rounds as dirty so + // that the next `update` call checks all proposals again. + self.mark_dirty(self.first_non_finalized_round_id); + } + // Save the latest signed message for participation tracking purposes. self.active[signed_msg.validator_idx] = Some(signed_msg.clone()); - // We considered this validator inactive until now, and didn't accept proposals that - // didn't have them in the `inactive` field. Mark all relevant rounds as dirty so that - // the next `update` call checks all proposals again. - self.mark_dirty(self.first_non_finalized_round_id); } let SignedMessage { round_id, diff --git a/node/src/components/consensus/protocols/zug/participation.rs b/node/src/components/consensus/protocols/zug/participation.rs index 4ca1b22d78..f91435a17c 100644 --- a/node/src/components/consensus/protocols/zug/participation.rs +++ b/node/src/components/consensus/protocols/zug/participation.rs @@ -42,18 +42,21 @@ impl ParticipationStatus { Fault::Direct(..) => ParticipationStatus::Equivocated, }); } - // TODO: Avoid iterating over all old rounds every time we log this. - for r_id in zug.rounds.keys().rev() { - if zug.has_echoed(*r_id, idx) - || zug.has_voted(*r_id, idx) - || (zug.has_accepted_proposal(*r_id) && zug.leader(*r_id) == idx) - { - if r_id.saturating_add(2) < zug.current_round { - return Some(ParticipationStatus::LastSeenInRound(*r_id)); - } - return None; // Seen recently; considered currently active. + + let last_seen_round = zug + .active + .get(idx) + .and_then(Option::as_ref) + .map(|signed_msg| signed_msg.round_id); + match last_seen_round { + // not seen at all + None => Some(ParticipationStatus::Inactive), + // seen, but not within last 2 rounds + Some(r_id) if r_id.saturating_add(2) < zug.current_round => { + Some(ParticipationStatus::LastSeenInRound(r_id)) } + // seen recently + _ => None, } - Some(ParticipationStatus::Inactive) } }