Skip to content

fix(security): freeze fund-exit for blocked/under-review wallets (block gate only guarded /attest)#8014

Open
Vyacheslav-Tomashevskiy wants to merge 1 commit into
Scottcjn:mainfrom
Vyacheslav-Tomashevskiy:fix/blocked-wallet-fund-exit-freeze
Open

fix(security): freeze fund-exit for blocked/under-review wallets (block gate only guarded /attest)#8014
Vyacheslav-Tomashevskiy wants to merge 1 commit into
Scottcjn:mainfrom
Vyacheslav-Tomashevskiy:fix/blocked-wallet-fund-exit-freeze

Conversation

@Vyacheslav-Tomashevskiy

Copy link
Copy Markdown
Contributor

Security bug: wallet block/review gate is enforced only on /attest/submit, not on any fund-exit path

wallet_review_gate_response() (backed by wallet_review_holds and the legacy blocked_wallets table) is the control an operator uses to freeze a compromised / fraud / sanctioned wallet. It is wired to exactly one call site — the attestation handler (review_gate = wallet_review_gate_response(miner)), which literally comments "Check wallet review / block registry".

Every path that lets value leave the ledger ignores it:

  • POST /withdraw/request — verifies nonce, balance, encumbrance, daily limit and signature, then debits — but never consults the block registry.
  • POST /wallet/transfer/signed — same, on the signed sender.

Impact

A wallet an admin has just marked blocked / needs_review / held / escalated (via /admin/wallet-review-holds, resolve action block, or a legacy blocked_wallets row) can still drain its entire balance to any address before a maintainer releases it. Freezing the ability to earn (attest) while leaving the ability to spend wide open defeats the whole purpose of a review hold — draining funds is exactly the action a flagged wallet is most likely to take.

Fix

  • Consult wallet_review_gate_response() on the acting wallet (miner_pk / from_address) before any state mutation in /withdraw/request and /wallet/transfer/signed. The gate already returns a ready (json, 403|409) response, so blocked → 403 wallet_blocked, under-review → 409 wallet_under_review. Checked before the BEGIN IMMEDIATE write txn so the gate's own read connection can't contend with the reserved write lock.
  • ensure_wallet_review_tables() now also creates the legacy blocked_wallets table that get_wallet_review_entry() reads. Previously the gate raised no such table: blocked_wallets on any DB where the main startup init hadn't created it — which is why it had never been safe to call outside the one attest path.

Test

node/tests/test_blocked_wallet_fund_exit_freeze.py drives both real endpoints with a genuinely blocked wallet holding a balance, and asserts 403 wallet_blocked with the balance untouched. Mutation-checked: neutralizing the gate makes both tests fail (the request proceeds past where it should stop); with the fix both pass. Existing withdraw/transfer/attest test suites remain green.

The wallet block/review gate (wallet_review_gate_response, backed by
wallet_review_holds + legacy blocked_wallets) was wired to only one
endpoint — /attest/submit. Every fund-EXIT path ignored it, so a wallet
an operator had just frozen for fraud/compromise could still drain its
whole balance via /withdraw/request or /wallet/transfer/signed before a
maintainer released it. Blocking earning while leaving spending open
defeats the purpose of a review hold.

- Consult the gate on the acting wallet (miner_pk / from_address) before
  any state mutation in /withdraw/request and /wallet/transfer/signed.
- ensure_wallet_review_tables() now also creates the legacy blocked_wallets
  table it reads, so the gate is safe to call from any endpoint instead of
  raising 'no such table: blocked_wallets' on DBs where startup init didn't
  create it.
- Mutation-checked regression test drives both real endpoints with a
  genuinely blocked wallet: asserts 403 wallet_blocked and untouched
  balance; fails on pre-fix code, passes after.
@github-actions github-actions Bot added BCOS-L1 Beacon Certified Open Source tier BCOS-L1 (required for non-doc PRs) BCOS-L2 Beacon Certified Open Source tier BCOS-L2 (required for non-doc PRs) node Node server related tests Test suite changes size/M PR: 51-200 lines labels Jul 18, 2026
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) BCOS-L2 Beacon Certified Open Source tier BCOS-L2 (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.

1 participant