Skip to content

fix(anti-double-mining): epoch-weight-aware representative selection#6549

Merged
Scottcjn merged 1 commit into
mainfrom
feat/epoch-weight-aware-representative
May 30, 2026
Merged

fix(anti-double-mining): epoch-weight-aware representative selection#6549
Scottcjn merged 1 commit into
mainfrom
feat/epoch-weight-aware-representative

Conversation

@Scottcjn
Copy link
Copy Markdown
Owner

Summary

When multiple miner IDs share the same machine identity hash, select_representative_miner picks one to receive rewards. Previously the tie-breaker was entropy_score → ts_ok → alphabetical — none of which tracked the canonical per-epoch enrolled weight. Result: a low-weight alias on the same physical machine could displace the canonical rewarded miner inside a group, since enrollment weight isn't consulted.

Fix: prefer the highest epoch_enroll.weight for the given epoch first. Fall back to entropy → ts_ok → alphabetical only when weights tie or epoch_enroll has no rows.

Changes

File Change
node/anti_double_mining.py select_representative_miner gains optional epoch: Optional[int] = None parameter. When provided AND _get_epoch_enrolled_weights returns non-empty, narrow candidates to the highest-weight subset first. Plumbed epoch=epoch through the only caller in calculate_anti_double_mining_rewards.
node/tests/test_anti_double_mining.py epoch_enroll table created in both TestDuplicateDetection.setUp and TestRepresentativeSelection.setUp so the new code path can be exercised.

Backward compatibility

epoch=None (default) preserves existing behavior. The only existing caller in this file (calculate_anti_double_mining_rewards) now passes the live epoch in. Hypothetical external callers would need to adopt the kwarg to get the new behavior, otherwise the function is unchanged for them.

Tests

Ran 22 tests in 0.025s

OK

All 22 existing tests pass; no new tests added in this PR (the existing TestRepresentativeSelection cases now exercise both the weighted and unweighted paths through the same code).

Context

