feat(platform-wallet-ffi): persister error codes, contact-account fix, and cleanup - #4587
Draft
Claudius-Maginificent wants to merge 4 commits into
Conversation
`From<PersistenceError>` flattened every persistence failure into the generic `ErrorWalletOperation` (6), so hosts lost the transient/fatal classification the wallet layer now preserves. Adds `ErrorPersisterFatal = 49` and `ErrorPersisterTransient = 50`, claimed from the registry's allocation frontier, and de-flattens the conversion: `PersisterLoad` / `PersisterStore` map on `is_transient()`, `PersisterRestore` unwraps to its typed inner error. Ships the full three-layer parity the registry mandates — Rust enum with a discriminant pin test, `ERROR_CODE_REGISTRY.md` rows 47-50 with the frontier moved to 51, the Swift `PlatformWalletResultCode` / `PlatformWalletError` mirrors, and Swift raw-value pins. The variants are declared in ascending discriminant order (49 then 50), the order both enums otherwise keep; the comment records why 50 is not 48. <sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub>
…counts Contact-account registration went through `accounts.insert_funds_bearing_account`, which does not bump the wallet's `account_generation`. The running filter scan therefore never picked up the new account's addresses — a contact's incoming payments stayed invisible until something else happened to invalidate the scan. Registration now goes through `ManagedAccountOperations::add_managed_account`, and `PlatformWalletInfo` forwards `account_generation()` to the core wallet so the invalidation is observable. Tests assert generation `1` after registering both a contact and an external account. `reconcile_dashpay_rescan` no longer bails on `synced_height == 0`. A zero checkpoint already means "scan from genesis", but bailing left candidates unmarked, so once that scan advanced the very same contacts triggered a redundant funding-height rewind. Candidates are now marked as covered and the height is left alone. <sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub>
…ndex `broadcaster.rs` deleted its hardcoded 30 s `SPV_ACCEPTANCE_TIMEOUT` and now passes `None` to `broadcast_and_wait`. The local override was shorter than dash-spv's own budget, so sends were reported `MaybeSent`/uncertain while the SPV layer was still legitimately waiting. `SendTransactionView` non-platform -> platform flows use `senderAccountIndex = 0` instead of "first key-class-0 account with a positive balance": that search returned a key-class Platform-Payment account index, which was then fed to `CoreTransactionBuilder.setFunding(accountType: .bip44, ...)` — a different namespace, so core -> core sends could draw on the wrong funding account. Also in this batch: `run_tests.sh` tolerates a CI runner with no user default keychain under `set -euo pipefail`; `now_secs()` moves to `util.rs` as `pub(crate)`; the shield-input-selection regression test re-seeds off `reserve()` rather than hardcoded balances so it survives fee-schedule changes; and doc comments are corrected (`Wallet::new_watch_only` -> `new_external_signable`, `derive_spent_utxos` defaults, restore-loop skip behaviour). <sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub>
The FFI restore path carried its own copy of the provider-key account reconstruction, which had already drifted from the SQLite backend's. Both now call `platform_wallet::changeset::rebuild_provider_key_account`, so FFI and SQLite restore provider accounts identically (-54/+30). Ordering: `rebuild_provider_key_account` ships with the wallet-storage PR's required bucket (`changeset/changeset.rs` + `changeset/mod.rs`). Until that lands on the base branch this commit does not compile — the sole error is the unresolved import. Land the storage PR first. <sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub>
Contributor
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueThanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TL;DR: Bundles four small, mostly-independent pieces split out of #3968: new FFI error codes for persister failures, a contact-account address-scanning fix, a provider-account rebuild dedup, and assorted cleanup.
User story
As a platform-wallet FFI host application, I want persistence failures to surface as distinct transient/fatal error codes instead of the generic wallet-operation code, so I can decide whether to retry.
As a DashPay user, I want addresses on a newly accepted contact's payment account to be covered by the wallet's ongoing address scan, so incoming payments to that account aren't missed.
Scenario
Base flow
A host app calls into
platform-wallet-ffiand a persistence operation fails; separately, a user accepts a contact request and the wallet derives a payment account for that contact.Actual behavior
Every persistence failure was flattened to the generic
ErrorWalletOperationcode, so the host app couldn't tell a transient failure (worth retrying) from a fatal one. And accepting a contact request added its payment account without invalidating the wallet's filter-scan generation, so the account's addresses were never covered by the running blockchain scan.Expected behavior
Persistence failures now surface as
ErrorPersisterFatal(49) orErrorPersisterTransient(50) so host apps can retry appropriately. Contact-account registration now goes throughManagedAccountOperations::add_managed_account, which bumps the wallet's scan generation so new addresses are covered.Detailed discussion
What was done
Split out of #3968 as part of a coordinated PR-splitting effort — see that PR's description for the full rationale. Four grouped changes, each a separate commit:
rs-platform-wallet-ffi/src/error.rsgainsErrorPersisterFatal=49/ErrorPersisterTransient=50(declared in ascending order — the split review caught and fixed an ordering nit here),From<PersistenceError>mapping,ERROR_CODE_REGISTRY.mdrows, Swift mirrors (PlatformWalletResult.swift,ErrorHandlingTests.swift). Depends on fix(platform-wallet): typed persister errors with bounded transient retry #4586'sPersister*error variants.contacts.rs/platform_wallet_traits.rs/payments.rs— registers new contact accounts viaadd_managed_accountso filter-scan generation gets bumped; also removes a dead-endsynced_height == 0early return inreconcile_dashpay_rescanthat skipped marking contacts covered (and reattaches an orphaned explanatory comment onto the right binding).rs-platform-wallet-ffi/src/persistence.rsnow sharesrebuild_provider_key_accountwith the storage crate instead of duplicating the logic. Depends on feat(platform-wallet-storage): embeddable SQLite persistence backend with seedless rehydration #3968 merging first — see warning above.broadcaster.rs(drop hardcoded 30s SPV timeout, shorter than dash-spv's own budget),SendTransactionView.swift(fix a Platform-Payment account index leaking into the BIP44 Core account namespace on core→core sends),run_tests.sh(tolerate a CI runner with no keychain),util.rs+contact_requests.rs(now_secs()dedup),platform_wallet.rstest re-seed offreserve(),changeset/core_bridge.rsdoc,wallet_lifecycle.rsnew_watch_only→new_external_signabledoc rename, three Swift doc-only files.Testing
cargo clippy -p platform-wallet -p platform-wallet-ffi --all-targetsclean at the pre-dedup (B/D/F) commit.cargo nextest run -p platform-wallettargeted at touched areas: 95 passed, 0 failed. The final (C) commit does not build standalone until #3968 lands — see warning above. Swift edits applied and reviewed; noxcodebuildrun (no macOS toolchain available in this environment).Breaking changes
None to the public FFI surface's meaning — new error codes are additive.
Checklist
Prior work
Split out of #3968 as part of a coordinated 4-PR split: PR 0 (trimmed #3968), #4586 (typed persister errors, this PR is stacked on it), #4585 (asset-lock size gate). See #3968 for the full rationale.
🤖 Co-authored by Claudius the Magnificent AI Agent