Skip to content

fix(key-wallet): don't build asset locks on unconfirmed funds - #836

Merged
xdustinface merged 3 commits into
dashpay:devfrom
xdustinface:fix/asset-lock-finality
Jul 6, 2026
Merged

fix(key-wallet): don't build asset locks on unconfirmed funds#836
xdustinface merged 3 commits into
dashpay:devfrom
xdustinface:fix/asset-lock-finality

Conversation

@xdustinface

@xdustinface xdustinface commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

Identity registration and top-up could hang for 300 seconds (AssetLockFinalityTimeout) because the wallet funded asset locks with coins that were not final yet. A funding transaction built on such inputs can never receive an InstantSend lock, so Platform never accepts it. This fixes the two key-wallet defects from #832:

  • Asset-lock coin selection now only spends confirmed or InstantSend-locked coins (new opt-in TransactionBuilder::require_final_inputs).
  • A self-send change output only counts as confirmed balance when every parent is final, matching the Bitcoin Core IsTrusted behavior that Utxo::is_trusted documents. Before, change of an unconfirmed transaction showed up as confirmed and spendable.
  • Ordinary payments are unchanged and can still spend unconfirmed coins, now pinned by a regression test.

The remaining defect from the issue (U3, dash-spv credits its own broadcast locally without any network acceptance signal) is already tracked in #664. The BIP61 reject handling suggested in the issue is not an option since Dash Core removed the reject message.

Closes #832

Summary by CodeRabbit

  • Bug Fixes
    • Tightened self-send trusted detection so change outputs are trusted only when all funding inputs are final.
    • Wallet balance calculations no longer count unconfirmed change from self-sends as confirmed/trusted.
    • Asset-lock transaction building now requires final funding inputs (InstantSend/DIP-0010-eligible).
  • Tests
    • Added a test covering unconfirmed parent handling for self-send change trust and balances.
    • Added/updated asset-lock builder tests to reject non-final-only scenarios and prefer confirmed UTXOs.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a9d8c568-2d6d-4e28-9891-28fc14991e8b

📥 Commits

Reviewing files that changed from the base of the PR and between 17d64d5 and 2386559.

📒 Files selected for processing (5)
  • key-wallet/src/managed_account/managed_core_funds_account.rs
  • key-wallet/src/transaction_checking/wallet_checker.rs
  • key-wallet/src/wallet/managed_wallet_info/asset_lock_builder.rs
  • key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs
  • key-wallet/src/wallet/managed_wallet_info/transaction_building.rs
🚧 Files skipped from review as they are similar to previous changes (5)
  • key-wallet/src/wallet/managed_wallet_info/transaction_building.rs
  • key-wallet/src/transaction_checking/wallet_checker.rs
  • key-wallet/src/managed_account/managed_core_funds_account.rs
  • key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs
  • key-wallet/src/wallet/managed_wallet_info/asset_lock_builder.rs

📝 Walkthrough

Walkthrough

This PR tightens trusted-change handling and final-input selection in key-wallet. It also applies the new final-input rule to asset-lock construction and adds regression tests for unconfirmed inputs.

Changes

Trust and finality enforcement

Layer / File(s) Summary
Self-send change trust detection based on input finality
key-wallet/src/managed_account/managed_core_funds_account.rs, key-wallet/src/transaction_checking/wallet_checker.rs
Replaces the sent-based heuristic with all_inputs_final_and_ours and adds a test showing an unconfirmed-parent self-send stays untrusted and unconfirmed.
TransactionBuilder final-input flag
key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs, key-wallet/src/wallet/managed_wallet_info/transaction_building.rs
Adds a require_final_inputs flag and builder method, filters candidate inputs when enabled, and adds a test showing ordinary spends still accept unconfirmed inputs by default.
Asset-lock builder enforces final inputs
key-wallet/src/wallet/managed_wallet_info/asset_lock_builder.rs
Calls require_final_inputs() in both asset-lock build paths, adds a funded-UTXO helper, and adds tests for unconfirmed-only rejection and confirmed-input selection.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ManagedWalletInfo
  participant TransactionBuilder
  participant UtxoSet
  participant CoinSelection
  ManagedWalletInfo->>TransactionBuilder: require_final_inputs()
  ManagedWalletInfo->>TransactionBuilder: build_signed()
  TransactionBuilder->>UtxoSet: filter to confirmed or instantlocked UTXOs
  TransactionBuilder->>CoinSelection: assemble_unsigned(filtered inputs)
  CoinSelection-->>TransactionBuilder: selected funding inputs
  TransactionBuilder-->>ManagedWalletInfo: signed asset-lock transaction
Loading

Possibly related issues

Possibly related PRs

  • dashpay/rust-dashcore#823: Also changes TransactionBuilder coin selection and assembly behavior in the same input-selection path.

Suggested labels: ready-for-review

Suggested reviewers: ZocoLini

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The asset-lock and trust/balance fixes are present, but the linked dash-spv self-broadcast rejection/recrediting work is not shown. Add the dash-spv rejection/phantom-output reconciliation fix, or narrow the linked scope if that work is intentionally separate.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is clear and matches the main change: preventing asset-locks from using unconfirmed funds.
Out of Scope Changes check ✅ Passed The changes stay focused on asset-lock finality, trust accounting, and regression tests, with no clear unrelated additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 73.32%. Comparing base (c88264e) to head (0f44859).

Additional details and impacted files
@@            Coverage Diff             @@
##              dev     #836      +/-   ##
==========================================
- Coverage   73.38%   73.32%   -0.07%     
==========================================
  Files         324      324              
  Lines       72803    72923     +120     
