fix: index rotated quorums by quorum_index and rebuild per cycle#97
fix: index rotated quorums by quorum_index and rebuild per cycle#97xdustinface wants to merge 4 commits intov0.42-devfrom
quorum_index and rebuild per cycle#97Conversation
|
Manki — Review complete Planner (24s) Review — 17 findings Judge — 8 kept · 0 dropped (83s) Review metadataConfig:
Judge decisions:
Timing:
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## v0.42-dev #97 +/- ##
=============================================
+ Coverage 67.84% 67.86% +0.02%
=============================================
Files 318 318
Lines 67784 67832 +48
=============================================
+ Hits 45988 46036 +48
Misses 21796 21796
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Three blockers need fixing before merge: a negative quorum_index from the network wraps silently to a phantom u16 key (DoS vector for InstantSend rotation), cargo test --release fails because the should_panic test expects a debug_assert that's a no-op in release, and the test module is missing #[cfg(feature = "quorum_validation")] which breaks compilation without that feature flag.
📊 8 findings (3 required, 2 suggestion, 3 nit) · 253 lines · 342s
Review stats
{
"model": "claude-sonnet-4-6",
"reviewTimeMs": 342054,
"diffLines": 253,
"diffAdditions": 181,
"diffDeletions": 72,
"filesReviewed": 4,
"agents": [
"Security & Safety",
"Correctness & Logic",
"Architecture & Design",
"Testing & Coverage"
],
"findingsRaw": 17,
"findingsKept": 8,
"findingsDropped": 9,
"severity": {
"required": 3,
"suggestion": 2,
"nit": 3
},
"verdict": "REQUEST_CHANGES",
"prNumber": 97,
"commitSha": "69e18e91b2e224936ed0fb9a7299fe26b8ecb5cf",
"agentMetrics": [
{
"name": "Security & Safety",
"findingsRaw": 4,
"findingsKept": 3,
"responseLength": 3864
},
{
"name": "Correctness & Logic",
"findingsRaw": 5,
"findingsKept": 3,
"responseLength": 5882
},
{
"name": "Architecture & Design",
"findingsRaw": 4,
"findingsKept": 3,
"responseLength": 3902
},
{
"name": "Testing & Coverage",
"findingsRaw": 4,
"findingsKept": 3,
"responseLength": 4278
}
],
"judgeMetrics": {
"confidenceDistribution": {
"high": 5,
"medium": 3,
"low": 0
},
"severityChanges": 8,
"mergedDuplicates": 9
},
"fileMetrics": {
"fileTypes": {
".rs": 3,
".hex": 1
},
"findingsPerFile": {
"dash/src/sml/masternode_list_engine/mod.rs": 7,
"dash/src/sml/masternode_list_engine/message_request_verification.rs": 1
}
},
"reviewerModel": "claude-sonnet-4-6",
"judgeModel": "claude-sonnet-4-6"
}|
Manki — Review complete Planner (24s) Review — 17 findings Judge — 12 kept · 0 dropped (131s) Review metadataConfig:
Judge decisions:
Timing:
|
There was a problem hiding this comment.
Negative-index wrapping and debug-only duplicate detection from the previous review are both resolved. The new regression test is missing its #[cfg(feature = "quorum_validation")] guard — a definite compile failure in non-validation builds — and TooManyRotatedQuorumsInCycle is inserted mid-enum in a bincode-serialized type, silently shifting discriminants for all subsequent variants.
📊 12 findings (1 required, 6 suggestion, 5 nit) · 360 lines · 371s
Review stats
{
"model": "claude-sonnet-4-6",
"reviewTimeMs": 371372,
"diffLines": 360,
"diffAdditions": 286,
"diffDeletions": 74,
"filesReviewed": 4,
"agents": [
"Security & Safety",
"Correctness & Logic",
"Architecture & Design",
"Testing & Coverage",
"Dependencies & Integration"
],
"findingsRaw": 17,
"findingsKept": 12,
"findingsDropped": 5,
"severity": {
"required": 1,
"suggestion": 6,
"nit": 5
},
"verdict": "REQUEST_CHANGES",
"prNumber": 97,
"commitSha": "6a4f4e0853d798a475d69e283850c78484407175",
"agentMetrics": [
{
"name": "Security & Safety",
"findingsRaw": 4,
"findingsKept": 4,
"responseLength": 3495
},
{
"name": "Correctness & Logic",
"findingsRaw": 2,
"findingsKept": 1,
"responseLength": 2416
},
{
"name": "Architecture & Design",
"findingsRaw": 4,
"findingsKept": 3,
"responseLength": 3859
},
{
"name": "Testing & Coverage",
"findingsRaw": 3,
"findingsKept": 3,
"responseLength": 3333
},
{
"name": "Dependencies & Integration",
"findingsRaw": 4,
"findingsKept": 4,
"responseLength": 4644
}
],
"judgeMetrics": {
"confidenceDistribution": {
"high": 8,
"medium": 4,
"low": 0
},
"severityChanges": 12,
"mergedDuplicates": 5
},
"fileMetrics": {
"fileTypes": {
".rs": 3,
".hex": 1
},
"findingsPerFile": {
"dash/src/sml/masternode_list_engine/mod.rs": 10,
"dash/src/sml/quorum_validation_error.rs": 1,
"dash/src/sml/masternode_list_engine/message_request_verification.rs": 1
}
},
"reviewerModel": "claude-sonnet-4-6",
"judgeModel": "claude-sonnet-4-6"
}|
Manki — Review complete Planner Review — 15 findings Judge — 6 kept · 0 dropped (122s) Review metadataConfig:
Judge decisions:
Timing:
|
942b341 to
0ea93ca
Compare
|
@manki review |
|
Review skipped — a review is currently in progress. Retry when it completes, or force now:
|
|
Manki — Review complete Planner (24s) Review — 6 findings Judge — 3 kept · 0 dropped (455s) Review metadataConfig:
Judge decisions:
Timing:
|
There was a problem hiding this comment.
The staging-map approach cleanly fixes the partial-write issue (PRRT_kwDOQSlaXs56Wvno) and the new test now carries the correct cfg guard (PRRT_kwDOQSlaXs56Wvns). The bincode discriminant problem from last review is still present and now flagged by all 5 specialists; the count-completeness check asymmetry on the None branch also carries over unresolved.
📊 6 findings (2 suggestion, 4 nit) · 379 lines · 648s
Review stats
{
"model": "claude-sonnet-4-6",
"reviewTimeMs": 648218,
"diffLines": 379,
"diffAdditions": 304,
"diffDeletions": 75,
"filesReviewed": 5,
"agents": [
"Security & Safety",
"Architecture & Design",
"Correctness & Logic",
"Testing & Coverage",
"Performance & Efficiency"
],
"findingsRaw": 15,
"findingsKept": 6,
"findingsDropped": 9,
"severity": {
"required": 0,
"suggestion": 2,
"nit": 4
},
"verdict": "REQUEST_CHANGES",
"prNumber": 97,
"commitSha": "942b34105997caace91b9664035c8230b9ce63eb",
"agentMetrics": [
{
"name": "Security & Safety",
"findingsRaw": 2,
"findingsKept": 0,
"responseLength": 2258
},
{
"name": "Architecture & Design",
"findingsRaw": 3,
"findingsKept": 2,
"responseLength": 2463
},
{
"name": "Correctness & Logic",
"findingsRaw": 3,
"findingsKept": 3,
"responseLength": 3082
},
{
"name": "Testing & Coverage",
"findingsRaw": 3,
"findingsKept": 1,
"responseLength": 4353
},
{
"name": "Performance & Efficiency",
"findingsRaw": 4,
"findingsKept": 3,
"responseLength": 4584
}
],
"judgeMetrics": {
"confidenceDistribution": {
"high": 3,
"medium": 2,
"low": 1
},
"severityChanges": 6,
"mergedDuplicates": 7
},
"fileMetrics": {
"fileTypes": {
".rs": 4,
".hex": 1
},
"findingsPerFile": {
"dash/src/sml/quorum_validation_error.rs": 1,
"dash/src/sml/masternode_list_engine/mod.rs": 4,
"dash/src/lib.rs": 1
}
},
"reviewerModel": "claude-sonnet-4-6",
"judgeModel": "claude-sonnet-4-6"
}There was a problem hiding this comment.
The or_default() empty-entry leak (Finding 1) persists despite the tmp_cycle_map refactor — switching to a staging map only helps re-feed paths, not brand-new cycle hashes. The prior required finding (PRRT_kwDOQSlaXs56Wvns) about the regression test at line 1441 missing its #[cfg(feature = "quorum_validation")] guard is still unaddressed and was missed by all three current reviewers.
📊 3 findings (1 suggestion, 2 nit) · 377 lines · 1013s
Review stats
{
"model": "claude-sonnet-4-6",
"reviewTimeMs": 1012532,
"diffLines": 377,
"diffAdditions": 304,
"diffDeletions": 73,
"filesReviewed": 4,
"agents": [
"Security & Safety",
"Correctness & Logic",
"Testing & Coverage"
],
"findingsRaw": 6,
"findingsKept": 3,
"findingsDropped": 3,
"severity": {
"required": 0,
"suggestion": 1,
"nit": 2
},
"verdict": "APPROVE",
"prNumber": 97,
"commitSha": "0ea93ca27f5c56829210bf64e5ee926a5c5a0375",
"agentMetrics": [
{
"name": "Security & Safety",
"findingsRaw": 2,
"findingsKept": 2,
"responseLength": 2924
},
{
"name": "Correctness & Logic",
"findingsRaw": 1,
"findingsKept": 0,
"responseLength": 1180
},
{
"name": "Testing & Coverage",
"findingsRaw": 3,
"findingsKept": 1,
"responseLength": 2928
}
],
"judgeMetrics": {
"confidenceDistribution": {
"high": 2,
"medium": 1,
"low": 0
},
"severityChanges": 3,
"mergedDuplicates": 0
},
"fileMetrics": {
"fileTypes": {
".rs": 3,
".hex": 1
},
"findingsPerFile": {
"dash/src/sml/masternode_list_engine/mod.rs": 3
}
},
"reviewerModel": "claude-sonnet-4-6",
"judgeModel": "claude-sonnet-4-6"
}* chore: add Manki AI code review configuration * fix: pass `claude_code_oauth_token` to Manki action
0ea93ca to
25d0f45
Compare
|
Manki — Review complete Planner Review — 12 findings Judge — 6 kept · 0 dropped (102s) Review metadataConfig:
Judge decisions:
Timing:
|
There was a problem hiding this comment.
No required fixes — the core indexing change (BTreeMap<u16, QualifiedQuorumEntry> keyed by quorum_index, cycle rebuild, duplicate detection) is structurally sound. The one actionable item is the misleading error variant in build_cycle_quorum_map that conflates a missing index with a negative one, which will frustrate future incident triage in this consensus-adjacent code.
📊 6 findings (1 suggestion, 5 nit) · 243 lines · 400s
Review stats
{
"model": "claude-sonnet-4-6",
"reviewTimeMs": 399615,
"diffLines": 243,
"diffAdditions": 155,
"diffDeletions": 88,
"filesReviewed": 3,
"agents": [
"Security & Safety",
"Architecture & Design",
"Correctness & Logic",
"Testing & Coverage",
"Performance & Efficiency"
],
"findingsRaw": 12,
"findingsKept": 6,
"findingsDropped": 6,
"severity": {
"required": 0,
"suggestion": 1,
"nit": 5
},
"verdict": "REQUEST_CHANGES",
"prNumber": 97,
"commitSha": "25d0f452e09e8189e640c489a61b0b9eaeb3efe6",
"agentMetrics": [
{
"name": "Security & Safety",
"findingsRaw": 3,
"findingsKept": 0,
"responseLength": 3571
},
{
"name": "Architecture & Design",
"findingsRaw": 2,
"findingsKept": 1,
"responseLength": 3645
},
{
"name": "Correctness & Logic",
"findingsRaw": 3,
"findingsKept": 3,
"responseLength": 2778
},
{
"name": "Testing & Coverage",
"findingsRaw": 2,
"findingsKept": 1,
"responseLength": 1877
},
{
"name": "Performance & Efficiency",
"findingsRaw": 2,
"findingsKept": 2,
"responseLength": 1526
}
],
"judgeMetrics": {
"confidenceDistribution": {
"high": 5,
"medium": 1,
"low": 0
},
"severityChanges": 6,
"mergedDuplicates": 2
},
"fileMetrics": {
"fileTypes": {
".rs": 2,
".hex": 1
},
"findingsPerFile": {
"dash/src/sml/masternode_list_engine/message_request_verification.rs": 1,
"dash/src/sml/masternode_list_engine/mod.rs": 5
}
},
"reviewerModel": "claude-sonnet-4-6",
"judgeModel": "claude-sonnet-4-6"
}The `Vec<QualifiedQuorumEntry>` had no size bound and no uniqueness check on `quorum_index`. Combined with QRInfo still being requested on every new block (will change soon), repeated `feed_qr_info` calls within a single rotation cycle accumulated duplicate entries that the lookup then returned as stale matches. - Switch the inner container to `BTreeMap<u16, QualifiedQuorumEntry>` keyed by `quorum_index` so insertion replaces by key, lookup is direct, and re-feeds of the same cycle are not causing wrong behaviour anymore. - Introduce `build_cycle_quorum_map` to centralize map construction with index validation and count checks. Both branches of `feed_qr_info` use it to rebuild the per-cycle map from scratch. - Migrates `tests/data/test_DML_diffs/masternode_list_engine.hex` to the new format.
The existing count check allowed a set like `{1, 2, ..., expected}` to pass - distinct indices with the right total, but missing index 0 and containing one out-of-range index. Downstream code assumes contiguous `[0, expected)` coverage and breaks on direct `get(&i)` lookups. Add a per-entry upper-bound guard inside the loop so the offending index is reported precisely; combined with the existing no-duplicates and count checks, this guarantees `keys == {0, 1, ..., expected - 1}` exactly.
Addresses CodeRabbit review comment on PR dashpay#637
dashpay#637 (comment)
25d0f45 to
d6d04a3
Compare
|
Manki — Review complete Planner (29s) Review — 7 findings Judge — 2 kept · 1 dropped (69s) Review metadataConfig:
Judge decisions:
Timing:
|
There was a problem hiding this comment.
The previously open thread on misleading error variant is cleanly resolved — InvalidQuorumIndex now correctly handles both the negative-index and out-of-bounds cases. One real test coverage gap remains: the bounds check branch (key >= expected) in build_cycle_quorum_map is never exercised despite the surrounding cases being covered.
📊 2 findings (1 suggestion, 1 nit) · 263 lines · 473s
Review stats
{
"model": "claude-sonnet-4-6",
"reviewTimeMs": 472568,
"diffLines": 263,
"diffAdditions": 175,
"diffDeletions": 88,
"filesReviewed": 4,
"agents": [
"Security & Safety",
"Correctness & Logic",
"Testing & Coverage"
],
"findingsRaw": 7,
"findingsKept": 2,
"findingsDropped": 5,
"severity": {
"required": 0,
"suggestion": 1,
"nit": 1
},
"verdict": "REQUEST_CHANGES",
"prNumber": 97,
"commitSha": "d6d04a30eb6e6e81cecea936ae7d2d93c77916e0",
"agentMetrics": [
{
"name": "Security & Safety",
"findingsRaw": 3,
"findingsKept": 0,
"responseLength": 3311
},
{
"name": "Correctness & Logic",
"findingsRaw": 1,
"findingsKept": 0,
"responseLength": 1228
},
{
"name": "Testing & Coverage",
"findingsRaw": 3,
"findingsKept": 2,
"responseLength": 3528
}
],
"judgeMetrics": {
"confidenceDistribution": {
"high": 2,
"medium": 1,
"low": 0
},
"severityChanges": 3,
"mergedDuplicates": 0
},
"fileMetrics": {
"fileTypes": {
".rs": 3,
".hex": 1
},
"findingsPerFile": {
"dash/src/sml/masternode_list_engine/mod.rs": 2
}
},
"reviewerModel": "claude-sonnet-4-6",
"judgeModel": "claude-sonnet-4-6"
}|
|
||
| #[cfg(feature = "quorum_validation")] | ||
| #[test] | ||
| fn build_cycle_quorum_map_edge_cases() { |
There was a problem hiding this comment.
💡 Suggestion [high confidence]: Missing test for out-of-bounds quorum_index in build_cycle_quorum_map
The build_cycle_quorum_map_edge_cases test covers five cases (valid, missing index, negative index, duplicate index, wrong count) but omits the distinct branch at line 219: if (key as usize) >= expected. For LlmqtypeTest with active_quorum_count==2, an entry with quorum_index=2 is a valid u16 and non-negative, so the u16::try_from path succeeds, but the bounds check should reject it with InvalidQuorumIndex. This code path is entirely untested, leaving a gap in coverage for a security-relevant validation function.
Suggested fix
| fn build_cycle_quorum_map_edge_cases() { | |
| // Out-of-bounds index is rejected (valid u16 but >= active_quorum_count) | |
| let quorums = vec![ | |
| make_qualified_quorum_entry(ty, Some(0)), | |
| make_qualified_quorum_entry(ty, Some(2)), // 2 >= count of 2 | |
| ]; | |
| let err = build_cycle_quorum_map(quorums, ty).expect_err("out-of-bounds index should fail"); | |
| assert!(matches!( | |
| err, | |
| QuorumValidationError::InvalidQuorumIndex { index: 2, .. } | |
| )); |
AI context
{
"file": "dash/src/sml/masternode_list_engine/mod.rs",
"line": 1232,
"severity": "suggestion",
"confidence": "high",
"flaggedBy": [
"Testing & Coverage"
],
"title": "Missing test for out-of-bounds quorum_index in build_cycle_quorum_map",
"fix": "// Out-of-bounds index is rejected (valid u16 but >= active_quorum_count)\nlet quorums = vec![\n make_qualified_quorum_entry(ty, Some(0)),\n make_qualified_quorum_entry(ty, Some(2)), // 2 >= count "
}|
This PR has merge conflicts with the base branch. Please rebase or merge the base branch into your branch to resolve them. |
The
Vec<QualifiedQuorumEntry>had no size bound and no uniqueness check onquorum_index. Combined with QRInfo still being requested on every new block (will change soon), repeatedfeed_qr_infocalls within a single rotation cycle accumulated duplicate entries that the lookup then returned as stale matches.BTreeMap<u16, QualifiedQuorumEntry>keyed byquorum_indexso insertion replaces by key, lookup is direct, and re-feeds of the same cycle are not causing wrong behaviour anymore.insert_cycle_quorum_by_indexas helper to centralize inserts.feed_qr_infonow.clear()the per-cycle map before refilling it and clearing is enforced via a debug assert.tests/data/test_DML_diffs/masternode_list_engine.hexto the new format.