Skip to content

fix(utxo): register dual-write transfer outputs as account-mirror provenance (danaher #2819 receiver residual) - #8388

Open
Scottcjn wants to merge 1 commit into
mainfrom
fix/utxo-dualwrite-receiver-provenance
Open

Scottcjn wants to merge 1 commit into
mainfrom
fix/utxo-dualwrite-receiver-provenance

Conversation

@Scottcjn

Copy link
Copy Markdown
Owner

Closes the receiver-provenance residual of the danaher-j cross-model double-spend (#2819). Found by a GPT-6 Astra audit of the UTXO transfer path and source-verified against the full mirror model.

The model (verified)

Under UTXO_DUAL_WRITE=1, balances (account) is the primary/total ledger (every epoch reward credits it); UTXO boxes are its shadow. account_mirror_boxes records which UTXO boxes back account value, with the consensus invariant mirror ≤ balance. For the two models not to double-count, every UTXO box representing account value must be mirror-tagged — so it can't be spent via the UTXO path independently of the account balance.

The residual

The mirror-input exclusion (spending an existing mirror box via UTXO) was already fixed and made unconditional. But a /utxo/transfer under dual-write:

  • credits the receiver's account balance (balances += amount), and
  • creates a spendable UTXO output box for the receiver via apply_transaction,

…and never registered that output as mirror provenance. So the receiver held amount in both models — spendable via /utxo/transfer (the untagged box) and via /wallet/transfer/signed (the account credit) = the same value spent twice. A total-only integrity check stays models_agree=True throughout.

Latent today (prod runs UTXO_DUAL_WRITE=0); arms on any dual-write enablement.

Fix

  • utxo_db.apply_transaction exposes the authoritative tx_id on the caller's tx dict (tx['tx_id']) so the endpoint can find the boxes it created.
  • The dual-write branch of /utxo/transfer registers every output box of the transfer (receiver idx 0, change idx 1) into account_mirror_boxes → the unconditional mirror-input exclusion then blocks re-spending them via UTXO; the value moves via the account path (which consumes the mirror on settle).
  • Fail-closed per-wallet mirror ≤ balance assertion (compared in nRTC: value_nrtc vs amount_i64 × (UNIT//ACCOUNT_UNIT) — mirror boxes are nano-RTC, account is micro-RTC); a violation rolls back rather than committing money that exists twice.

Tests (node/test_utxo_dualwrite_receiver_provenance.py)

  • receiver output is registered as mirror provenance;
  • receiver box is excluded from UTXO spendable candidates (double-spend closed);
  • mirror ≤ balance holds after transfer.

All 3 fail on origin/main and pass with this change. Existing UTXO suites: 154 passed, no new failures (the 17 pre-existing POC/isolation failures are identical on origin/main).

⚠️ Related, NOT fixed here (flagged for follow-up)

The epoch mining-reward dual-write path (rustchain_v2_integrated_...:~5698) likewise creates UTXO reward boxes without account_mirror_boxes provenance — same bug class, same latency behind UTXO_DUAL_WRITE=0. Keep UTXO_DUAL_WRITE=0 on prod until both are resolved. Recommend danaher-j review; bounty for #2819 remains held pending payout destination.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KbyXP4eiiRYEa8GsQtQPhR

…venance (danaher #2819 receiver residual)

Under UTXO_DUAL_WRITE=1, `balances` (account) is the primary ledger and UTXO
boxes are its shadow; `account_mirror_boxes` records which boxes back account
value, with the consensus invariant `mirror <= balance`. The mirror-input
exclusion already blocks spending a mirror box via the UTXO path (input side,
fixed earlier). The RESIDUAL: a /utxo/transfer credits the receiver's account
balance AND creates a spendable UTXO output box for the receiver, but never
registered that output as mirror provenance — so the same value was spendable
via BOTH models (UTXO output box + account credit) = double spend. A total-only
integrity check stays models_agree=True throughout (danaher-j private report,
#2819 residual; latent today: UTXO_DUAL_WRITE is off on prod).

Fix:
- utxo_db.apply_transaction now exposes the authoritative tx_id on the caller's
  tx dict (tx['tx_id']), so the endpoint can locate the boxes it created.
- The dual-write branch of /utxo/transfer registers every output box of the
  transfer (receiver at index 0, change at index 1) into account_mirror_boxes,
  so the unconditional mirror-input exclusion blocks re-spending them via UTXO;
  the value must move via the account path (which consumes the mirror on settle).
- Added a fail-closed per-wallet `mirror <= balance` assertion (compared in nRTC:
  mirror value_nrtc vs balance amount_i64 * (UNIT//ACCOUNT_UNIT)); a violation
  rolls the transfer back rather than committing money that exists twice.

Tests: node/test_utxo_dualwrite_receiver_provenance.py (3 tests) — receiver
output is mirror-registered, receiver box is excluded from UTXO spendable
candidates (double-spend closed), invariant holds. All 3 fail on origin/main and
pass with this change. Existing UTXO suites: 154 passed, no new failures (17
pre-existing POC/isolation failures are identical on origin/main).

RELATED (not fixed here, flagged for follow-up): the epoch mining-reward
dual-write path (rustchain_v2_integrated...:~5698) likewise creates UTXO reward
boxes without account_mirror_boxes provenance — same class, same latency behind
UTXO_DUAL_WRITE=0. Keep UTXO_DUAL_WRITE=0 on prod until both are resolved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KbyXP4eiiRYEa8GsQtQPhR
@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 size/L PR: 201-500 lines labels Sep 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ BCOS v2 Scan Results

Metric Value
Trust Score 36/100
Certificate ID BCOS-a201b126
Tier L1 (not 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

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/L PR: 201-500 lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants