fix(utxo): register dual-write transfer outputs as account-mirror provenance (danaher #2819 receiver residual) - #8388
Conversation
|
| Metric | Value |
|---|---|
| Trust Score | 36/100 |
| Certificate ID | BCOS-2576f7f7 |
| Tier | L2 (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
Two ways finalize_epoch's UTXO_DUAL_WRITE mint diverged from the account model it is supposed to mirror (#2819, favoritegrandson-tech): 1. Precision split: the account credit truncated the share to 6 decimals (amount_i64) while the mint truncated the same Decimal to 8 decimals, so each reward box was up to 99 nRTC larger than the credit and /utxo/integrity reported the models disagreeing after every epoch with fractional shares. The mint is now derived from the truncated credit: amount_nrtc = amount_i64 * (UTXO_UNIT // ACCOUNT_UNIT). A module-level assert pins that ratio as an exact integer. 2. Ghost mint: a miner enrolled without a balances row gets no account credit (no-phantom invariant) but was still minted a UTXO box. The mint is now gated on the account row actually being credited. This is a prerequisite for #8394 (registering reward boxes as mirrors must not create mirrors larger than the balance they mirror). Tests (end-to-end finalize_epoch, dual-write on): the reward box equals the credit x100 for co-prime weights, a ghost gets nothing, and the credited miner still mirrors exactly while a ghost dilutes the weight. Both fail on main (45 nRTC drift; 0.75 RTC ghost mint) and pass here. tests/ + epoch/UTXO/dual-write suites: 0 regressions (4192 -> 4194). UTXO_DUAL_WRITE is off and prod has 0 UTXO rows, so there is no history to reconcile. Reported-by: favoritegrandson-tech Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Scott <scottbphone12@gmail.com>
…nance (danaher #2819, reward path) Same cross-model double-spend class as the /utxo/transfer receiver residual, in the epoch settlement path. Under UTXO_DUAL_WRITE=1, finalize_epoch credits each miner's ACCOUNT balance (primary ledger) AND mints a UTXO reward box for them — but never registered those boxes in account_mirror_boxes. So each reward was spendable via BOTH models (UTXO box + account balance) = double spend; a total-only integrity check stays models_agree=True. Latent today (prod runs UTXO_DUAL_WRITE=0). Fix: - account_mirror_boxes is now part of the canonical UTXO SCHEMA_SQL, so init_tables() creates it for every dual-write writer. This avoids a CREATE TABLE inside finalize_epoch's open settlement transaction, which Python's sqlite3 would implicit-commit (splitting the atomic epoch settle). (Comment in SCHEMA_SQL is kept free of the ';' character — _execute_schema splits on it.) - finalize_epoch registers every reward box of each dual-write batch into account_mirror_boxes (located by the batch's creation_height; apply_transaction enforces one mining_reward per height, so all boxes at that height are exactly the batch's outputs). Pure INSERTs, same connection/transaction as the settle. The mirror-input exclusion then blocks re-spending these boxes via the UTXO path, forcing the reward to move through the account path (single spend). Tests (node/test_utxo_dualwrite_reward_provenance.py): init_tables creates the table; a registered reward box is excluded from UTXO spendable candidates (double-spend closed) while an unregistered one is not (control); finalize_epoch wires the registration inside the dual-write block (source/AST, matching test_epoch_utxo_dual_write_guard's convention). All fail on origin/main, pass here. UTXO + settlement suites: 165 passed, no new failures (2 pre-existing genesis-migration failures are identical on origin/main). Companion to PR #8388 (transfer receiver residual). Keep UTXO_DUAL_WRITE=0 on prod until both merge. danaher-j #2819 bounty remains held pending destination. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KbyXP4eiiRYEa8GsQtQPhR
The reward-mirror registration selected every box at batch_height. The
one-mining_reward-per-height guard only limits MINT transactions: an
ordinary /utxo/transfer box can share that height (both use slot numbers),
and would be tagged as an account mirror, locking the user's own funds
(every spend -> 409 ACCOUNT_MIRROR_BOX_NOT_SPENDABLE). Join on the
mining_reward tx so only this batch's mint outputs are registered.
Materialize the rows (bounded by UTXO_MAX_OUTPUTS) because the INSERTs
reuse the same cursor.
End-to-end test (real finalize_epoch, dual-write on, a user transfer box
at the batch height): the user box is not tagged, each reward box is
registered once, and each mirror equals its account credit exactly
(needs the precision fix underneath). Fails on the previous height-only
query ('user-box' tagged) and on main (nothing registered).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Scott <scottbphone12@gmail.com>
…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
…; annotate bounded fetch - The tests used 'bob' as the recipient. /utxo/transfer format-checks to_address since #8396/#8465 (lower-case RTC + 40 hex), so the transfer returned 400 before reaching the code under test. Use a canonical address. - test_dual_write_exact_balance_goes_to_zero seeded 100 RTC of UTXO against a 10 RTC account. With receiver/change outputs registered as account mirrors, the 90 RTC change exceeds the (now 0) account balance and the MIRROR_EXCEEDS_BALANCE guard correctly fails closed. Seed the consistent dual-write state (UTXO == account) the invariant assumes. - Annotate the per-transfer output fetch for the fetchall guard (bounded by one transfer's outputs). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Scott <scottbphone12@gmail.com>
f623815 to
c727426
Compare
|
Updated (rebased + tests repaired). Stack order: #8481 → #8394 → this PR. Their commits show here until they merge, so merge them in that order.
Regression check against the #8394 base: 0 regressions (4199 → 4202). |
…nance (#8394) * fix(utxo): dual-write reward boxes exactly mirror the account credit Two ways finalize_epoch's UTXO_DUAL_WRITE mint diverged from the account model it is supposed to mirror (#2819, favoritegrandson-tech): 1. Precision split: the account credit truncated the share to 6 decimals (amount_i64) while the mint truncated the same Decimal to 8 decimals, so each reward box was up to 99 nRTC larger than the credit and /utxo/integrity reported the models disagreeing after every epoch with fractional shares. The mint is now derived from the truncated credit: amount_nrtc = amount_i64 * (UTXO_UNIT // ACCOUNT_UNIT). A module-level assert pins that ratio as an exact integer. 2. Ghost mint: a miner enrolled without a balances row gets no account credit (no-phantom invariant) but was still minted a UTXO box. The mint is now gated on the account row actually being credited. This is a prerequisite for #8394 (registering reward boxes as mirrors must not create mirrors larger than the balance they mirror). Tests (end-to-end finalize_epoch, dual-write on): the reward box equals the credit x100 for co-prime weights, a ghost gets nothing, and the credited miner still mirrors exactly while a ghost dilutes the weight. Both fail on main (45 nRTC drift; 0.75 RTC ghost mint) and pass here. tests/ + epoch/UTXO/dual-write suites: 0 regressions (4192 -> 4194). UTXO_DUAL_WRITE is off and prod has 0 UTXO rows, so there is no history to reconcile. Reported-by: favoritegrandson-tech Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Scott <scottbphone12@gmail.com> * fix(utxo): register epoch mining-reward boxes as account-mirror provenance (danaher #2819, reward path) Same cross-model double-spend class as the /utxo/transfer receiver residual, in the epoch settlement path. Under UTXO_DUAL_WRITE=1, finalize_epoch credits each miner's ACCOUNT balance (primary ledger) AND mints a UTXO reward box for them — but never registered those boxes in account_mirror_boxes. So each reward was spendable via BOTH models (UTXO box + account balance) = double spend; a total-only integrity check stays models_agree=True. Latent today (prod runs UTXO_DUAL_WRITE=0). Fix: - account_mirror_boxes is now part of the canonical UTXO SCHEMA_SQL, so init_tables() creates it for every dual-write writer. This avoids a CREATE TABLE inside finalize_epoch's open settlement transaction, which Python's sqlite3 would implicit-commit (splitting the atomic epoch settle). (Comment in SCHEMA_SQL is kept free of the ';' character — _execute_schema splits on it.) - finalize_epoch registers every reward box of each dual-write batch into account_mirror_boxes (located by the batch's creation_height; apply_transaction enforces one mining_reward per height, so all boxes at that height are exactly the batch's outputs). Pure INSERTs, same connection/transaction as the settle. The mirror-input exclusion then blocks re-spending these boxes via the UTXO path, forcing the reward to move through the account path (single spend). Tests (node/test_utxo_dualwrite_reward_provenance.py): init_tables creates the table; a registered reward box is excluded from UTXO spendable candidates (double-spend closed) while an unregistered one is not (control); finalize_epoch wires the registration inside the dual-write block (source/AST, matching test_epoch_utxo_dual_write_guard's convention). All fail on origin/main, pass here. UTXO + settlement suites: 165 passed, no new failures (2 pre-existing genesis-migration failures are identical on origin/main). Companion to PR #8388 (transfer receiver residual). Keep UTXO_DUAL_WRITE=0 on prod until both merge. danaher-j #2819 bounty remains held pending destination. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KbyXP4eiiRYEa8GsQtQPhR * fix(utxo): register only this epoch's mint outputs as reward mirrors The reward-mirror registration selected every box at batch_height. The one-mining_reward-per-height guard only limits MINT transactions: an ordinary /utxo/transfer box can share that height (both use slot numbers), and would be tagged as an account mirror, locking the user's own funds (every spend -> 409 ACCOUNT_MIRROR_BOX_NOT_SPENDABLE). Join on the mining_reward tx so only this batch's mint outputs are registered. Materialize the rows (bounded by UTXO_MAX_OUTPUTS) because the INSERTs reuse the same cursor. End-to-end test (real finalize_epoch, dual-write on, a user transfer box at the batch height): the user box is not tagged, each reward box is registered once, and each mirror equals its account credit exactly (needs the precision fix underneath). Fails on the previous height-only query ('user-box' tagged) and on main (nothing registered). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Scott <scottbphone12@gmail.com> --------- Signed-off-by: Scott <scottbphone12@gmail.com> Co-authored-by: Scott <scottbphone12@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
RTC RewardThis merged PR earned 5 RTC — sent to |
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_boxesrecords which UTXO boxes back account value, with the consensus invariantmirror ≤ 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/transferunder dual-write:balances += amount), andapply_transaction,…and never registered that output as mirror provenance. So the receiver held
amountin 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 staysmodels_agree=Truethroughout.Latent today (prod runs
UTXO_DUAL_WRITE=0); arms on any dual-write enablement.Fix
utxo_db.apply_transactionexposes the authoritative tx_id on the caller'stxdict (tx['tx_id']) so the endpoint can find the boxes it created./utxo/transferregisters every output box of the transfer (receiver idx 0, change idx 1) intoaccount_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).mirror ≤ balanceassertion (compared in nRTC:value_nrtcvsamount_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)mirror ≤ balanceholds after transfer.All 3 fail on
origin/mainand pass with this change. Existing UTXO suites: 154 passed, no new failures (the 17 pre-existing POC/isolation failures are identical onorigin/main).The epoch mining-reward dual-write path (
rustchain_v2_integrated_...:~5698) likewise creates UTXO reward boxes withoutaccount_mirror_boxesprovenance — same bug class, same latency behindUTXO_DUAL_WRITE=0. KeepUTXO_DUAL_WRITE=0on 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