Skip to content

Commit 426bcc6

Browse files
committed
feat: add QRInfoFeedResult and return it from feed_qr_info
For now just add logging for the result. This will also be used more in follow up PRs.
1 parent 0093278 commit 426bcc6

2 files changed

Lines changed: 89 additions & 36 deletions

File tree

dash-spv/src/sync/masternodes/sync_manager.rs

Lines changed: 48 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -235,43 +235,48 @@ impl<H: BlockHeaderStorage> SyncManager for MasternodesManager<H> {
235235
tracing::info!("Fed {} block heights to engine", fed);
236236

237237
// Feed QRInfo to engine first to populate masternode lists
238-
if let Err(e) = engine.feed_qr_info(qr_info.clone(), true, true) {
239-
// Check if this is a tip ChainLock error (h - 0 means the tip block)
240-
// The QRInfo response always includes `mn_list_diff_tip` which is the current
241-
// chain tip. If the tip was just mined, the ChainLock hasn't propagated yet.
242-
let is_tip_chainlock_error = matches!(
243-
e,
244-
QuorumValidationError::RequiredRotatedChainLockSigNotPresent(0, _)
245-
);
246-
247-
if is_tip_chainlock_error {
248-
self.sync_state.qrinfo_retry_count += 1;
238+
let summary = match engine.feed_qr_info(qr_info.clone(), true, true) {
239+
Ok(summary) => summary,
240+
Err(e) => {
241+
// Check if this is a tip ChainLock error (h - 0 means the tip block)
242+
// The QRInfo response always includes `mn_list_diff_tip` which is the
243+
// current chain tip. If the tip was just mined, the ChainLock hasn't
244+
// propagated yet.
245+
let is_tip_chainlock_error = matches!(
246+
e,
247+
QuorumValidationError::RequiredRotatedChainLockSigNotPresent(0, _)
248+
);
249249

250-
if self.sync_state.qrinfo_retry_count <= MAX_RETRY_ATTEMPTS {
251-
tracing::info!(
252-
"ChainLock not yet available for tip, scheduling retry {}/{} in {}s",
253-
self.sync_state.qrinfo_retry_count,
254-
MAX_RETRY_ATTEMPTS,
255-
CHAINLOCK_RETRY_DELAY_SECS
256-
);
257-
// Schedule a delayed retry - the tick handler will trigger it
258-
self.sync_state.chainlock_retry_after = Some(
259-
Instant::now() + Duration::from_secs(CHAINLOCK_RETRY_DELAY_SECS),
260-
);
261-
drop(engine);
262-
self.set_state(SyncState::Syncing);
263-
return Ok(vec![]);
250+
if is_tip_chainlock_error {
251+
self.sync_state.qrinfo_retry_count += 1;
252+
253+
if self.sync_state.qrinfo_retry_count <= MAX_RETRY_ATTEMPTS {
254+
tracing::info!(
255+
"ChainLock not yet available for tip, scheduling retry {}/{} in {}s",
256+
self.sync_state.qrinfo_retry_count,
257+
MAX_RETRY_ATTEMPTS,
258+
CHAINLOCK_RETRY_DELAY_SECS
259+
);
260+
// Schedule a delayed retry - the tick handler will trigger it
261+
self.sync_state.chainlock_retry_after = Some(
262+
Instant::now()
263+
+ Duration::from_secs(CHAINLOCK_RETRY_DELAY_SECS),
264+
);
265+
drop(engine);
266+
self.set_state(SyncState::Syncing);
267+
return Ok(vec![]);
268+
}
264269
}
265-
}
266270

267-
// For other errors or max retries reached, fail
268-
tracing::error!(
269-
"QRInfo failed after {} retries: {}",
270-
self.sync_state.qrinfo_retry_count,
271-
e
272-
);
273-
return Err(SyncError::MasternodeSyncFailed(e.to_string()));
274-
}
271+
// For other errors or max retries reached, fail
272+
tracing::error!(
273+
"QRInfo failed after {} retries: {}",
274+
self.sync_state.qrinfo_retry_count,
275+
e
276+
);
277+
return Err(SyncError::MasternodeSyncFailed(e.to_string()));
278+
}
279+
};
275280

276281
// Populate known_mn_list_heights from engine after QRInfo processing
277282
self.sync_state.known_mn_list_heights =
@@ -296,6 +301,15 @@ impl<H: BlockHeaderStorage> SyncManager for MasternodesManager<H> {
296301
drop(engine);
297302
drop(storage);
298303

304+
if let Some(summary) = summary {
305+
tracing::info!(
306+
"QRInfo processed: stored_cycle_height={:?}, rotated_quorum_count={}, freshly_validated_count={}",
307+
summary.stored_cycle_height,
308+
summary.rotated_quorum_count,
309+
summary.freshly_validated_count,
310+
);
311+
}
312+
299313
// Queue and send MnListDiff requests via pipeline
300314
self.sync_state.mnlistdiff_pipeline.queue_requests(request_pairs);
301315
self.sync_state.mnlistdiff_pipeline.send_pending(requests)?;

dash/src/sml/masternode_list_engine/mod.rs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,25 @@ use serde::{Deserialize, Serialize};
3737
/// The mnListDiffH in QRInfo is at (cycle_height - WORK_DIFF_DEPTH), not at the cycle boundary itself
3838
pub const WORK_DIFF_DEPTH: u32 = 8;
3939

40+
/// Callers can use this to log what happened during the call, or to decide whether a
41+
/// rotation cycle has been freshly validated this round.
42+
#[derive(Clone, Debug, PartialEq, Eq)]
43+
pub struct QRInfoFeedResult {
44+
/// Total number of rotated quorums in `last_commitment_per_index` for this QRInfo.
45+
pub rotated_quorum_count: usize,
46+
/// Rotated quorums that went through the fresh-validation path this call, using
47+
/// the four rotation CL signatures supplied by this QRInfo.
48+
pub freshly_validated_count: usize,
49+
/// Height of the cycle under which rotated quorums were stored in
50+
/// `rotated_quorums_per_cycle`. `None` when nothing was stored, either
51+
/// because `last_commitment_per_index` was empty or its first entry's
52+
/// `quorum_hash` could not be resolved to a height.
53+
///
54+
/// This is the first entry's `quorum_hash` height, which carries the
55+
/// previous cycle when the current cycle's DKG has not completed yet.
56+
pub stored_cycle_height: Option<CoreBlockHeight>,
57+
}
58+
4059
#[derive(Clone, Eq, PartialEq, Default)]
4160
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
4261
#[cfg_attr(feature = "bincode", derive(Encode, Decode))]
@@ -504,7 +523,7 @@ impl MasternodeListEngine {
504523
qr_info: QRInfo,
505524
verify_tip_non_rotated_quorums: bool,
506525
verify_rotated_quorums: bool,
507-
) -> Result<(), QuorumValidationError> {
526+
) -> Result<Option<QRInfoFeedResult>, QuorumValidationError> {
508527
#[allow(unused_variables)]
509528
let QRInfo {
510529
quorum_snapshot_at_h_minus_c,
@@ -552,6 +571,14 @@ impl MasternodeListEngine {
552571
.map(|quorum_entry| quorum_entry.llmq_type)
553572
.unwrap_or(self.network.isd_llmq_type());
554573

574+
#[cfg(feature = "quorum_validation")]
575+
let stored_cycle_height = last_commitment_per_index
576+
.first()
577+
.and_then(|q| self.block_container.get_height(&q.quorum_hash));
578+
#[cfg(feature = "quorum_validation")]
579+
let rotated_quorum_count = last_commitment_per_index.len();
580+
#[cfg(feature = "quorum_validation")]
581+
let mut freshly_validated_count: usize = 0;
555582
if let Some((quorum_snapshot_at_h_minus_4c, mn_list_diff_at_h_minus_4c)) =
556583
quorum_snapshot_and_mn_list_diff_at_h_minus_4c
557584
{
@@ -597,6 +624,7 @@ impl MasternodeListEngine {
597624
{
598625
Ok(qualified_quorum_entry)
599626
} else {
627+
freshly_validated_count += 1;
600628
let sigm2 = maybe_sigm2.ok_or(
601629
QuorumValidationError::RequiredRotatedChainLockSigNotPresent(
602630
3,
@@ -795,7 +823,18 @@ impl MasternodeListEngine {
795823
));
796824
}
797825

798-
Ok(())
826+
#[cfg(feature = "quorum_validation")]
827+
{
828+
Ok(Some(QRInfoFeedResult {
829+
rotated_quorum_count,
830+
freshly_validated_count,
831+
stored_cycle_height,
832+
}))
833+
}
834+
#[cfg(not(feature = "quorum_validation"))]
835+
{
836+
Ok(None)
837+
}
799838
}
800839

801840
/// Applies a masternode list diff to create or update a masternode list.

0 commit comments

Comments
 (0)