Conversation
…tics, worker ledger GPT-6 Astra audit of the RTC withdrawal off-ramp (funds leaving the chain), each finding source-verified. The visible request-time debit path was already sound (BEGIN IMMEDIATE, guarded atomic debit, finite/positive/precision checks); these close the seams around it. #1 nonce type-encoding replay (request_withdrawal): the signature is verified over the STRINGIFIED nonce while the dedup key is stored TEXT, so JSON `true` and the string "True" render identically in the signed message yet store as distinct dedup keys -> one signature replayable once. Reject bool nonces and canonicalize to the signed string form (int 5 and "5" now collapse to one key). #2 payout worker ledger mismatch + latent double-debit: the node debits the canonical `balances` ledger (amount + fee) at REQUEST time, but the worker debited a NON-EXISTENT `accounts` table again at send time. Removed the send-time debit (claim-only, exactly-once via pending->processing rowcount); refund-on-pre-broadcast-failure now credits `balances` (schema-tolerant, mirroring the node) exactly once, gated on the processing->failed transition. #3 review-hold TOCTOU (request_withdrawal): the pre-BEGIN gate left a race window before the debit. Added an in-transaction re-check on the reserved write connection (plain indexed read, no DDL) so a hold applied mid-request is caught without introducing lock contention. #5 fee semantics: fee is charged on top (debit = amount + fee; destination receives amount). Response reported net_amount = amount - fee, contradicting the debit. Now reports net_amount = amount and total_debited = amount + fee. Tests: added test_withdrawal_nonce_replay_and_fee.py and test_payout_worker_ledger.py; updated test_payout_worker_recovery.py (its last case asserted the old phantom-accounts double-debit). Targeted withdrawal + payout suite: 39 passed. The wider node suite has 102 pre-existing isolation-ordering failures identical on origin/main (unrelated to this change). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KbyXP4eiiRYEa8GsQtQPhR
Contributor
|
| Metric | Value |
|---|---|
| Trust Score | 36/100 |
| Certificate ID | BCOS-79ef672c |
| Tier | L1 (not met) |
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
BCOS v2 Engine - Free & Open Source (MIT) - Elyan Labs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
GPT-6 Astra audit of the RTC withdrawal off-ramp (funds leaving the chain), every finding source-verified against
origin/main. The visible request-time debit path was already sound (BEGIN IMMEDIATE, guarded atomic debit, finite/positive/precision checks, bool amount rejected) — these close the seams around it.#1 — Nonce type-encoding replay (
request_withdrawal)The signature is verified over the stringified nonce (
f"...:{nonce}") while the replay-dedup key is storedTEXTinwithdrawal_nonces. JSONtrue(Pythonbool) and the string"True"render identically in the signed message, but SQLite stores the bool asINTEGER 1 → TEXT "1"and the string as"True"→ two distinct dedup keys, one signature replayable once. Bounded (destination is inside the signed message, so no redirection — griefing / double-process, not theft). Fix: rejectboolnonces and canonicalize to the signed string form, soint 5and"5"collapse to a single dedup key.#2 — Payout worker ledger mismatch + latent double-debit (
payout_worker.py)The node debits the canonical
balancesledger (amount + fee) at request time; the worker debited a non-existentaccountstable again at send time — a latent double-debit against a phantom ledger, masked only because production broadcast is stubbed to refuse. Fix: removed the send-time debit (claim-only, exactly-once via thepending→processingrowcount gate); refund-on-pre-broadcast-failure now creditsbalances(schema-tolerant, mirroring the node's_apply_wallet_balance_delta) exactly once, gated on theprocessing→failedtransition; a failure before the claim leaves the rowpendingfor retry rather than fabricating a refund.#3 — Review-hold TOCTOU (
request_withdrawal)The pre-
BEGINreview gate left a race window before the debit. Fix: added an in-transaction re-check on the reserved write connection (plain indexed read onwallet_review_holds/blocked_wallets, no DDL — tables already ensured by the pre-check), closing the window with no separate-connection lock contention.#5 — Fee semantics
Fee is charged on top (debit = amount + fee; destination receives
amount), but the response reportednet_amount = amount - fee, contradicting the debit. Fix: reportnet_amount = amountand addtotal_debited = amount + fee.Tests
test_withdrawal_nonce_replay_and_fee.py(bool rejected; int/str share dedup key; fee response matches debit).test_payout_worker_ledger.py(noaccountsreference; success doesn't double-debit; pre-broadcast failure refunds tobalances; refund exactly-once).test_payout_worker_recovery.py— its last case asserted the old phantom-accountsdouble-debit (100→89); now asserts the worker leavesbalancesuntouched, keeping the exactly-once-claim coverage.Targeted withdrawal + payout suite: 39 passed. The wider node suite has 102 pre-existing isolation/ordering failures identical on
origin/main(each passes in isolation), unrelated to this change.🤖 Generated with Claude Code
https://claude.ai/code/session_01KbyXP4eiiRYEa8GsQtQPhR