Conversation
|
Should we drop the regenerated Swift bindings from this PR? They expect the new FFI symbols but Package.swift still points at the released v0.2.2 xcframework so root swift build breaks. (The release flow can regenerate bindings with the matching framework) |
Done, I’ve removed the Swift bindings from this PR |
|
I did a quick review and can give this a concept ACK but need to try testing locally and give it a deeper look. |
Adds a public `sign_digest` wrapper on `TapSigner` and an FFI entry
point so Swift/Kotlin consumers can sign arbitrary digests (BIP-137
"Bitcoin Signed Message", proof-of-key challenges, generic
attestations) without going through `sign_psbt`.
The FFI returns `SignedDigest { signature, pubkey, rec_id }` — the
recovery id is computed at the boundary so callers can verify locally
or build a BIP-137 header byte without an extra round-trip.
Closes bitcoindevkit#70
Picks up `signDigest(digest:subPath:cvc:)`, `SignedDigest`, and `SignDigestError` from the FFI surface added in the previous commit. Also splits the bindings into `CKTap.swift` + `CKTapFFI.h` + `CKTapFFI.modulemap` (replacing the single `cktap_ffi.swift`).
uniffi-bindgen now derives Swift binding filenames from the UDL namespace (`CKTap`) instead of the cargo lib name (`cktap_ffi`), so the script was looking for `cktap_ffiFFI.h`/`.modulemap` and silently falling through — producing an xcframework whose modulemap pointed at a header that was never copied in. `swift build` then failed with hundreds of unresolved C symbols. Also place the header and modulemap directly at the top of the headers dir (not in a `<basename>/` subdir): the xcframework Info.plist exposes `HeadersPath = "Headers"`, and clang only auto-discovers `Headers/module.modulemap`. Remove the stray `CKTapFFI.h`/`CKTapFFI.modulemap` that were accidentally committed under `Sources/CKTap/` — they belong in `target/include/` and are now moved there by the script.
Add unit tests for the recovery-id derivation used by `sign_digest`, exercising the realistic branches without requiring a card: - `derive_recovery_id_matches_signer_rec_id` — round-trips a fixed secret/digest pair and asserts the derived id matches what the recoverable signer returned. - `derive_recovery_id_covers_both_compressed_ids` — sweeps seeds until both rec_id 0 and rec_id 1 are observed (the only ids reachable with compressed-pubkey ECDSA). - `derive_recovery_id_errors_when_pubkey_does_not_match` — passes a pubkey from a different key and asserts `SignDigestError::RecoveryId`.
…ework The regenerated CKTap.swift referenced sign_digest FFI symbols absent from the released v0.2.2 xcframework that root Package.swift points at, breaking root swift build. Bindings will be regenerated by the release flow against the matching framework.
b84ba25 to
d583436
Compare
tks @notmandatory, ok I keep watching #74 |
Summary
sign_digestonTapSignerand through the UniFFI surface so Swift/Kotlin consumers can sign arbitrary 32-byte digests (BIP-137 "Bitcoin Signed Message", proof-of-key challenges, generic attestations) without going throughsign_psbt.SignedDigest { signature, pubkey, rec_id }— the recovery id is computed at the FFI boundary so callers can verify locally or build a BIP-137 header byte without an extra round-trip.Closes #70.
Changes
lib/src/tap_signer.rs— publicsign_digest(digest, sub_path, cvc)wrapper around the existing crate-privateTapSignerShared::sign.lib/src/lib.rs— re-exportbitcoinso downstream FFI code can userust_cktap::bitcoin::secp256k1.cktap-ffi/src/tap_signer.rs— UniFFI entry point +SignedDigestrecord + brute-force recovery-id derivation against the returned compressed pubkey.cktap-ffi/src/error.rs— newSignDigestErrorwithCkTap,InvalidDigestLength { len: u32 }, andRecoveryId { msg }variants.Test plan
cargo build --all-features --all-targets(workspace)cargo test—cktap-ffirecovery-id coverage (derive_recovery_id_matches_signer_rec_id,derive_recovery_id_errors_when_pubkey_does_not_match,derive_recovery_id_covers_both_compressed_ids) passescargo test -p rust-cktap --features emulator -- --nocaptureagainst the coinkite emulatorsignDigest(digest:subPath:cvc:)and verify the returned signature against the returned pubkey using therec_id