Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
e5e34f5
feat(unified-sdk-jni): bind the paged wallet inventory and outpoint c…
HashEngineering Sep 18, 2026
f59bb8c
fix(kotlin-sdk): reconcile the TXO store against the engine
HashEngineering Sep 18, 2026
8569384
fix(kotlin-sdk): send the full account tuple in TXO classification qu…
HashEngineering Oct 1, 2026
52af68f
fix(kotlin-sdk): drop the netAmount-suspects log from the TXO reconcile
HashEngineering Oct 2, 2026
642fa99
feat(unified-sdk-jni): deliver the engine's credit verdicts to Kotlin
HashEngineering Oct 2, 2026
1414087
fix(kotlin-sdk): write TXOs the engine did not credit as spent at cre…
HashEngineering Oct 2, 2026
b26d93f
style(unified-sdk-jni): rustfmt wallet_manager.rs
HashEngineering Oct 2, 2026
fb2f641
feat(kotlin-sdk): count committed persistence rounds and page unspent…
HashEngineering Oct 3, 2026
f097230
fix(kotlin-sdk): reconcile the TXO store the way Swift does
HashEngineering Oct 3, 2026
b646aff
fix(kotlin-sdk): gate and schedule the TXO reconcile the way Swift does
HashEngineering Oct 3, 2026
420996c
fix(kotlin-sdk): gate the TXO reconcile on the engine's wallet watermark
HashEngineering Oct 3, 2026
6e795dd
Merge branch 'v5.1-dev' into feat/kotlin-sdk-txo-reconcile-4638-port
HashEngineering Oct 10, 2026
e6ad3a4
fix(kotlin-sdk): resolve reconcile TXO owners the way the restore does
HashEngineering Oct 10, 2026
4035de5
fix(kotlin-sdk): key reconcile log references with a per-process secret
HashEngineering Oct 10, 2026
94c915a
test(unified-sdk-jni): pin the reconcile transport to a shared golden…
HashEngineering Oct 10, 2026
0c674bc
fix(kotlin-sdk): require every routing field of an engine inventory row
HashEngineering Oct 10, 2026
fe26713
fix(kotlin-sdk): refuse reconcile flips while a persistence round is …
HashEngineering Oct 16, 2026
97ec47a
fix(kotlin-sdk): reset the reconcile steady-state edge when SPV stops
HashEngineering Oct 16, 2026
92fe869
Merge remote-tracking branch 'origin/v5.1-dev' into feat/kotlin-sdk-t…
HashEngineering Oct 17, 2026
da997b5
Merge remote-tracking branch 'origin/v5.1-dev' into feat/kotlin-sdk-t…
HashEngineering Oct 18, 2026
1f8df04
feat(platform-wallet): own the TXO-store reconcile policy in the wallet
HashEngineering Oct 19, 2026
d4a577e
feat(platform-wallet): expose the TXO-store reconcile policy over the…
HashEngineering Oct 19, 2026
0a62646
refactor(kotlin-sdk): ask the Rust wallet for every TXO reconcile dec…
HashEngineering Oct 19, 2026
b998f90
fix(kotlin-sdk): never lose the native reconcile claim to a cancelled…
HashEngineering Oct 20, 2026
8a9a3f7
style(platform-wallet-ffi): import the UTXO page types the shared hel…
HashEngineering Oct 20, 2026
9e7711d
Merge remote-tracking branch 'origin/v5.1-dev' into feat/kotlin-sdk-t…
HashEngineering Oct 20, 2026
3a73b8b
fix(platform-wallet): let a wallet the host skips stay due
HashEngineering Oct 25, 2026
ee82972
fix(platform-wallet): keep a removed wallet's reconcile cadence forgo…
HashEngineering Oct 25, 2026
1c37445
fix(platform-wallet): release a reconcile claim when begin is dropped
HashEngineering Oct 25, 2026
133ffdf
fix(platform-wallet): hold the engine still until a spend verdict is …
HashEngineering Oct 25, 2026
021461d
Merge remote-tracking branch 'origin/v5.1-dev' into feat/kotlin-sdk-t…
HashEngineering Oct 25, 2026
7764daf
fix(platform-wallet-ffi): release held reconcile verdicts without the…
HashEngineering Oct 25, 2026
d7a2674
fix(kotlin-sdk): never skip the reconcile finish or leak a held-verdi…
HashEngineering Oct 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,31 @@ abstract class NativePersistenceBridge {
*/
open fun onWalletChangesetChainLockHeight(walletId: ByteArray, height: Int): Int = 0

/**
* The engine's credit verdicts for the round (dashpay/platform#4638):
* every `Received` / `Change` output of the round's records that the
* engine did NOT credit to the owning account. Fired inside the
* begin/end bracket BEFORE the changeset callback, and only on rounds
* that carry at least one verdict, so an implementation has them in
* hand when the round's [onWalletChangesetUtxoAdded] entries arrive.
* Descriptor `([B[B[B[II)I`.
*
* [outpoints] is [count] 36-byte keys (32-byte wire-order txid plus a
* little-endian vout) packed into one array; [verdicts] holds one
* `UTXO_CREDIT_VERDICT_*` code per outpoint (1 observed spent, 2
* doomed, 3 uncredited) and [spentAtHeights] the observed spending
* block's height for code 1, 0 otherwise. Purely additive: a host that
* ignores the verdicts writes exactly what it wrote before the slot
* existed.
*/
open fun onWalletChangesetUtxoVerdicts(
walletId: ByteArray,
outpoints: ByteArray,
verdicts: ByteArray,
spentAtHeights: IntArray,
count: Int,
): Int = 0

/**
* The wallet's DashPay coreHeight backfill record — the durable half of
* the contact rescan guard (dashpay/platform#4302). Fired inside the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,150 @@ internal object WalletManagerNative {
gapLimit: Int,
)

// ── TXO-store reconcile (dashpay/platform#4638) ─────────────────────
//
// One export = one `platform-wallet` call. The schedule, the start
// gates, the heal verdicts and the stale-page retry limit all live in
// `platform_wallet::manager::txo_reconcile`; Kotlin pages its own Room
// rows, resolves owners and writes.

/** [walletManagerTxoReconcileBegin]: the run may start
* (`TXO_RECONCILE_GATE_RUN`). */
const val TXO_RECONCILE_GATE_RUN = 0

/**
* The wallets whose reconcile is due now, as concatenated 32-byte ids
* (`platform_wallet_manager_txo_reconcile_due_wallets`). Called on every
* SPV progress poll, running or not: the poll is the schedule's clock,
* and the native side detects the steady-state edge and applies the
* cadence. Each returned wallet counts as scheduled now.
*/
external fun walletManagerTxoReconcileDueWallets(managerHandle: Long): ByteArray?

/**
* Claim [walletId] for one reconcile run and check that it may start
* (`platform_wallet_manager_txo_reconcile_begin`). Returns
* [TXO_RECONCILE_GATE_RUN] or a `TXO_RECONCILE_SKIP_*` reason (see
* [org.dashfoundation.dashsdk.wallet.TxoReconcileSkipReason.fromGateCode]);
* on [TXO_RECONCILE_GATE_RUN] `outTipHeight[0]` is the scan tip to pass
* to [walletManagerTxoHealPageJson], and the claim holds until
* [walletManagerTxoReconcileFinish]. [outTipHeight] is an `IntArray(1)`.
*/
external fun walletManagerTxoReconcileBegin(
managerHandle: Long,
walletId: ByteArray,
outTipHeight: IntArray,
): Int

/**
* Release the claim a [TXO_RECONCILE_GATE_RUN] from
* [walletManagerTxoReconcileBegin] granted; [ran] is whether the run went
* ahead, which restarts the wallet's cadence
* (`platform_wallet_manager_txo_reconcile_finish`).
*/
external fun walletManagerTxoReconcileFinish(managerHandle: Long, walletId: ByteArray, ran: Boolean)

/**
* Whether a reconcile run classifies a page again after it came back
* stale for the [staleInARow]-th time in a row, or stops incomplete
* (`platform_wallet_txo_reconcile_retry_stale_page`, which holds the
* limit).
*/
external fun txoReconcileRetryStalePage(staleInARow: Int): Boolean

/**
* One bounded page of the engine's UTXO inventory for one wallet,
* across every funds account, each row with the engine's heal verdict
* at [tipHeight] (the tip [walletManagerTxoReconcileBegin] returned), as
* JSON `{"utxos":[...],"cursor":<string|null>,"hasMore":<bool>}`. A
* serialization shim over one Rust call
* (`platform_wallet_txo_reconcile_heal_page`): account ordering, cursor
* semantics, the page bound, the address rendering and the verdicts all
* live in `platform-wallet`.
*
* Paged, not swept whole: a wallet's UTXO count is chain-controlled
* (anyone who knows a watched address can keep sending dust to it), so
* a full-inventory read would let a remote party decide how much this
* process allocates on every steady-state transition and every cadence
* pass. Pass [cursor] `null` to start, then hand back the returned
* `cursor` verbatim while `hasMore` is true — a cursor this export did
* not produce throws. [limit] caps the rows in one page; non-positive
* asks for the engine's own default, and the engine clamps its own
* maximum, so no bound is mirrored on this side.
*
* Contact watch-only chains (`DashpayExternalAccount`) are absent by
* construction — those coins are the contact's money, not this
* wallet's — so the inventory never offers one to heal.
*
* Each `utxos` row is a
* [org.dashfoundation.dashsdk.persistence.PlatformWalletPersistenceHandler.EngineUtxoRow]:
* the owning account tuple, the txid hex in the same byte order the
* changeset path hands the handler (so hex→bytes reproduces the
* `txos.txid` blob), vout, amount (duffs), the engine's own
* Base58Check address (empty when the script has no address form),
* scriptHex, height, the engine's own `isConfirmed` /
* `isInstantLocked` / `isCoinbase` / `isLocked` flags, and its
* `healVerdict` (`TXO_HEAL_VERDICT_*`).
*/
external fun walletManagerTxoHealPageJson(
managerHandle: Long,
walletId: ByteArray,
cursor: String?,
limit: Int,
tipHeight: Int,
): String?

/**
* Classify a batch of store rows against the engine's live state: the
* reverse half of the reconcile transport, and the reason the paged
* inventory above carries no spent-outpoint list. The caller pages its
* OWN mirror rows and asks about them a batch at a time, so neither
* side ever builds a set over the whole engine inventory.
*
* [queriesJson] is a JSON array of
* [org.dashfoundation.dashsdk.persistence.PlatformWalletPersistenceHandler.OutpointQuery]:
* for each row, the account tuple the STORE files the coin under, its
* txid hex, vout, and the scriptHex the store recorded. The account
* and script are the store's claim — the engine checks both against
* its own pools rather than trusting them, which is why a bare
* outpoint is not enough to ask the question.
*
* Returns one verdict byte per query, positionally — see
* `OUTPOINT_CLASS_*` on
* [org.dashfoundation.dashsdk.persistence.PlatformWalletPersistenceHandler]:
* 0 unknown, 1 unspent, 2 known-uncredited, 3 not-owned. A query whose
* account tag this build cannot map keeps `unknown`; the rest are
* still answered.
*
* Only for the wallet's claimed reconcile run (between
* [walletManagerTxoReconcileBegin] and [walletManagerTxoReconcileFinish]).
* The answers come back with the engine's wallet lock still held for
* reading, so no fork can drop a spender they name before the page is
* applied. The guard is written to [outHeld] (a `LongArray(1)`; 0 when
* nothing is held) only together with the answers. Release it with
* [txoReconcileHeldVerdictsFree] once the page is applied and before
* the next call that takes [managerHandle]: those resolve the handle in
* the native registry, which must never be waited for while the guard
* is held. Finish releases a guard left behind, as a backstop. Call it
* outside the persistence handler's callback exclusion and never from
* inside a persistence callback: the engine calls those callbacks while
* it holds its wallet lock for writing.
*/
external fun walletManagerTxoReconcileClassifyOutpoints(
managerHandle: Long,
walletId: ByteArray,
queriesJson: String,
outHeld: LongArray,
): ByteArray?

/**
* Release the engine guard [walletManagerTxoReconcileClassifyOutpoints]
* handed out. Takes no manager handle and touches no native registry,
* so it completes whatever is queued there. 0 is a no-op; release each
* value once.
*/
external fun txoReconcileHeldVerdictsFree(held: Long)

// ── Core transaction builder (1:1 over `core_wallet_tx_builder_*`) ─
//
// Each step is a thin extern (one export = one FFI call, per
Expand Down
Loading
Loading