Skip to content

fix: prevent external weight downgrade via /epoch/enroll#2110

Merged
Scottcjn merged 1 commit intoScottcjn:mainfrom
createkr:feat/issue-enroll-weight-downgrade
Apr 6, 2026
Merged

fix: prevent external weight downgrade via /epoch/enroll#2110
Scottcjn merged 1 commit intoScottcjn:mainfrom
createkr:feat/issue-enroll-weight-downgrade

Conversation

@createkr
Copy link
Copy Markdown
Contributor

@createkr createkr commented Apr 6, 2026

Fix: prevent external weight downgrade via /epoch/enroll

Problem

The /epoch/enroll endpoint and sophia_elya_service.enroll_epoch() use
INSERT OR REPLACE INTO epoch_enroll, allowing any caller to overwrite a
legitimate miner's epoch weight with a near-zero value. This causes
proportional reward loss for the victim miner.

Unlike the prior fix (which covered the auto-enroll path and attestation
record path), the explicit enrollment endpoint remained vulnerable to
external downgrade — an attacker with a miner's pubkey can call this
endpoint to destroy the miner's epoch rewards.

Exploit: POST /epoch/enroll with victim's miner_pubkey + empty/default
device → weight recalculated as 1.0 (or 1e-9 if fingerprint fails) →
INSERT OR REPLACE overwrites victim's legitimate enrollment (e.g. 2.5).

Changes

node/rustchain_v2_integrated_v2.2.1_rip200.py

  • /epoch/enroll endpoint: Change INSERT OR REPLACE to
    INSERT OR IGNORE for epoch_enroll. The first enrollment in an epoch
    wins; subsequent calls for the same (epoch, miner_pk) are no-ops.

node/sophia_elya_service.py

  • enroll_epoch(): Same INSERT OR REPLACEINSERT OR IGNORE change.

node/tests/test_attestation_overwrite_reward_loss.py

3 new tests:

  • test_external_enroll_downgrade_old_behaviour — demonstrates the external
    downgrade bug (INSERT OR REPLACE allows weight overwrite)
  • test_external_enroll_downgrade_fixed — verifies INSERT OR IGNORE blocks
    the external downgrade
  • test_first_enroll_wins_fixed — documents the first-enrollment-wins
    trade-off (acceptable: attacker needs victim's pubkey and sacrifices own rewards)

Testing

$ python3 -m pytest node/tests/test_attestation_overwrite_reward_loss.py -v
11 passed in 0.09s

All 11 tests pass (8 existing + 3 new).

Risk

Minimal. Narrowly scoped to the epoch_enroll upsert logic:

  • INSERT OR IGNORE only affects same-epoch re-enrolls; new epochs are unaffected.
  • No schema changes required.
  • No API contract changes (endpoint still returns the same response).
  • Consistent with the auto-enroll path which already uses INSERT OR IGNORE.

Trade-off

With INSERT OR IGNORE, the first enrollment wins. An attacker who enrolls
a victim's pubkey first with low weight blocks the victim's later legitimate
enrollment. This is acceptable because:

  • Requires the victim's pubkey in advance.
  • Attacker sacrifices their own rewards.
  • No worse than attacker mining with that pubkey from the start.
  • Future: add signature-based authorization to allow safe re-enrollment.

@createkr createkr requested a review from Scottcjn as a code owner April 6, 2026 01:39
@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 Apr 6, 2026
@createkr
Copy link
Copy Markdown
Contributor Author

createkr commented Apr 6, 2026

For bounty payout, please use RTC wallet: RTC1d48d848a5aa5ecf2c5f01aa5fb64837daaf2f35.

@Scottcjn
Copy link
Copy Markdown
Owner

Scottcjn commented Apr 6, 2026

Merged. Payment: 50 RTC. Addresses #2109.

INSERT OR REPLACEINSERT OR IGNORE on epoch_enroll closes the weight downgrade vector. Both main node and sophia_elya_service.py patched. Solid test coverage for the external enrollment path.

@Scottcjn Scottcjn merged commit f748378 into Scottcjn:main Apr 6, 2026
8 of 9 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.

2 participants