Skip to content

Commit 1b742e1

Browse files
committed
refactor(dash): extract store_cycle_if_fully_verified from feed_qr_info
Both the `verify_rotated_quorums == true` and `== false` branches in `feed_qr_info` had identical logic for gating storage of a rotation cycle into `rotated_quorums_per_cycle`: check that every entry is `Verified`, refuse to overwrite a cycle that is already fully verified, then build the cycle map and record the stored height. Move that logic into a single helper so the two call sites stay aligned.
1 parent 111897b commit 1b742e1

1 file changed

Lines changed: 47 additions & 46 deletions

File tree

  • dash/src/sml/masternode_list_engine

dash/src/sml/masternode_list_engine/mod.rs

Lines changed: 47 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,43 @@ impl MasternodeListEngine {
707707
hashes
708708
}
709709

710+
/// `rotated_quorums_per_cycle` is the authoritative map for IS lock
711+
/// verification, so only store a cycle when every entry is `Verified`.
712+
/// Skipped entries (e.g. from incomplete CL sigs or missing context) cannot
713+
/// sign and must not enter the map. A later QRInfo with complete context
714+
/// will store the cycle. Also preserve an already-fully-Verified cycle
715+
/// across subsequent QRInfo responses: a thin `mn_list_diff_h` can produce
716+
/// Skipped entries, and a `verify_rotated_quorums == false` call can leave
717+
/// entries unverified, neither of which must downgrade it.
718+
///
719+
/// Returns the height of the stored cycle, or `None` if storage was
720+
/// skipped because the gate did not fire.
721+
#[cfg(feature = "quorum_validation")]
722+
fn store_cycle_if_fully_verified(
723+
&mut self,
724+
cycle_key: BlockHash,
725+
qualified_last_commitment_per_index: Vec<QualifiedQuorumEntry>,
726+
rotation_quorum_type: LLMQType,
727+
) -> Result<Option<CoreBlockHeight>, QuorumValidationError> {
728+
let already_fully_verified =
729+
self.rotated_quorums_per_cycle.get(&cycle_key).is_some_and(|existing| {
730+
!existing.is_empty()
731+
&& existing
732+
.values()
733+
.all(|q| matches!(q.verified, LLMQEntryVerificationStatus::Verified))
734+
});
735+
let all_entries_verified = qualified_last_commitment_per_index
736+
.iter()
737+
.all(|q| matches!(q.verified, LLMQEntryVerificationStatus::Verified));
738+
if !all_entries_verified || already_fully_verified {
739+
return Ok(None);
740+
}
741+
let cycle_map =
742+
build_cycle_quorum_map(qualified_last_commitment_per_index, rotation_quorum_type)?;
743+
*self.rotated_quorums_per_cycle.entry(cycle_key).or_default() = cycle_map;
744+
Ok(self.block_container.get_height(&cycle_key))
745+
}
746+
710747
/// Processes and applies a QRInfo message to the masternode list engine.
711748
///
712749
/// The caller is expected to pre-populate [`Self::block_container`] with heights
@@ -974,32 +1011,11 @@ impl MasternodeListEngine {
9741011
.count();
9751012

9761013
if let Some(key) = cycle_key {
977-
// `rotated_quorums_per_cycle` is the authoritative map for IS
978-
// lock verification, so only store a cycle when every entry is
979-
// `Verified`. Skipped entries (e.g. from incomplete CL sigs or
980-
// missing context) cannot sign and must not enter the map. A
981-
// later QRInfo with complete context will store the cycle.
982-
// Also preserve an already-fully-Verified cycle across
983-
// subsequent QRInfo responses: a thin `mn_list_diff_h` can
984-
// produce Skipped entries that must not downgrade it.
985-
let already_fully_verified =
986-
self.rotated_quorums_per_cycle.get(&key).is_some_and(|existing| {
987-
!existing.is_empty()
988-
&& existing.values().all(|q| {
989-
matches!(q.verified, LLMQEntryVerificationStatus::Verified)
990-
})
991-
});
992-
let all_entries_verified = qualified_last_commitment_per_index
993-
.iter()
994-
.all(|q| matches!(q.verified, LLMQEntryVerificationStatus::Verified));
995-
if all_entries_verified && !already_fully_verified {
996-
let cycle_map = build_cycle_quorum_map(
997-
qualified_last_commitment_per_index,
998-
rotation_quorum_type,
999-
)?;
1000-
*self.rotated_quorums_per_cycle.entry(key).or_default() = cycle_map;
1001-
stored_cycle_height = self.block_container.get_height(&key);
1002-
}
1014+
stored_cycle_height = self.store_cycle_if_fully_verified(
1015+
key,
1016+
qualified_last_commitment_per_index,
1017+
rotation_quorum_type,
1018+
)?;
10031019
}
10041020

10051021
// Apply collected updates after iteration to avoid borrow conflicts
@@ -1098,26 +1114,11 @@ impl MasternodeListEngine {
10981114
.iter()
10991115
.filter(|q| matches!(q.verified, LLMQEntryVerificationStatus::Verified))
11001116
.count();
1101-
// Never overwrite an already-fully-Verified cycle with unverified entries from a
1102-
// `verify_rotated_quorums == false` call.
1103-
let already_fully_verified =
1104-
self.rotated_quorums_per_cycle.get(&cycle_key).is_some_and(|existing| {
1105-
!existing.is_empty()
1106-
&& existing
1107-
.values()
1108-
.all(|q| matches!(q.verified, LLMQEntryVerificationStatus::Verified))
1109-
});
1110-
let all_entries_verified = qualified_last_commitment_per_index
1111-
.iter()
1112-
.all(|q| matches!(q.verified, LLMQEntryVerificationStatus::Verified));
1113-
if all_entries_verified && !already_fully_verified {
1114-
let cycle_map = build_cycle_quorum_map(
1115-
qualified_last_commitment_per_index,
1116-
rotation_quorum_type,
1117-
)?;
1118-
*self.rotated_quorums_per_cycle.entry(cycle_key).or_default() = cycle_map;
1119-
stored_cycle_height = self.block_container.get_height(&cycle_key);
1120-
}
1117+
stored_cycle_height = self.store_cycle_if_fully_verified(
1118+
cycle_key,
1119+
qualified_last_commitment_per_index,
1120+
rotation_quorum_type,
1121+
)?;
11211122
}
11221123

11231124
#[cfg(not(feature = "quorum_validation"))]

0 commit comments

Comments
 (0)