refactor(platform-wallet): adopt rust-dashcore wallet event-bus API#3556
refactor(platform-wallet): adopt rust-dashcore wallet event-bus API#3556QuantumExplorer merged 1 commit intov3.1-devfrom
Conversation
Upstream rust-dashcore PR #696 deleted `WalletChangeSet` and `WalletPersistence` in favour of a `broadcast::Sender<WalletEvent>` bus where each event carries the post-change balance and records. Re-points `rust-dashcore` deps at the merge commit ea33cbc84179666c25515dfc817ce32210953037 and adapts platform-wallet: * New `CoreChangeSet` (platform-owned projection of `WalletEvent` data) replaces `WalletChangeSet` in `PlatformWalletChangeSet.core` — the persister/merge/apply surfaces stay identical, only the field type changed. * New `spawn_wallet_event_adapter` (replaces `CorePersistenceBridge`) drains the upstream broadcast and ships projected changesets through the platform persister. Decouples persistence from SPV's write lock — a slow Swift persister no longer stalls block processing. * `WalletInfoInterface` impl, `SpvRuntime` (3 generics, was 4), `BalanceUpdateHandler`, FFI conversion all updated to the new shapes. * SwiftData: `PersistentTransaction` cascade-deletes `PersistentUtxo`; `PersistentUtxo.txid` is now a computed property reading through the relationship. * Swift FFI: `AccountType.ffiValue` returns `FFIAccountKind` (upstream renamed the discriminant enum and reused `FFIAccountType` for a richer struct). * FFI manager constructor enters the shared tokio runtime so the event-adapter task spawn lands on it. TODO(events) markers in the diff flag follow-up work: * IS-lock standalone events not yet wired through the FFI. * `IdentityAuthenticationEcdsa`/`Bls` AccountType variants were removed upstream; FFI tags need new mapping once identity-key derivation moves off `AccountType`. Verified: `cargo check --workspace` clean, `cargo fmt --all` clean, iOS simulator framework + SwiftExampleApp build succeed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughWorkspace dependencies are updated to a new rust-dashcore commit. Core wallet persistence architecture shifts from callback-based bridge to async event adapter model. A new Changes
Sequence DiagramsequenceDiagram
participant WM as WalletManager
participant EA as EventAdapter<br/>(spawn_wallet_event_adapter)
participant PERSIST as PlatformWalletPersistence
rect rgba(100, 150, 200, 0.5)
Note over WM, EA: Event Adapter Subscription Loop
activate EA
WM->>WM: Emits WalletEvent
WM->>EA: Broadcasts WalletEvent<br/>(via stream)
EA->>EA: Subscribe to event stream
end
rect rgba(150, 200, 100, 0.5)
Note over EA, PERSIST: Event→CoreChangeSet Conversion
EA->>EA: Extract wallet_id, balance,<br/>transactions, instant locks
EA->>EA: Derive UTXO created/spent sets<br/>from record projections
EA->>EA: Build CoreChangeSet<br/>(records, utxos, heights)
EA->>EA: Check emptiness to avoid<br/>unnecessary persist calls
end
rect rgba(200, 150, 100, 0.5)
Note over EA, PERSIST: Changeset Persistence
EA->>PERSIST: Wrap CoreChangeSet in<br/>PlatformWalletChangeSet
PERSIST->>PERSIST: store() persists state
PERSIST-->>EA: Acknowledgment
EA->>EA: Continue listening for<br/>next event
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Review GateCommit:
|
|
✅ DashSDKFFI.xcframework built for this PR.
SwiftPM (host the zip at a stable URL, then use): .binaryTarget(
name: "DashSDKFFI",
url: "https://your.cdn.example/DashSDKFFI.xcframework.zip",
checksum: "e91e96312f678e32dca1d802545572bc6579212caea2e7aa401495b84af827d8"
)Xcode manual integration:
|
Issue being fixed or feature implemented
rust-dashcorePR #696 — feat: make wallet events atomic deletedkey_wallet::changeset::WalletChangeSetandkey_wallet_manager::WalletPersistencein favour of abroadcast::Sender<WalletEvent>bus where every variant carries the post-changeWalletCoreBalanceplus the records affected (TransactionDetected,TransactionInstantLocked,BlockProcessed { inserted, updated, matured },SyncHeightAdvanced). The platform side leaned heavily on both removed types and stopped compiling after the upstream merge.This PR re-points the workspace's
rust-dashcoredeps at the merge commitea33cbc84179666c25515dfc817ce32210953037and adapts platform-wallet to the new model. Same persistence shape from the client's perspective; the source of truth on the Rust side moves from a callback to an event subscriber.What was done?
Architecture preserved.
PlatformWalletChangeSet,Merge,apply.rs, the persister trait, and the Swift FFI surface (WalletChangeSetFFI) all keep their structure. OnlyPlatformWalletChangeSet.core's field type changes.New
CoreChangeSet— platform-owned projection ofWalletEventdata. Replaces the deletedkey_wallet::changeset::WalletChangeSet:Chain-locked records skip the IS-lock map (chain-lock supersedes IS finality).
Mergeimpl uses monotonic-max for the height watermarks, append-only for the vecs, last-write-wins for the IS-lock map.New
spawn_wallet_event_adapterreplacesCorePersistenceBridge. Subscribes toWalletManager::subscribe_events()from a tokio task, projects each event into aCoreChangeSet(deriving UTXO deltas from each record'sinput_details/output_details), wraps asPlatformWalletChangeSet { core: Some(cs), .. }, forwards toPlatformWalletPersistence::store. Decouples persistence from SPV — the previous bridge ran the persister callback inside SPV's own write lock, so a slow SwiftData transaction stalled block processing. The broadcast channel buffers events; the adapter task drains them off the SPV path.Updated for the new upstream API:
WalletInfoInterfaceimpl onPlatformWalletInfo: addsget_spendable_utxos,last_processed_height,update_last_processed_height,matured_coinbase_records.update_balancereturns().mark_instant_send_utxosreturnsbool.SpvRuntime:DashSpvClient<W, N, S>is now 3 generics; the old EventManager 4th generic became anevent_handlers: Vec<Arc<dyn EventHandler>>field.PlatformEventManagerregisters as one of those dyn handlers.BalanceUpdateHandler: reads balance from the per-event snapshot (TransactionDetected/TransactionInstantLocked/BlockProcessed) instead of the removedBalanceUpdatedvariant.next_*_addresscallsites get the newadd_to_state: boolarg (truefor transaction-build sites;falsefor asset-lock building, which marks-as-used itself inside the builder).WalletChangeSetFFI::from_changeset(&CoreChangeSet)buckets records byaccount_typeand derives per-account UTXO deltas at FFI time (linear find/insert becauseAccountTypeisn'tOrdupstream).runtime().enter()so the event-adapter task spawn lands on it (Swift calls in synchronously, no reactor in scope).PlatformWalletManagergains ashutdown()method that cancels and joins the adapter task.SwiftData schema (
packages/swift-sdk/Sources/SwiftDashSDK/Persistence/Models/):PersistentTransactiongains@Relationship(deleteRule: .cascade, inverse: \PersistentUtxo.transaction) var utxos: [PersistentUtxo].PersistentUtxodrops the redundant storedtxid: String; gains the inversevar transaction: PersistentTransaction?. A computedtxidproperty delegates through the relationship so existing readers (UtxoStorageDetailView) keep working without churn.upsertUtxolooks up the parentPersistentTransaction(already in context — upstream sends transactions before their UTXOs in the same flush) and links it with defensive stub creation if missing.Swift FFI rename. Upstream renamed the FFI account discriminant enum from
FFIAccountTypetoFFIAccountKindand reused theFFIAccountTypename for a richer struct that bundles the discriminant with index / Dashpay-pointer / key-class fields.AccountType.ffiValueandinit(ffiType:)updated toFFIAccountKind.TODO(events) follow-ups left in code
instant_locks_for_non_final_recordsis not yet wired through the FFI surface. The Swift side learns about IS-lock state only when re-emitted records flow throughrecordswithcontext = InstantSend(..). When a standalone IS-lock event needs to reach Swift, add a corresponding FFI field and populate it.AccountType::IdentityAuthenticationEcdsa/IdentityAuthenticationBlswere removed upstream; the FFI tags remain in the ABI for back-compat but mapping back to a key-walletAccountTypeerrors with aTODO(events)marker until identity-key derivation moves off the walletAccountTypeentirely.How Has This Been Tested?
cargo check --workspace— cleancargo fmt --all -- --check— clean./packages/swift-sdk/build_ios.sh --target sim— Rust workspace + xcframework +SwiftDashSDK+SwiftExampleAppall build end-to-endPlatformWalletManager::newwithout the tokio "no reactor" panic that an early iteration hit (fix landed: enter the FFI's shared runtime around the constructor)Breaking Changes
None at the consensus layer. Public API churn in
platform-wallet(thecorefield's type) andplatform-wallet-ffi(thefrom_changesetinput type, the deprecated identity-authAccountTypemapping) — all internal to this repo.Checklist:
🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
Bug Fixes
Refactor