Skip to content

feat: wallet transaction confirmation lifecycle#540

Merged
xdustinface merged 7 commits intov0.42-devfrom
feat/wallet-confirmation-lifecycle
Mar 18, 2026
Merged

feat: wallet transaction confirmation lifecycle#540
xdustinface merged 7 commits intov0.42-devfrom
feat/wallet-confirmation-lifecycle

Conversation

@xdustinface
Copy link
Collaborator

@xdustinface xdustinface commented Mar 15, 2026

Add InstantSend transaction context and transaction state management
so wallet transactions can transition through confirmation stages
(mempool → InstantSend → block → chain-locked block).

Key changes:

  • Add TransactionContext::InstantSend variant with FFI support
  • Track known transactions to avoid double-counting
  • Add confirm_transaction() for mempool→block transitions
  • Add IS lock dedup set to prevent redundant processing
  • Update balance after any UTXO state change
  • Add status_changed field to TransactionCheckResult
  • Add TestWalletContext::with_mempool_funding() helper
  • Add more relevant tests

Based on:

Summary by CodeRabbit

  • New Features

    • Added InstantSend support and expanded transaction lifecycle tracking (mempool → instant-send → in-block → chain-locked).
    • Added ability to reprocess/confirm transactions and mark UTXOs as instant-locked.
    • Optionally update wallet balances during transaction checks.
  • Tests

    • Expanded test coverage for InstantSend lifecycles, rescan/UTXO persistence, and balance-update paths.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 15, 2026

📝 Walkthrough

Walkthrough

Adds InstantSend transaction context and propagation across FFI, manager, wallet-checking, account/UTXO state, and test utilities; expands check_core_transaction with an extra update_balance flag and updates call sites and conversions accordingly.

Changes

Cohort / File(s) Summary
FFI enums & examples
key-wallet-ffi/include/key_wallet_ffi.h, key-wallet-ffi/examples/check_transaction.c, key-wallet-ffi/src/types.rs
Added InstantSend = 1 to FFITransactionContext, shifted subsequent discriminants (InBlock -> 2, InChainLockedBlock -> 3); added conversions between native TransactionContext and FFI enum.
FFI transaction checks
key-wallet-ffi/src/transaction.rs, key-wallet-ffi/src/transaction_checking.rs, key-wallet-ffi/src/wallet_manager.rs
Handle FFITransactionContext::InstantSend mapping to native TransactionContext::InstantSend; updated async check invocations to pass new boolean update_balance argument.
Core transaction context & checking
key-wallet/src/transaction_checking/wallet_checker.rs, key-wallet/src/transaction_checking/account_checker.rs, key-wallet/src/transaction_checking/transaction_router.rs, key-wallet/src/transaction_checking/.../tests/*
Added InstantSend variant to public TransactionContext (derive PartialEq, Eq); introduced InstantSend-specific processing paths in WalletTransactionChecker; expanded check_core_transaction signature to accept update_balance: bool and updated many test call sites.
Managed wallet/account changes
key-wallet/src/managed_account/mod.rs, key-wallet-manager/src/wallet_manager/mod.rs, key-wallet-manager/src/wallet_manager/process_block.rs
Added confirm_transaction(...) and mark_utxos_instant_send(...) to per-account logic; propagated new update_balance flag through WalletManager::check_transaction_in_all_wallets and call sites.
Managed wallet info & interface
key-wallet/src/wallet/managed_wallet_info/mod.rs, key-wallet/src/wallet/managed_wallet_info/wallet_info_interface.rs
Added instant_send_locks: HashSet<Txid> to ManagedWalletInfo; added mark_instant_send_utxos(&mut self, txid: &Txid) -> bool to WalletInfoInterface and its implementation.
Test utilities
key-wallet/src/test_utils/wallet.rs
Added helpers for accessing first BIP44 account, transactions, UTXOs, and async helpers check_transaction(...) and with_mempool_funding(...); updated imports for new types.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant FFI as FFI client
participant WM as WalletManager
participant WI as ManagedWalletInfo
participant AC as ManagedCoreAccount
participant Store as WalletState

FFI->>WM: submit transaction + context (InstantSend/mempool/InBlock)
WM->>WI: check_transaction_in_all_wallets(tx, context, update_state, update_balance)
WI->>AC: check_core_transaction(tx, context, wallet, update_state, update_balance)
AC->>AC: if InstantSend -> mark_utxos_instant_send(txid) / confirm_transaction(...)
AC->>Store: update UTXO is_instantlocked / update TransactionRecord
Store-->>AC: persist result
AC-->>WI: report if wallet changed
WI-->>WM: aggregate results
WM-->>FFI: return whether any wallet affected

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 A hop, a nibble, an InstantSend cheer—
Locks tick in UTXOs, the ledger draws near.
FFI bridges the burrow to core with a grin,
Transactions settle, new states tuck in.
Hooray for the change—may confirmations hop in! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately describes the main change: implementing wallet transaction confirmation lifecycle with support for mempool→InstantSend→block→chain-locked transitions.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/wallet-confirmation-lifecycle
📝 Coding Plan
  • Generate coding plan for human review comments

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 and usage tips.

@codecov
Copy link

codecov bot commented Mar 15, 2026

Codecov Report

❌ Patch coverage is 89.11565% with 32 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.24%. Comparing base (d5bec1e) to head (698d75a).
⚠️ Report is 5 commits behind head on v0.42-dev.

Files with missing lines Patch % Lines
...allet/managed_wallet_info/wallet_info_interface.rs 0.00% 11 Missing ⚠️
key-wallet-ffi/src/types.rs 0.00% 9 Missing ⚠️
key-wallet-ffi/src/transaction.rs 0.00% 3 Missing ⚠️
key-wallet-ffi/src/transaction_checking.rs 50.00% 3 Missing ⚠️
key-wallet-ffi/src/wallet_manager.rs 0.00% 3 Missing ⚠️
key-wallet/src/managed_account/mod.rs 97.14% 1 Missing ⚠️
...-wallet/src/transaction_checking/wallet_checker.rs 99.53% 1 Missing ⚠️
key-wallet/src/wallet/managed_wallet_info/mod.rs 66.66% 1 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##           v0.42-dev     #540      +/-   ##
=============================================
+ Coverage      66.13%   66.24%   +0.10%     
=============================================
  Files            311      311              
  Lines          64757    65022     +265     
=============================================
+ Hits           42829    43073     +244     
- Misses         21928    21949      +21     
Flag Coverage Δ
core 75.13% <ø> (ø)
ffi 37.10% <14.28%> (-0.04%) ⬇️
rpc 28.28% <ø> (ø)
spv 80.85% <ø> (+0.04%) ⬆️
wallet 65.91% <94.87%> (+0.41%) ⬆️
Files with missing lines Coverage Δ
key-wallet-manager/src/wallet_manager/mod.rs 48.51% <100.00%> (+0.59%) ⬆️
...wallet-manager/src/wallet_manager/process_block.rs 48.97% <ø> (ø)
...wallet/src/transaction_checking/account_checker.rs 63.96% <100.00%> (ø)
key-wallet/src/managed_account/mod.rs 49.63% <97.14%> (+3.25%) ⬆️
...-wallet/src/transaction_checking/wallet_checker.rs 95.11% <99.53%> (+1.95%) ⬆️
key-wallet/src/wallet/managed_wallet_info/mod.rs 57.14% <66.66%> (+0.53%) ⬆️
key-wallet-ffi/src/transaction.rs 0.00% <0.00%> (ø)
key-wallet-ffi/src/transaction_checking.rs 1.57% <50.00%> (+0.25%) ⬆️
key-wallet-ffi/src/wallet_manager.rs 53.81% <0.00%> (-0.22%) ⬇️
key-wallet-ffi/src/types.rs 58.42% <0.00%> (-1.26%) ⬇️
... and 1 more

... and 4 files with indirect coverage changes

@xdustinface xdustinface force-pushed the feat/wallet-confirmation-lifecycle branch 3 times, most recently from 5528f98 to 11405bc Compare March 16, 2026 07:28
@github-actions github-actions bot added the merge-conflict The PR conflicts with the target branch. label Mar 16, 2026
@github-actions
Copy link

This PR has merge conflicts with the base branch. Please rebase or merge the base branch into your branch to resolve them.

@xdustinface xdustinface force-pushed the feat/wallet-confirmation-lifecycle branch from 11405bc to 41d957f Compare March 17, 2026 02:57
@github-actions github-actions bot removed the merge-conflict The PR conflicts with the target branch. label Mar 17, 2026
@xdustinface xdustinface marked this pull request as ready for review March 17, 2026 02:58
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
key-wallet/src/managed_account/mod.rs (1)

347-357: ⚠️ Potential issue | 🔴 Critical

Add #[serde(default)] to Utxo::is_instantlocked for backward compatibility.

The new is_instantlocked field in key-wallet/src/utxo.rs (line 32) is serialized without a default value. Previously persisted wallets—which lack this field entirely—will fail to deserialize after upgrade because Serde expects the field to be present. Add #[serde(default)] to allow loading older wallet files:

Fix
// key-wallet/src/utxo.rs
#[cfg_attr(feature = "serde", serde(default))]
pub is_instantlocked: bool,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@key-wallet/src/managed_account/mod.rs` around lines 347 - 357, The Utxo
struct's new boolean field is_instantlocked needs a Serde default so older
serialized wallets missing that field can deserialize; edit the Utxo definition
(the is_instantlocked field in key-wallet/src/utxo.rs) and add the serde default
attribute (e.g. cfg_attr for the serde feature) on that field so missing values
default to false, then run the serde-related tests/deserialize older fixtures to
confirm compatibility.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@key-wallet-ffi/include/key_wallet_ffi.h`:
- Around line 179-191: The example C enum usages in the example
(check_transaction.c) are now out-of-date because the discriminants were
changed: MEMPOOL=0, INSTANT_SEND=1, IN_BLOCK=2, IN_CHAIN_LOCKED_BLOCK=3; update
any literal integer values or switches in the example to use the new enum
constants (MEMPOOL, INSTANT_SEND, IN_BLOCK, IN_CHAIN_LOCKED_BLOCK) or adjust the
numeric literals to match these new discriminants so that old InBlock=1
semantics no longer map to InstantSend.

In `@key-wallet-ffi/src/types.rs`:
- Around line 713-721: The FFI discriminants were shifted by inserting
InstantSend; restore the original discriminants and append the new variant:
change FFITransactionContext to keep Mempool = 0, set InBlock = 1,
InChainLockedBlock = 2, and add InstantSend = 3 as the last variant so existing
consumers keep their numeric values; update the enum declaration for
FFITransactionContext accordingly.

In `@key-wallet/src/managed_account/mod.rs`:
- Around line 389-397: The code only updates confirmation metadata when both
block height and block hash are Some, causing a mempool tx to retain stale
confirmation state when TransactionContext::InBlock or ::InChainLockedBlock
provide height but block_hash is None; change the condition so that if
context.block_height() is Some(height) you call tx_record.mark_confirmed(height,
context.block_hash()) (allowing a None hash), set changed = true, and still
update tx_record.timestamp from context.timestamp() if present; update the
branch around tx_record.is_confirmed() accordingly (referencing
tx_record.mark_confirmed, TransactionContext::InBlock/InChainLockedBlock, and
update_utxos).
- Around line 388-399: confirm_transaction() can update the UTXO set for an
account even when that account has no TransactionRecord (get_mut() misses),
leaving spendable outputs without a history entry; change confirm_transaction()
to backfill a per-account TransactionRecord when the tx is absent before calling
update_utxos: detect the missing record (get_mut() returned None and
check_core_transaction() indicated this account should care), create and insert
a TransactionRecord for this account (setting confirmation via
TransactionRecord::mark_confirmed and timestamp from context if present) and
only then call update_utxos so both transaction history and UTXO updates happen
atomically; ensure the insertion and the mark_confirmed/timestamp assignment are
performed together to prevent inconsistent state.

In `@key-wallet/src/transaction_checking/wallet_checker.rs`:
- Around line 167-193: check_core_transaction() mutates UTXO spendability in the
InstantSend branch and the normal record/confirm path but never refreshes cached
balances; after you mark UTXOs as IS-locked (inside the InstantSend branch where
account.mark_utxos_instant_send(&txid) is called) and after the normal
record/confirm paths (the code region around lines 200-259), call the wallet's
balance-refresh helper (e.g. self.update_balances() or the existing
update_balance(...) API) to recompute cached balances so update_state=true
semantics are honored for external callers; add the call right after the UTXO
mutations in check_core_transaction().

In `@key-wallet/src/wallet/managed_wallet_info/wallet_info_interface.rs`:
- Around line 236-244: The mark_instant_send_utxos method updates UTXO lock
state across accounts but does not refresh the wallet's cached balance
(self.balance), which can leave callers seeing stale balances; after the loop,
if any_changed is true call the existing balance refresh/recompute routine
(e.g., a method like recompute_balance(), refresh_balance_cache(), or the
appropriate balance update helper) to recalculate and assign self.balance so the
cache reflects the InstantSend state change; locate mark_instant_send_utxos,
accounts.all_accounts_mut(), and account.mark_utxos_instant_send(txid) to add
this conditional balance recomputation.

---

Outside diff comments:
In `@key-wallet/src/managed_account/mod.rs`:
- Around line 347-357: The Utxo struct's new boolean field is_instantlocked
needs a Serde default so older serialized wallets missing that field can
deserialize; edit the Utxo definition (the is_instantlocked field in
key-wallet/src/utxo.rs) and add the serde default attribute (e.g. cfg_attr for
the serde feature) on that field so missing values default to false, then run
the serde-related tests/deserialize older fixtures to confirm compatibility.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6589e0f6-7109-413c-b248-78ed43cc80c0

📥 Commits

Reviewing files that changed from the base of the PR and between ca232cb and 41d957f.

📒 Files selected for processing (10)
  • key-wallet-ffi/include/key_wallet_ffi.h
  • key-wallet-ffi/src/transaction.rs
  • key-wallet-ffi/src/transaction_checking.rs
  • key-wallet-ffi/src/types.rs
  • key-wallet/src/managed_account/mod.rs
  • key-wallet/src/test_utils/wallet.rs
  • key-wallet/src/transaction_checking/account_checker.rs
  • key-wallet/src/transaction_checking/wallet_checker.rs
  • key-wallet/src/wallet/managed_wallet_info/mod.rs
  • key-wallet/src/wallet/managed_wallet_info/wallet_info_interface.rs

@xdustinface xdustinface marked this pull request as draft March 17, 2026 03:52
@QuantumExplorer
Copy link
Member

Review from Claude

Issue 1: FFI ABI Breaking Change — InBlock and InChainLockedBlock enum values shifted

Severity: Critical

Inserting INSTANT_SEND = 1 between MEMPOOL and IN_BLOCK shifts existing values:

Variant Before After
MEMPOOL 0 0
INSTANT_SEND 1
IN_BLOCK 1 2
IN_CHAIN_LOCKED_BLOCK 2 3

Since FFITransactionContext is #[repr(C)], any existing C/Swift consumer that passes 1 meaning "in-block" will now be interpreted as "InstantSend". The transaction won't be marked confirmed, UTXOs will have incorrect state, and coinbase maturity checks could use height 0.

Suggested fix: Append INSTANT_SEND at the end (= 3) instead of inserting in the middle, or bump a version and document the ABI break.

Issue 2: confirm_transaction silently skips when block_hash is None

Severity: High

The guard if let (Some(height), Some(hash)) = (context.block_height(), context.block_hash()) requires BOTH to be Some. But the FFI layer allows block_hash: None for InBlock context. When this happens:

  • TransactionRecord is NOT marked confirmed (height stays None, is_confirmed() returns false)
  • update_utxos IS called unconditionally afterward, setting utxo.is_confirmed = true

This creates split-brain state: the transaction record says "unconfirmed" but its UTXOs are spendable. Subsequent IS-lock dedup checks (already_confirmed) will incorrectly think the tx isn't confirmed, causing unnecessary reprocessing.

Suggested fix: Allow confirming with height alone:

if let Some(height) = context.block_height() {
    if !tx_record.is_confirmed() {
        tx_record.mark_confirmed(height, context.block_hash().unwrap_or_default());
        changed = true;
    }
}

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 17, 2026
@github-actions github-actions bot added the ready-for-review CodeRabbit has approved this PR label Mar 17, 2026
Add `InstantSend` transaction context and transaction state management
so wallet transactions can transition through confirmation stages
(mempool → InstantSend → block → chain-locked block).

Key changes:
- Add `TransactionContext::InstantSend` variant with FFI support
- Track known transactions to avoid double-counting
- Add `confirm_transaction()` for mempool→block transitions
- Add IS lock dedup set to prevent redundant processing
- Update balance after any UTXO state change
- Add `status_changed` field to `TransactionCheckResult`
- Add `TestWalletContext::with_mempool_funding()` helper
- Add more relevant tests
…issing accounts

Addresses CodeRabbit review comment on PR #540
File: key-wallet/src/managed_account/mod.rs
Addresses CodeRabbit review comment on PR #540
File: key-wallet/src/wallet/managed_wallet_info/wallet_info_interface.rs
…ansaction()`

Addresses CodeRabbit review comment on PR #540
File: key-wallet/src/transaction_checking/wallet_checker.rs
@xdustinface xdustinface force-pushed the feat/wallet-confirmation-lifecycle branch from 41d957f to 698d75a Compare March 17, 2026 14:07
@xdustinface xdustinface marked this pull request as ready for review March 17, 2026 14:09
@github-actions github-actions bot removed the ready-for-review CodeRabbit has approved this PR label Mar 17, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
key-wallet-ffi/include/key_wallet_ffi.h (1)

175-191: ⚠️ Potential issue | 🔴 Critical

Preserve the existing FFI discriminants or version the ABI.

The enum has been reordered, shifting InBlock from 1 to 2 and InChainLockedBlock from 2 to 3 with the insertion of InstantSend = 1. This is a breaking change for existing C/Swift clients that previously passed 1 for "in block" transactions—they will now be interpreted as InstantSend.

Since this file is auto-generated by cbindgen, fix the Rust enum source in key-wallet-ffi/src/types.rs by either: (1) restoring the original discriminant values (InBlock = 1, InChainLockedBlock = 2) and appending InstantSend = 3 at the end, or (2) explicitly documenting and versioning the ABI break if breaking compatibility is intentional.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@key-wallet-ffi/include/key_wallet_ffi.h` around lines 175 - 191, The
generated C enum changed discriminants and breaks existing FFI consumers; fix
the Rust source in key-wallet-ffi/src/types.rs (the enum that emits MEMPOOL,
InstantSend, InBlock, InChainLockedBlock) so the C ABI preserves original
values: set InBlock = 1 and InChainLockedBlock = 2 and move/append InstantSend
to a new discriminant (e.g., InstantSend = 3), or alternatively add explicit
ABI/versioning documentation and a new versioned enum type if the break is
intentional; regenerate the header with cbindgen after updating the Rust enum.
key-wallet-ffi/src/transaction_checking.rs (1)

144-187: ⚠️ Potential issue | 🟠 Major

Reject NULL block hashes for confirmed contexts at the FFI boundary.

The FFI function builds TransactionContext::InBlock { block_hash: None } and InChainLockedBlock { block_hash: None } when the caller omits the hash. This causes a state split in ManagedWalletInfo:

  • For new transactions: record_transaction sets height from the context, making is_confirmed() return true, but leaves block_hash: None in the record.
  • For existing unconfirmed transactions: confirm_transaction requires both (Some(height), Some(hash)) to update the record. With a NULL hash, mark_confirmed() is never called, so the transaction record stays unconfirmed. However, update_utxos() still runs and marks UTXOs as confirmed because context.confirmed() is true for InBlock regardless of the hash value.

The result is inconsistent persisted state: transaction balance remains unconfirmed while UTXO balance becomes confirmed. Either validate and reject missing hashes for confirmed contexts here, or redesign the record structure to be height-only. As per coding guidelines, **/*-ffi/**/*.rs: Handle memory safety carefully at FFI boundaries.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@key-wallet-ffi/src/transaction_checking.rs` around lines 144 - 187, The code
currently allows constructing TransactionContext::InBlock and
::InChainLockedBlock with block_hash == None when the incoming FFI pointer is
NULL; change the FFI boundary in the match over FFITransactionContext to
validate that block_hash is non-null for confirmed contexts and return a failure
(propagate an Err/invalid-argument) instead of creating a None hash. Concretely,
inside the arms handling FFITransactionContext::InBlock and
::InChainLockedBlock, add an explicit check for block_hash.is_null() and return
an error/result early (do not proceed to slice::from_raw_parts or build
TransactionContext with block_hash=None); keep the existing conversion using
slice::from_raw_parts and dashcore::BlockHash::from_byte_array when non-null so
the code still constructs TransactionContext::InBlock/InChainLockedBlock with
Some(hash) and timestamp handling as before.
🧹 Nitpick comments (1)
key-wallet/src/test_utils/wallet.rs (1)

76-83: Expose the new balance-refresh switch in the test helper.

Hardcoding update_balance = true here means tests built on TestWalletContext can only exercise the eager-refresh path, which leaves the new batching path awkward to cover. Thread the flags through or add a second helper so lifecycle tests can hit both behaviors.

♻️ Suggested refactor
-    pub async fn check_transaction(
-        &mut self,
-        tx: &Transaction,
-        context: TransactionContext,
-    ) -> TransactionCheckResult {
-        self.managed_wallet.check_core_transaction(tx, context, &mut self.wallet, true, true).await
-    }
+    pub async fn check_transaction_with_options(
+        &mut self,
+        tx: &Transaction,
+        context: TransactionContext,
+        update_state: bool,
+        update_balance: bool,
+    ) -> TransactionCheckResult {
+        self.managed_wallet
+            .check_core_transaction(
+                tx,
+                context,
+                &mut self.wallet,
+                update_state,
+                update_balance,
+            )
+            .await
+    }
+
+    pub async fn check_transaction(
+        &mut self,
+        tx: &Transaction,
+        context: TransactionContext,
+    ) -> TransactionCheckResult {
+        self.check_transaction_with_options(tx, context, true, true).await
+    }
As per coding guidelines, "Write unit tests for new functionality in Rust code".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@key-wallet/src/test_utils/wallet.rs` around lines 76 - 83, The test helper
check_transaction currently hardcodes the balance-refresh flag by calling
managed_wallet.check_core_transaction(..., true, true); update check_transaction
signature to accept an update_balance: bool (or add an alternative helper like
check_transaction_eager/check_transaction_batched) and forward that boolean into
the managed_wallet.check_core_transaction call instead of the hardcoded last
true; specifically modify the async fn check_transaction(&mut self, tx:
&Transaction, context: TransactionContext, update_balance: bool) ->
TransactionCheckResult (or add a second helper) and pass update_balance as the
final argument to managed_wallet.check_core_transaction(tx, context, &mut
self.wallet, true, update_balance).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@key-wallet/src/transaction_checking/wallet_checker.rs`:
- Around line 218-222: The confirm_transaction path conflates "InBlock with
height but no block_hash" so transactions stay unconfirmed while update_utxos
marks UTXOs confirmed; modify confirm_transaction to treat InBlock { height:
Some(h), block_hash: None } as a valid confirmation (i.e., accept height-only
confirmations) so the transaction record and UTXOs stay in sync: update the
pattern matching/logic in confirm_transaction (and any place that later writes
transaction.status or confirmation fields) to accept height-only InBlock (use
the height when present, and either leave hash empty or substitute a canonical
placeholder) rather than requiring both height AND block_hash, ensuring
update_utxos and context.confirmed() remain consistent.

---

Outside diff comments:
In `@key-wallet-ffi/include/key_wallet_ffi.h`:
- Around line 175-191: The generated C enum changed discriminants and breaks
existing FFI consumers; fix the Rust source in key-wallet-ffi/src/types.rs (the
enum that emits MEMPOOL, InstantSend, InBlock, InChainLockedBlock) so the C ABI
preserves original values: set InBlock = 1 and InChainLockedBlock = 2 and
move/append InstantSend to a new discriminant (e.g., InstantSend = 3), or
alternatively add explicit ABI/versioning documentation and a new versioned enum
type if the break is intentional; regenerate the header with cbindgen after
updating the Rust enum.

In `@key-wallet-ffi/src/transaction_checking.rs`:
- Around line 144-187: The code currently allows constructing
TransactionContext::InBlock and ::InChainLockedBlock with block_hash == None
when the incoming FFI pointer is NULL; change the FFI boundary in the match over
FFITransactionContext to validate that block_hash is non-null for confirmed
contexts and return a failure (propagate an Err/invalid-argument) instead of
creating a None hash. Concretely, inside the arms handling
FFITransactionContext::InBlock and ::InChainLockedBlock, add an explicit check
for block_hash.is_null() and return an error/result early (do not proceed to
slice::from_raw_parts or build TransactionContext with block_hash=None); keep
the existing conversion using slice::from_raw_parts and
dashcore::BlockHash::from_byte_array when non-null so the code still constructs
TransactionContext::InBlock/InChainLockedBlock with Some(hash) and timestamp
handling as before.

---

Nitpick comments:
In `@key-wallet/src/test_utils/wallet.rs`:
- Around line 76-83: The test helper check_transaction currently hardcodes the
balance-refresh flag by calling managed_wallet.check_core_transaction(..., true,
true); update check_transaction signature to accept an update_balance: bool (or
add an alternative helper like
check_transaction_eager/check_transaction_batched) and forward that boolean into
the managed_wallet.check_core_transaction call instead of the hardcoded last
true; specifically modify the async fn check_transaction(&mut self, tx:
&Transaction, context: TransactionContext, update_balance: bool) ->
TransactionCheckResult (or add a second helper) and pass update_balance as the
final argument to managed_wallet.check_core_transaction(tx, context, &mut
self.wallet, true, update_balance).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f7c5240c-6379-43bd-89a8-0854d4b146a3

📥 Commits

Reviewing files that changed from the base of the PR and between 41d957f and 698d75a.

📒 Files selected for processing (19)
  • key-wallet-ffi/examples/check_transaction.c
  • key-wallet-ffi/include/key_wallet_ffi.h
  • key-wallet-ffi/src/transaction.rs
  • key-wallet-ffi/src/transaction_checking.rs
  • key-wallet-ffi/src/types.rs
  • key-wallet-ffi/src/wallet_manager.rs
  • key-wallet-manager/src/wallet_manager/mod.rs
  • key-wallet-manager/src/wallet_manager/process_block.rs
  • key-wallet/src/managed_account/mod.rs
  • key-wallet/src/test_utils/wallet.rs
  • key-wallet/src/transaction_checking/account_checker.rs
  • key-wallet/src/transaction_checking/transaction_router/tests/asset_unlock.rs
  • key-wallet/src/transaction_checking/transaction_router/tests/coinbase.rs
  • key-wallet/src/transaction_checking/transaction_router/tests/identity_transactions.rs
  • key-wallet/src/transaction_checking/transaction_router/tests/provider.rs
  • key-wallet/src/transaction_checking/transaction_router/tests/routing.rs
  • key-wallet/src/transaction_checking/wallet_checker.rs
  • key-wallet/src/wallet/managed_wallet_info/mod.rs
  • key-wallet/src/wallet/managed_wallet_info/wallet_info_interface.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • key-wallet/src/transaction_checking/account_checker.rs
  • key-wallet/src/wallet/managed_wallet_info/mod.rs

@github-actions github-actions bot added the ready-for-review CodeRabbit has approved this PR label Mar 17, 2026
@xdustinface xdustinface requested review from ZocoLini March 17, 2026 14:47
@xdustinface xdustinface merged commit 213a9b4 into v0.42-dev Mar 18, 2026
39 checks passed
@xdustinface xdustinface deleted the feat/wallet-confirmation-lifecycle branch March 18, 2026 16:42
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

Development

Successfully merging this pull request may close these issues.

3 participants