Skip to content

fix(platform-wallet): carry a resumed asset lock's change into the store - #5716

Merged
shumkov merged 3 commits into
v5.0-devfrom
fix/asset-lock-change-confirmation
Nov 3, 2026
Merged

shumkov merged 3 commits into
v5.0-devfrom
fix/asset-lock-change-confirmation

Conversation

@romchornyi

@romchornyi romchornyi commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Basic explanation

What this does: When the app stops between sending an asset lock and receiving its InstantSend lock, the wallet resumes that lock on the next launch from a stripped-down copy of the transaction. That copy did not list the wallet's own change, so when the network later confirmed the lock, the change was confirmed in memory but never in the app's database. This change makes the resumed copy list the change, so the confirmation reaches the database.

Value: The change of such an asset lock stays spendable after the next launch. Before, it was counted in the balance but excluded from every send: Max from Core used only part of the Core balance, and a larger amount was refused as "Insufficient balance". The only way out was a reset and restore.

Risks: Low. No consensus or protocol code. The change only affects a record that is re-created at load for an unresolved asset lock, and it lists only coins that account holds at that moment, so it cannot bring back a spent coin. Wallets that already hit the bug are repaired by #5719, not by this change.

Issue being fixed or feature implemented

The FFI stores (SwiftData, and Kotlin through rs-unified-sdk-jni) take a coin's is_confirmed / is_instantlocked / height only from the output_details of an emitted record (record_new_utxos_ffi).

restore_unresolved_asset_lock_tx_records re-inserts a Built/Broadcast asset lock's funding transaction as a synthetic record with empty output_details. When its InstantSend lock or block arrives, key-wallet finds that record, updates its context and the engine's UTXO, and re-emits it as updated. With no details, nothing reaches the store, so the change row keeps the flags it had in the mempool. The next launch restores the coin from that row as unconfirmed, and the block is never scanned again.

What was done?

  • rs-platform-wallet/src/wallet/asset_lock/restored_record.rs (new): held_output_details(account, tx) returns the outputs of tx that the account holds as coins, with the role from key-wallet's own match (check_transaction_for_match): receive address → Received, change address → Change, in record_transaction's order. Outputs not in the account's utxos (spent, or another account's) are left out. It logs a warning when the account holds a coin of the transaction but does not match it.
  • rs-platform-wallet-ffi/src/persistence.rs: the restored record gets these output_details instead of an empty list. The UTXOs are restored earlier in the same load, so they are in place. Swift and Kotlin share this restore and the same projection, so both get the fix.

Not in this PR:

The SQLite persister is not affected: it never restores this synthetic record.

How Has This Been Tested?

New tests in rs-platform-wallet-ffi/src/persistence.rs. They build a real asset-lock transaction (burn output at vout 0, credit output in the payload, change at vout 1), see it in the mempool, drop the in-memory record as a reload does, restore it, and read the result through WalletChangeSetFFI::from_changeset, the projection the stores apply:

  • should_name_the_held_change_on_a_restored_lock_record
  • should_store_the_change_confirmed_when_a_restored_lock_is_mined
  • should_store_the_change_locked_when_a_restored_lock_gets_its_instant_lock (through mark_instant_send_utxos, the path for a lock on a known transaction)
  • should_leave_a_coin_no_longer_held_off_a_restored_lock_record
  • should_store_another_accounts_change_confirmed_when_a_restored_lock_is_mined

The first three fail on v5.0-dev without the change. The last two are boundaries and pass both ways.

  • cargo test -p platform-wallet-ffi --all-features: 444 passed
  • cargo test -p platform-wallet --all-features: 1521 passed
  • cargo clippy -p platform-wallet -p platform-wallet-ffi --all-features --all-targets -- -D warnings: clean
  • cargo check -p rs-unified-sdk-jni: builds

Breaking Changes

None.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have added "!" to the title and described breaking changes in the corresponding section if my code contains any
  • I have made corresponding changes to the documentation if needed
  • If I added or changed GroveDB structure, I described it in the area's structure.rs, regenerated grovedb-structure.json, and checked the structure viewer link posted on this pull request

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

🤖 Generated with Claude Code

PR Hygiene · 8510ed2

  • Bots — thepastaclaw ✓
  • Self-review — posted; again after any push
  • Reviewer requests paused — your 5 review slots are occupied; this PR is excluded from reviewers' queues. Required approvals still count without a slot.
  • Build green
  • Approvals
    • rs-platform-wallet-ffi (packages/rs-platform-wallet-ffi/src/persistence.rs) — HashEngineering or ZocoLini or llbartekll
    • rs-platform-wallet (packages/rs-platform-wallet/src/wallet/asset_lock/mod.rs, packages/rs-platform-wallet/src/wallet/asset_lock/restored_record.rs) — HashEngineering or ZocoLini or llbartekll

When every merge requirement is met, the PR Hygiene check passes. Reviewer limits do not block merging; other required GitHub checks and protections still apply.