==========================================
+ Hits        53430    53472      +42     
- Misses      19373    19451      +78     
Flag Coverage Δ
core 76.94% <ø> (ø)
ffi 45.48% <ø> (-1.37%) ⬇️
rpc 20.00% <ø> (ø)
spv 90.50% <ø> (+0.07%) ⬆️
wallet 72.97% <100.00%> (+0.35%) ⬆️
Files with missing lines Coverage Δ
.../src/managed_account/managed_core_funds_account.rs 77.58% <100.00%> (+0.27%) ⬆️
...-wallet/src/transaction_checking/wallet_checker.rs 99.25% <100.00%> (+0.03%) ⬆️
...c/wallet/managed_wallet_info/asset_lock_builder.rs 88.13% <100.00%> (+4.88%) ⬆️
.../wallet/managed_wallet_info/transaction_builder.rs 85.65% <100.00%> (+0.47%) ⬆️
...wallet/managed_wallet_info/transaction_building.rs 87.87% <100.00%> (+0.53%) ⬆️

... and 19 files with indirect coverage changes

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@key-wallet/src/managed_account/managed_core_funds_account.rs`:
- Around line 152-165: The trust recalculation in
update_utxos/confirm_transaction is order-dependent and can overwrite an already
trusted change output back to untrusted on reprocessing. Adjust the logic around
update_utxos in managed_core_funds_account::ManagedCoreFundsAccount so that an
existing outpoint retains its prior is_trusted state, or compute trust from a
source that does not depend on whether spent parents have already been removed
from self.utxos. Use the existing all_inputs_final_and_ours check and the trust
assignment path for new/existing UTXOs to ensure same-context self-sends remain
trusted across repeated passes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c5f2ec04-9974-4a94-820c-e91ae6e69c6a

📥 Commits

Reviewing files that changed from the base of the PR and between a8a0968 and 17d64d5.

📒 Files selected for processing (5)
  • key-wallet/src/managed_account/managed_core_funds_account.rs
  • key-wallet/src/transaction_checking/wallet_checker.rs
  • key-wallet/src/wallet/managed_wallet_info/asset_lock_builder.rs
  • key-wallet/src/wallet/managed_wallet_info/transaction_builder.rs
  • key-wallet/src/wallet/managed_wallet_info/transaction_building.rs

Comment thread key-wallet/src/managed_account/managed_core_funds_account.rs
Asset-lock builders selected funding UTXOs filtered only by `is_spendable`, which deliberately admits unconfirmed mempool outputs. A non-final input is not InstantSend-eligible per DIP-0010, so the resulting funding transaction could never receive the InstantSend lock or ChainLock Platform requires, and identity registration or top-up died on a 300s `AssetLockFinalityTimeout`.

Add an opt-in `TransactionBuilder::require_final_inputs` that restricts coin selection to `is_confirmed || is_instantlocked` UTXOs and enable it in `build_asset_lock` and `build_asset_lock_with_signer`. Ordinary spends keep admitting unconfirmed inputs, now pinned by a regression test.

Part of dashpay#832 (U2).
`Utxo::is_trusted` was set from a flat `has_owned_input && change-addr` check, so change of a self-send spending a still-unconfirmed parent was credited to the confirmed balance bucket. `Utxo::is_trusted` documents Bitcoin Core `CWalletTx::IsTrusted` parity, which is recursive: a zero-conf output is trusted only if every parent is itself trusted.

Set `is_trusted` only when every input spends one of our own UTXOs that is confirmed, InstantSend-locked, or itself trusted. The stored parent flag carries the recursion transitively, and the spent parents are still present in the UTXO map at that point because insertion runs before spent-outpoint removal. Unknown or non-final parents deny trust, so funds the network may still drop never surface as confirmed.

Part of dashpay#832 (U1).
@xdustinface
xdustinface force-pushed the fix/asset-lock-finality branch from 17d64d5 to 2386559 Compare July 2, 2026 14:12
@github-actions github-actions Bot added the ready-for-review CodeRabbit has approved this PR label Jul 2, 2026

@ZocoLini ZocoLini left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I approved but after that I thought about something, why don't we tell the CoinSelector the type of transactions is being built based on the data we have at build time and let it discard those invalid inputs for the asset lock transactions??

@github-actions github-actions Bot added ready-for-review CodeRabbit has approved this PR and removed ready-for-review CodeRabbit has approved this PR labels Jul 6, 2026
@xdustinface

xdustinface commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator Author

I approved but after that I thought about something, why don't we tell the CoinSelector the type of transactions is being built based on the data we have at build time and let it discard those invalid inputs for the asset lock transactions??

Whats better about it? Feel free to come up with a PR to refactor it if there is some good reason im missing (i guess centralizing it would be a good point here now after another thought..), im going to merge it like this now as lukasz seems to be quite keen on getting this merged :)

@xdustinface
xdustinface merged commit 417d61d into dashpay:dev Jul 6, 2026
35 checks passed
xdustinface added a commit that referenced this pull request Jul 6, 2026
`update_utxos` rebuilds each output with a fresh `Utxo::new` on every reprocess, so a mempool→block confirmation silently reset `is_instantlocked` (and `is_trusted` and any coin reservation) back to false. An InstantSend lock is permanent for a txid per DIP-0010 and trust only ever settles, so both now latch monotonically when an entry for the outpoint already exists, and the coin-reservation flag is carried through unchanged. `is_confirmed` stays freshly derived so a reorg can still downgrade it.

Addresses CodeRabbit review comment on PR #836
#836 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-review CodeRabbit has approved this PR

Projects

None yet

3 participants