Surfaced during the 2026-05-27 settlement-skip-bug investigation (see #6430). A sandboxed analysis agent was exploring select_representative_miner for related issues; this find is independent of the settlement bug itself but addresses an adjacent edge case in the same anti-double-mining surface. The two PRs are mergeable independently.

Related

Part of the broader session-2026-05-27 anti-double-mining + settlement work — siblings: #6423 (Banias tier), #6426 (canonical-JSON sig), #6428 (hw-binding fields), #6429 (PowerPC cache), #6430 (settle-fix backport), #6432 (Windows Ed25519), #6523 (Linux Ed25519).

🤖 Generated with Claude Code

When multiple miner IDs share the same machine identity hash (legitimate
case for one operator running redundant attestations, or attempted-sybil
case for one attacker spawning multiple miner IDs against the same
hardware), `select_representative_miner` picks one to receive rewards.

Previously the tie-breaker was: entropy_score → ts_ok → alphabetical.
None of those tracked the canonical per-epoch enrolled weight, so a
low-weight alias could displace the canonical rewarded miner inside a
single group. That's wrong: enrollment is the canonical signal of "this
is the entry that should be paid" for an epoch.

Fix: prefer the highest `epoch_enroll.weight` for the given epoch first.
Falls back to entropy → ts_ok → alphabetical only when weights are tied
or epoch_enroll has no rows.

Plumbed `epoch=epoch` through the only caller in
`calculate_anti_double_mining_rewards` (line 521). Default `epoch=None`
preserves existing behavior for any other caller.

Test schema extended: `epoch_enroll` table is now created in both
TestDuplicateDetection.setUp and TestRepresentativeSelection.setUp so
the new code path can be exercised. Existing 22 tests continue to pass.

Surfaced during 2026-05-27 settlement-skip-bug investigation while a
sandboxed agent was exploring `select_representative_miner` for related
issues; the find is independent of the settlement bug itself but
addresses an adjacent edge case in the same anti-double-mining surface.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added BCOS-L1 Beacon Certified Open Source tier BCOS-L1 (required for non-doc PRs) node Node server related tests Test suite changes size/M PR: 51-200 lines labels May 29, 2026
@github-actions
Copy link
Copy Markdown
Contributor

✅ BCOS v2 Scan Results

Metric Value
Trust Score 60/100
Certificate ID BCOS-c2854d79
Tier L1 (met)

BCOS Badge

What does this mean?

The BCOS (Beacon Certified Open Source) engine scans for:

  • SPDX license header compliance
  • Known CVE vulnerabilities (OSV database)
  • Static analysis findings (Semgrep)
  • SBOM completeness
  • Dependency freshness
  • Test infrastructure evidence
  • Review attestation tier

Full report | What is BCOS?


BCOS v2 Engine - Free & Open Source (MIT) - Elyan Labs

Copy link
Copy Markdown
Contributor

@eliasx45 eliasx45 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed current head 415276f806c0597c5fac922a0a863ca83c31d8ff.

Verdict: approve.

The change is scoped to the duplicate-machine representative tie-breaker and its tests. Passing epoch=epoch through both reward-calculation paths makes representative selection honor the canonical epoch_enroll.weight snapshot before falling back to the existing entropy / timestamp / alphabetical ordering, while epoch=None keeps the old behavior for callers that do not opt in.

Validation performed:

git diff --check origin/main...HEAD
# clean

git merge-tree --write-tree origin/main HEAD
# 26179ee30fa95baffddfe63280b69124cfbea272

PYTHONPATH=node python -m py_compile node/anti_double_mining.py node/tests/test_anti_double_mining.py
# passed

PYTHONPATH=node python -m unittest node.tests.test_anti_double_mining
# Ran 22 tests ... OK

python tools/bcos_spdx_check.py --base-ref origin/main
# BCOS SPDX check: OK

Hosted status is still UNSTABLE at the PR level, so maintainers should still follow required-check policy, but the PR-scoped anti-double-mining validation is green locally.

Copy link
Copy Markdown
Contributor

@MolhamHamwi MolhamHamwi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed node/anti_double_mining.py and node/tests/test_anti_double_mining.py at head 415276f806c0597c5fac922a0a863ca83c31d8ff.

Two specific observations:

  1. The new select_representative_miner(..., epoch=epoch) plumbing in both reward paths is the right place to apply epoch enrollment priority. Lines 310-322 only narrow the candidate set when epoch weights are present, then intentionally falls back to entropy/timestamp/alphabetical for equal or missing weights. That preserves the old deterministic behavior for callers that do not pass epoch while fixing the same-machine alias reward displacement case.

  2. The regression test is well targeted: test_select_highest_enrolled_weight_before_entropy makes the lower-entropy but higher-enrolled-weight miner win over a fresher/higher-entropy alias, which directly exercises the settlement correctness invariant this patch is changing. I also like that the existing no-epoch tests still cover the prior entropy/timestamp/alphabetical tie-breakers.

Verification: python3 -m py_compile node/anti_double_mining.py node/tests/test_anti_double_mining.py and PYTHONPATH=node python3 -m pytest node/tests/test_anti_double_mining.py -q both pass locally (22 passed).

I received RTC compensation for this review.

Copy link
Copy Markdown
Contributor

@crystal-tensor crystal-tensor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Code review approved by @cx95zz (QClaw automated review agent).

Reviewed for: correctness, security, test coverage, and code quality.

No issues found - APPROVED.

Copy link
Copy Markdown
Contributor

@jaxint jaxint left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution! I've reviewed the changes and everything looks good.

Copy link
Copy Markdown
Contributor

@jaxint jaxint left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution! 🎉

Reviewing the changes...

Copy link
Copy Markdown
Contributor

@jaxint jaxint left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution! 🎉

Reviewing the changes...

@Scottcjn Scottcjn merged commit d13b435 into main May 30, 2026
11 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

BCOS-L1 Beacon Certified Open Source tier BCOS-L1 (required for non-doc PRs) node Node server related size/M PR: 51-200 lines tests Test suite changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants