Skip to content

Fix reserve-proof replay and raw-extrinsic sender off-by-one#129

Open
wmagev wants to merge 2 commits intoentrius:testfrom
wmagev:fix/proof-handling-bugs
Open

Fix reserve-proof replay and raw-extrinsic sender off-by-one#129
wmagev wants to merge 2 commits intoentrius:testfrom
wmagev:fix/proof-handling-bugs

Conversation

@wmagev
Copy link
Copy Markdown

@wmagev wmagev commented Apr 20, 2026

Two proof-handling bugs on the reservation / transfer-verification path.

Reserve proof was signed over allways-reserve:{from_address}:{block_anchor} with no check that block_anchor is close to the validator's current block and no binding to miner_hotkey, chain direction, or amounts. An attacker who observes one valid proof could replay it against any miner, in any direction, for any amount; each expiry strikes the victim's address with a doubling cooldown and locks them out after a few replays.

  • Add RESERVE_PROOF_MAX_AGE_BLOCKS freshness check against validator.block.
  • Expand the signed message to bind miner_hotkey, from_chain, to_chain, tao_amount, from_amount, to_amount, block_anchor. Tag v2 so pre-fix proofs stop verifying.
  • Move the format into a shared build_reserve_proof_message helper so the CLI signer and validator verifier share one source of truth.

parse_raw_extrinsic in SubtensorProvider read body[1:33] to extract the sender, which captured the MultiAddress variant byte plus only 31 of the 32 AccountId bytes - every raw-parsed block produced a garbage SS58. Raw-parse is the fallback path for pruned/archival nodes; when it runs, expected-sender checks false-reject legitimate TAO swaps.

  • Read body[2:34] after asserting the MultiAddress variant is Id (0x00); reject Index/Raw/Address{20,32} which the rest of the parser does not support.

CLI and validator must be upgraded together - old CLI against new validator will receive "Invalid source address proof" on every reservation.

Fixes #128

Two proof-handling bugs on the reservation / transfer-verification path.

Reserve proof was signed over `allways-reserve:{from_address}:{block_anchor}`
with no check that `block_anchor` is close to the validator's current block
and no binding to miner_hotkey, chain direction, or amounts. An attacker who
observes one valid proof could replay it against any miner, in any direction,
for any amount; each expiry strikes the victim's address with a doubling
cooldown and locks them out after a few replays.

- Add RESERVE_PROOF_MAX_AGE_BLOCKS freshness check against validator.block.
- Expand the signed message to bind miner_hotkey, from_chain, to_chain,
  tao_amount, from_amount, to_amount, block_anchor. Tag `v2` so pre-fix
  proofs stop verifying.
- Move the format into a shared `build_reserve_proof_message` helper so the
  CLI signer and validator verifier share one source of truth.

parse_raw_extrinsic in SubtensorProvider read `body[1:33]` to extract the
sender, which captured the MultiAddress variant byte plus only 31 of the
32 AccountId bytes - every raw-parsed block produced a garbage SS58.
Raw-parse is the fallback path for pruned/archival nodes; when it runs,
expected-sender checks false-reject legitimate TAO swaps.

- Read `body[2:34]` after asserting the MultiAddress variant is Id (0x00);
  reject Index/Raw/Address{20,32} which the rest of the parser does not
  support.

CLI and validator must be upgraded together - old CLI against new validator
will receive "Invalid source address proof" on every reservation.
@wmagev wmagev force-pushed the fix/proof-handling-bugs branch from dc8c352 to 8cc292f Compare April 20, 2026 20:08
@anderdc anderdc added the bug Something isn't working label Apr 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Two proof-handling bugs on the reservation path

2 participants