An asset lock whose InstantSend lock was not persisted before the app
stopped is restored at load as a synthetic record with no output
details. When the lock or the block arrives, key-wallet confirms the
change in memory and re-emits that record; the FFI stores derive coin
flags only from a record's output details, so the change row kept
is_confirmed=false / is_instantlocked=false for good and the coin never
became spendable after the next launch.

The restored record now carries the coins its account holds from the
transaction, classified with key-wallet's own account match
(held_output_details), so the re-emit writes the change's new flags.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Tip

🎯 Customize your risk assessments

Guide how CodeRabbit assesses merge risk and security architecture risk. Highlight sensitive changes and explain your security boundaries using Merge Risk Instructions and Security Risk Instructions in your review settings. Security architecture risk assessments require the Advanced plan or above.

✅ Automatic approvals, with your rules

Let CodeRabbit approve pull requests once required threads are resolved, pre-merge checks pass, and your risk limits are met. Enable Automatic approvals in review settings, with overrides for teams or file paths.

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

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.

@github-actions github-actions Bot added this to the v5.0.0 milestone Nov 2, 2026
@github-actions

github-actions Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Waiting for bot review — thepastaclaw not yet. Wait for the missing reviews, or a writer can post /skip-bots to proceed without them; blocking findings still need addressing.
Full checklist in the description.

@github-actions github-actions Bot added the waiting-bots Waiting for the review bots to report on this head label Nov 2, 2026
@thepastaclaw

thepastaclaw commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

✅ Final review complete — no blockers (commit 8510ed2) · triage: normal

@thepastaclaw thepastaclaw 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.

Final validation — Phase 1 + Phase 2

At head 462f524, the fix correctly reconstructs held output details in platform-wallet and leaves the FFI adapter to consume them; the upstream confirmation behavior and linked rust-dashcore#1291 support its premise. No blocking defect was found; one combined suggestion addresses diagnostics and coverage for inconsistent restored address pools. This was a static review: current CI reports successful Rust wallet, Swift SDK, frozen-schema, and Kotlin checks, with PR Hygiene pending.

🟡 1 suggestion(s)

Review provenance

