Skip to content

fix: validate mirror quorum against snapshot weights#76

Merged
JakeHartnell merged 2 commits into
v1from
fix/mirror-quorum-snapshot-v1
May 9, 2026
Merged

fix: validate mirror quorum against snapshot weights#76
JakeHartnell merged 2 commits into
v1from
fix/mirror-quorum-snapshot-v1

Conversation

@layertau

@layertau layertau commented May 9, 2026

Copy link
Copy Markdown

Summary

Fixes the two high-severity mirror quorum findings from the v1 security audit:

  • Use cross-multiplication for mirror service-manager quorum checks instead of floor-rounded threshold division.
  • Load mirror stake-registry total voting power at signature_data.reference_block, matching signer and operator weight snapshot reads.

Security impact

Prevents under-quorum acceptance for non-divisible totals such as 1-of-2 passing a 2/3 threshold, and prevents historical envelopes from being validated against latest total voting power while signer weights are evaluated at the reference block.

Testing

  • cargo fmt
  • cargo test --workspace --all-targets attempted, but the sandbox is missing OpenSSL pkg-config metadata required by openssl-sys:
    • Could not find openssl via pkg-config
    • No package 'openssl' found

@layertau

layertau commented May 9, 2026

Copy link
Copy Markdown
Author

Reviewer note (unable to submit a formal "request changes" review because GitHub treats this as my own PR):

Thanks for turning around the audit fixes quickly. High-1 is addressed correctly by switching the mirror service-manager quorum check to cross multiplication, which avoids the floor-rounding bypass.

I recommend changes for High-2: the PR currently calls TOTAL_WEIGHT.may_load_at_height(...), but packages/contracts/mirror/stake-registry/src/state.rs still defines TOTAL_WEIGHT as Item<Uint256>, not a SnapshotItem<Uint256>. As written, this should not compile and also does not actually persist historical total-weight snapshots.

Required fix:

  • Change mirror stake-registry TOTAL_WEIGHT to SnapshotItem<Uint256> with checkpoint/changelog namespaces, matching the ECDSA/BLS service-manager pattern.
  • Save total weight with the same snapshot_height used by operator weight updates, including instantiate and set_operator_details_at.
  • Keep query_validate_signature loading TOTAL_WEIGHT at reference_block.
  • Add/confirm regression tests for total weight decreasing and increasing after reference_block.

Validation performed locally:

  • cargo fmt --all -- --check passed.
  • cargo check -p cw-wavs-mirror-stake-registry did not reach this specific compile error because the checkout is missing node_modules/@wavs/solidity/... files required by alloy_sol_types::sol!. Earlier full workspace test attempts were also blocked by sandbox dependency setup. Source inspection is sufficient to identify the Item vs SnapshotItem issue.

Once TOTAL_WEIGHT is snapshotted and tests are added/updated, I can re-review.

@layertau

layertau commented May 9, 2026

Copy link
Copy Markdown
Author

Re-review of c99b7ac (unable to submit formal approval because GitHub treats this as my own PR): the requested High-2 fix is now implemented correctly.

Verified:

  • High-1 remains fixed via cross-multiplication in mirror service-manager quorum validation, avoiding floor-rounded thresholds.
  • High-2 is now fixed: mirror stake-registry TOTAL_WEIGHT is a SnapshotItem, is saved at instantiate/update snapshot heights, and ValidateSignature reads total weight at reference_block consistently with signer/operator weights.
  • Regression coverage was added for validating a historical signature after later total-weight increase and signer-weight decrease.

Local validation:

  • cargo fmt --all -- --check passed.
  • cargo check -p cw-wavs-mirror-stake-registry passed.
  • cargo test -p off-chain-tests --test contracts_mirror -- --nocapture is still blocked in this sandbox by openssl-sys not finding OpenSSL/pkg-config metadata, but the handoff reports it passed locally with OpenSSL env configured.

CI was still pending for off-chain tests/rust linting at review time; please wait for those checks before merge.

@JakeHartnell JakeHartnell merged commit 5ff984e into v1 May 9, 2026
3 checks passed
@JakeHartnell JakeHartnell deleted the fix/mirror-quorum-snapshot-v1 branch May 9, 2026 23:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants