feat: core derive_wallet_id for hardware watch-only wallets#108
Conversation
Adds a device-agnostic `derive_wallet_id(device_type, xpubs)` so iOS and
Android derive the same `wallet_id` for the same physical device by
construction, instead of each platform reimplementing it (the Swift side
currently improvises "trezor:" + SHA256(sorted xpubs)).
Canonical derivation: sort xpubs lexicographically, join with "\n",
SHA256, hex-encode, return "{device_type}:{hash}". Order-independent.
Exported via UniFFI alongside get_default_wallet_id.
Closes subtask 1 of #107.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
So consumers of the checked-in iOS/Python packages can call the new derive_wallet_id() export. (iOS xcframework / Android Kotlin / dylib are rebuilt at release via build.sh, per repo convention.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ben-kaufman
left a comment
There was a problem hiding this comment.
Two things I think we should tighten before merging:
-
derive_wallet_idshould reject an emptyxpubslist instead of returningtrezor:<sha256 empty string>. Otherwise any caller that reaches this with no account xpubs gets the same wallet id for that device type, which can collapse separate setup failures into the same activity scope. Could we make this return an error and update the empty-input test? -
The bindings look stale or partially regenerated. The Python wrapper references
uniffi_bitkitcore_fn_func_derive_wallet_id, but the committed dylib does not export it, sopython3 -c "import bitkitcore"fails withsymbol not found. The Swift wrapper also calls the new symbol while the iOS headers and XCFramework do not include it, and Android has noderiveWalletIdwrapper or JNI declaration. Could we regenerate and commit all platform bindings and artifacts from this Rust head?
Addresses @ben-kaufman review: 1. derive_wallet_id now returns Result<String, ActivityError> and rejects an empty xpubs list (and blank entries / blank device_type). An empty set would otherwise hash to the same id for every device of a type, collapsing distinct wallets into one activity scope. 2. Regenerated all platform bindings + native artifacts from this Rust head via build.sh all (Swift + bitkitcoreFFI.h + XCFramework, Kotlin + jniLibs .so, Python wrapper + libbitkitcore.dylib), so the new export is callable from the committed packages instead of failing with a missing symbol. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks @ben-kaufman, both addressed in 80dadad.
|
ben-kaufman
left a comment
There was a problem hiding this comment.
Looks good, thanks for fixing the empty xpubs case and regenerating the bindings.
One release note before publishing: since the regenerated Swift zip checksum changed while Package.swift still points at v0.3.3, we may need to bump the bindings/package version so SwiftPM does not fetch the existing v0.3.3 asset with the old checksum.
|
also LGTM 👍🏻 🎉 |
Summary
Subtask 1 of #107. Core now owns the
wallet_idderivation for hardware (watch-only) wallets so iOS and Android produce the same id for the same physical device by construction, instead of each platform reimplementing it. The Swift side currently improvisesHwWalletId.derive="trezor:" + SHA256(sorted xpubs)and flagged it as interim pending agreement with Android — if the two platforms derive independently they drift and activity won't reconcile cross-platform.What changed
#[uniffi::export] pub fn derive_wallet_id(device_type: String, xpubs: Vec<String>) -> String.device_typekeepstrezor/ledger/ … distinct), so it generalises beyond Trezor.Canonical derivation
xpubslexicographically (input order doesn't matter),\n,"{device_type}:{hash}"(e.g.trezor:ab12…).Trezor watch-only is new, so core defines the canonical form; platforms call this instead of rolling their own.
Tests
cargo test derive_wallet_id— order-independence, device_type distinctness, a fixed known-vector, and the empty-xpubs case.🤖 Generated with Claude Code