fix: stop panicking on errors in exported gomobile bindings#2
Merged
TaprootFreak merged 1 commit intoMay 8, 2026
Conversation
fd312fb to
681a9dd
Compare
TaprootFreak
requested changes
May 8, 2026
TaprootFreak
left a comment
There was a problem hiding this comment.
Android AAR must be rebuilt before merge — shipping iOS-only panic fixes while Android still crashes on the same code paths is not acceptable for a release.
Please run run_build_tool_android.sh and include the updated AAR binary in this PR.
Every exported function in `go/api/` panicked on the first error, which gomobile surfaces as an Objective-C/JNI exception and terminates the host engine. realunit-app hit this on a routine ETHSignMessage NACK from the device — the whole Flutter app died before the caller could show a useful message. - Replace `panic(err)` with logged early returns of the function's zero value (empty string, nil bytes, false). - Add `defer recoverPanic(name)` to every exported function as a safety net for unexpected panics from the underlying SDK. - Nil-check the `bitbox` device pointer before dereferencing it in GetChannelHash, ChannelHashVerify, InitDevice, SupportsETH, SupportsLTC, SupportsBluetooth, SupportsERC20 and DeviceInfo. - DeviceInfo no longer dereferences a nil `*info` when the device query fails. - GetDeviceWithInfo falls back to a sensible default semver instead of panicking on a malformed version string. The iOS xcframework was rebuilt with `gomobile bind -target=ios .` so the host can pick up the new behaviour. Android still needs to be rebuilt separately via the existing run_build_tool_android.sh.
681a9dd to
d919b27
Compare
Author
|
@TaprootFreak Android AAR jetzt mit drin ( Build-Setup:
Beide Binaries (iOS xcframework + Android AAR) sind aus identischer Go-Quelle gebaut und enthalten dieselben Panic-zu-Error-Konvertierungen. |
TaprootFreak
added a commit
that referenced
this pull request
May 8, 2026
* refactor: replace Go binding script with Gradle task and new helper script * refactor: adjust Gradle build configuration and update Go binding task * refactor: update Gradle task to handle API AAR relocation * refactor: simplify Gradle build script and adjust Go binding task paths * feat: add iOS BLE support for BitBox02 Nova Implement CoreBluetooth communication layer for BitBox02 Nova (Plus) hardware wallet on iOS: - Add Bluetooth.swift with BLE service/characteristic discovery, read/write operations, and u2fhid packet handling - Add BitboxFlutterPlugin.swift methods for iOS: initBitBox, getChannelHash, channelHashVerify, getETHAddress (all on background threads to avoid blocking UI) - Fix product mapping: bb02p-* now correctly maps to ProductBitBox02PlusMulti/PlusBTCOnly (not the non-Plus variants) - Fix BLE stale buffer issue: clear read buffer + drain semaphore before each new u2fhid init frame to prevent hwwRspBusy panics - Fix race condition: semaphore.signal() moved inside readBufferLock - Add 10s timeout on readBlocking to prevent hangs on BLE disconnect - Change InitDevice() from panic to bool return on error - Add pre-built Api.xcframework for iOS (gomobile bind) - Add channelHash and channelHashVerify to Dart platform interface * fix: run signETHMessage on background thread Prevents blocking the main thread during DFX auth signing when BitBox device communication is required. * fix: run all signing methods on background thread Move signETHRLPTransaction and signETHTypedMessage to DispatchQueue.global().async to prevent blocking the main thread during device communication. * fix: ANR. * fix: stop panicking on errors in exported gomobile bindings (#2) Every exported function in `go/api/` panicked on the first error, which gomobile surfaces as an Objective-C/JNI exception and terminates the host engine. realunit-app hit this on a routine ETHSignMessage NACK from the device — the whole Flutter app died before the caller could show a useful message. - Replace `panic(err)` with logged early returns of the function's zero value (empty string, nil bytes, false). - Add `defer recoverPanic(name)` to every exported function as a safety net for unexpected panics from the underlying SDK. - Nil-check the `bitbox` device pointer before dereferencing it in GetChannelHash, ChannelHashVerify, InitDevice, SupportsETH, SupportsLTC, SupportsBluetooth, SupportsERC20 and DeviceInfo. - DeviceInfo no longer dereferences a nil `*info` when the device query fails. - GetDeviceWithInfo falls back to a sensible default semver instead of panicking on a malformed version string. The iOS xcframework was rebuilt with `gomobile bind -target=ios .` so the host can pick up the new behaviour. Android still needs to be rebuilt separately via the existing run_build_tool_android.sh. --------- Co-authored-by: Konstantin Ullrich <konstantinullrich12@gmail.com> Co-authored-by: TuanLamNguyen <xnguyen.lam@gmail.com> Co-authored-by: joshuakrueger-dfx <joshua.krueger@dfx.swiss>
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.
Summary
panic(err)ingo/api/with a logged early return of the function's zero value (empty string, nil bytes, false)defer recoverPanic(name)to every exported function as a safety net for unexpected panics from the underlying SDKbitboxdevice pointer before dereferencing it (GetChannelHash,ChannelHashVerify,InitDevice,SupportsETH,SupportsLTC,SupportsBluetooth,SupportsERC20,DeviceInfo)DeviceInfono longer dereferences a nil*infoon a failed device queryGetDeviceWithInfofalls back to a sensible default semver instead of panicking on a malformed version stringApi.xcframeworkrebuilt withgomobile bind -target=ios .Why
Every exported function panicked on the first error. gomobile surfaces a Go panic as an Objective-C / JNI exception and the host engine terminates before any Dart code can react to it. realunit-app hit this on a routine
ETHSignMessageNACK from the device — the whole Flutter app died beforeDfxAuthServicecould fall back to the unauthenticated path.getETHAddresson a freshly paired device,BTCSignPSBTon a rejected confirmation,DeviceInfoafter a disconnect — all crashed the engine the same way.The SDK now stays in process: callers receive an empty signature / address / fingerprint and can decide what to do with it. Existing checks in the Swift wrapper (
if let sig = signature) and Dart layer already treat empty results as "not available".Verified behaviour after the patch
On realunit-app (
developHEAD = 951ac75) with this branch wired in viadependency_overrides, pairing a BitBox02 Plus succeeds and the app no longer crashes when the BitBox returns NACK onETHSignMessage. The DFX auth call surfaces a normal Dart exception ("invalid signature string, value=0x") instead of an engine death — caller-side code can now show that error, retry, or fall back.Out of scope
run_build_tool_android.shshould be re-run before merging into a release branch.LastError()getter or split the API to return tuples, but that needs callers on both Android and iOS to be updated.ETHSignMessagefrom BitBox02 Plus onm/44'/60'/0'/0/0chainId 1 was observed during testing and is not addressed here. That is a device-side / firmware compatibility question (likely needs DFX backend to accept a different proof-of-ownership for BitBox-backed wallets, or a SDK upgrade to a newerbitbox02-api-gothat aligns with current BitBox02 Plus firmware).Test plan
go build ./...cleanETHSignMessageNACKDfxAuthService.getAuthToken()(signature comes back empty, backend rejects, app shows the unauthenticated state instead of dying)