Source: reviewer 1: glm-5.3-flash (agent: phase1-reviewer, role: general); reviewer 2: glm-5.3-flash (agent: phase1-reviewer, role: rust-quality); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: low by gpt-6.1-sol (effort low) — The production change is a small, contained reconstruction of held output details during wallet restore, with extensive regression tests and no changes to coin selection, consensus, or storage schemas.
  • Phase 1 reviewers: glm-5.3-flash — general (completed, effort high); agent phase1-reviewer, glm-5.3-flash — rust-quality (completed, effort high); agent phase1-reviewer
  • Phase 1 model: glm-5.3-flash — zai quota: 5h 98% left, weekly 99% left; passed over gemini-3.8-flash-high (antigravity quota lookup failed)
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort medium); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort medium); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort medium); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort medium); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/rs-platform-wallet/src/wallet/asset_lock/restored_record.rs`:
- [SUGGESTION] packages/rs-platform-wallet/src/wallet/asset_lock/restored_record.rs:78-80: Report and test held outputs missing from restored address pools
  The warning above covers a complete transaction mismatch, but this branch silently skips a held output when the transaction matches through another output or a tracked input and this output's address is missing from the restored pools. That state is representable: the persistence loader inserts UTXOs without requiring their addresses to exist in a pool, and key-wallet's matcher can return a match independently of this output. Excluding an unclassifiable output is appropriate, but its confirmation flags will be absent from the record's store projection without the diagnostic provided for the complete-mismatch case. Log the txid and output index here, and add focused tests for both partial mismatch and the existing held-coin/no-match warning; the current restoration tests exercise neither diagnostic case. This is an observability and coverage improvement for inconsistent persisted state, not a blocker for the normal restore path.
Out-of-scope follow-up suggestions (1)

These are valid observations, but they are outside this PR's scope and should be handled in separate issues or author/maintainer-requested PRs rather than blocking this review.

  • Track recovery for wallets whose asset-lock change is already stuck — The existing load path restores synthetic funding records only for unresolved asset locks. A wallet that previously persisted the lock as resolved but left its change unconfirmed therefore does not enter the repaired path. This is a concrete recovery gap for already-affected wallets, but the diff neither introduces nor worsens it.
    • Follow-up: Track a separate targeted recovery or rescan for stale confirmation flags on change from already-resolved asset locks.

@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Bots are done — your move: post /self-reviewed.
Full checklist in the description.

@github-actions github-actions Bot added waiting-self-review Waiting for the author to post /self-reviewed and removed waiting-bots Waiting for the review bots to report on this head labels Nov 2, 2026
held_output_details left out, without a log, a held output whose address
the account's match does not name as receive or change, or whose script
has no address. Each now logs a warning with the txid, vout and account.
The whole-transaction no-match warning names the held vouts. The held
outputs are collected once, and the account is matched only when it
holds one. Tests in platform-wallet capture both warnings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Waiting for bot review — thepastaclaw not yet. Wait for the missing reviews, or a writer can post /skip-bots to proceed without them; blocking findings still need addressing.
Full checklist in the description.

@github-actions github-actions Bot added waiting-bots Waiting for the review bots to report on this head and removed waiting-self-review Waiting for the author to post /self-reviewed labels Nov 2, 2026

@thepastaclaw thepastaclaw 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.

Re-review — Final validation — Phase 1 + Phase 2

Reviewed the complete diff at 30697bf and verified the restoration, upstream confirmation, and persistence-projection paths; no in-scope findings remain. The prior diagnostic finding is fixed: excluded held outputs are now reported, and focused tests cover partial and complete classification mismatches. Validation was static only; current CI still has “Select compatible runner image” and “Determine changed packages” pending, so head-specific build and test success is not yet established.

🔴 0 blocking | 🟡 0 suggestion(s) | 💬 0 nitpick(s)

Review provenance

Source: reviewer 1: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 2: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 7: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 8: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 9: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 10: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: normal by gpt-6.1-sol (effort low) — The diff changes wallet restore and confirmation propagation logic with substantial tests, but does not itself introduce intricate funds movement, coin selection, or storage migrations.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity quota lookup failed), glm-5.3-flash (not used above high effort; tier asks max)
  • Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort high); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort high); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort high); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort high); agent phase2-reviewer, gpt-6.1-sol — general (completed, effort high); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort high); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort high); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort high); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify the current code and confirm that no unresolved issues remain.

No unresolved findings remain from the prior review on this head.

@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Bots are done — your move: post /self-reviewed.
Full checklist in the description.

@github-actions github-actions Bot added waiting-self-review Waiting for the author to post /self-reviewed and removed waiting-bots Waiting for the review bots to report on this head labels Nov 2, 2026
…ange-confirmation

# Conflicts:
#	packages/rs-platform-wallet-ffi/src/persistence.rs
@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Waiting for bot review — thepastaclaw not yet. Wait for the missing reviews, or a writer can post /skip-bots to proceed without them; blocking findings still need addressing.
Full checklist in the description.

@github-actions github-actions Bot added waiting-bots Waiting for the review bots to report on this head and removed waiting-self-review Waiting for the author to post /self-reviewed labels Nov 3, 2026

@thepastaclaw thepastaclaw 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.

Re-review — Final validation — Phase 1 + Phase 2

Independently reviewed the complete diff at 8510ed2 and found no actionable defects. Restored output classification stays in platform-wallet, follows key-wallet's role precedence, and includes only coins the account holds; the broader upstream repair is tracked in dashpay/rust-dashcore#1291. Validation was static only: current CI shows Rust wallet tests and Kotlin build/tests passing, with Swift SDK build/tests still pending.

🔴 0 blocking | 🟡 0 suggestion(s) | 💬 0 nitpick(s)

Review provenance

Source: reviewer 1: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 2: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 7: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 8: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 9: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 10: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: normal by gpt-6.1-sol (effort low) — The diff adds contained but nontrivial wallet restoration and output-classification logic with substantial tests, without intricate changes to funds movement, coin selection, or storage migrations.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity quota lookup failed), glm-5.3-flash (not used above high effort; tier asks max)
  • Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort high); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort high); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort high); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort high); agent phase2-reviewer, gpt-6.1-sol — general (completed, effort high); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort high); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort high); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort high); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify the current code and confirm that no unresolved issues remain.

No unresolved findings remain from the prior review on this head.

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Bots are done — your move: post /self-reviewed.
Full checklist in the description.

@github-actions github-actions Bot added waiting-self-review Waiting for the author to post /self-reviewed and removed waiting-bots Waiting for the review bots to report on this head labels Nov 3, 2026
@romchornyi

Copy link
Copy Markdown
Contributor Author

/self-reviewed

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Missing human approval. Automatic reviewer requests are paused and this PR is excluded from reviewers' queues. Merge, close, or draft another active PR by this author to free a review slot. Required human review can be satisfied without a slot; once all merge requirements are met, PR Hygiene passes.
Full checklist in the description.

@github-actions github-actions Bot added too-many-open-prs Beyond the author's 5 open PRs; waits for one to merge before a human is asked. and removed waiting-self-review Waiting for the author to post /self-reviewed labels Nov 3, 2026
@shumkov
shumkov merged commit 0f6e0d7 into v5.0-dev Nov 3, 2026
22 of 23 checks passed
@shumkov
shumkov deleted the fix/asset-lock-change-confirmation branch November 3, 2026 07:50
@shumkov shumkov mentioned this pull request Nov 3, 2026
13 of 22 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

too-many-open-prs Beyond the author's 5 open PRs; waits for one to merge before a human is asked.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants