diff --git a/CHANGELOG.md b/CHANGELOG.md index ebcb092..561b63f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.3.3 - 2026-06-22 + +- Surface wrong/cancelled/expected Trezor PIN failures as typed `TrezorError` variants (`InvalidPin`, `PinCancelled`, `PinRequired`) instead of generic device errors, so mobile clients can clear the PIN spinner, prompt a deliberate retry, and avoid reconnecting while the device is mid-flow. Backed by `trezor-connect-rs` 0.3.3, which maps protocol `Failure` codes to typed errors; unknown failure codes remain generic `TrezorError::DeviceError`. + ## 0.3.2 - 2026-06-22 - Expose Trezor lock state through `TrezorFeatures.unlocked` so mobile apps can distinguish PIN protection from the current locked/unlocked session state. diff --git a/Cargo.lock b/Cargo.lock index 659331e..e476319 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -475,7 +475,7 @@ dependencies = [ [[package]] name = "bitkitcore" -version = "0.3.2" +version = "0.3.3" dependencies = [ "android_logger", "async-trait", @@ -4689,9 +4689,9 @@ dependencies = [ [[package]] name = "trezor-connect-rs" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2243996b0d2a6f5dc6526a31922b3398fc1b1a56623ed24318257138e38f69b" +checksum = "71fbb6b200a4de26bca79396c7e1c9b582292a515d0438f505b03b758315f6a9" dependencies = [ "aes-gcm", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index ad519cd..65c9d0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bitkitcore" -version = "0.3.2" +version = "0.3.3" edition = "2021" [lib] @@ -47,11 +47,11 @@ btleplug = "0.11" # Trezor connect library - non-iOS platforms get USB + Bluetooth [target.'cfg(not(target_os = "ios"))'.dependencies] -trezor-connect-rs = { version = "0.3.2", features = ["psbt"] } +trezor-connect-rs = { version = "0.3.3", features = ["psbt"] } # iOS: Bluetooth only (libusb has no iOS backend, so no USB support) [target.'cfg(target_os = "ios")'.dependencies] -trezor-connect-rs = { version = "0.3.2", default-features = false, features = ["bluetooth", "psbt"] } +trezor-connect-rs = { version = "0.3.3", default-features = false, features = ["bluetooth", "psbt"] } # JNI for Android (must match btleplug's jni version) [target.'cfg(target_os = "android")'.dependencies] diff --git a/Package.swift b/Package.swift index df964c9..f24c21f 100644 --- a/Package.swift +++ b/Package.swift @@ -3,8 +3,8 @@ import PackageDescription -let tag = "v0.3.2" -let checksum = "40864189de4d992fbdb3308ece78b8002dc14fed65078d6e9b4c871de262818a" +let tag = "v0.3.3" +let checksum = "0935c7069f650d42b9ec2cfd9996b762d7c777dc5f6550797fca72120c31906a" let url = "https://github.com/synonymdev/bitkit-core/releases/download/\(tag)/BitkitCore.xcframework.zip" let package = Package( diff --git a/bindings/android/gradle.properties b/bindings/android/gradle.properties index 0008764..dd31de4 100644 --- a/bindings/android/gradle.properties +++ b/bindings/android/gradle.properties @@ -3,4 +3,4 @@ android.useAndroidX=true android.enableJetifier=true kotlin.code.style=official group=com.synonym -version=0.3.2 +version=0.3.3 diff --git a/bindings/android/lib/src/main/jniLibs/arm64-v8a/libbitkitcore.so b/bindings/android/lib/src/main/jniLibs/arm64-v8a/libbitkitcore.so index d5a06d4..1ce94aa 100755 Binary files a/bindings/android/lib/src/main/jniLibs/arm64-v8a/libbitkitcore.so and b/bindings/android/lib/src/main/jniLibs/arm64-v8a/libbitkitcore.so differ diff --git a/bindings/android/lib/src/main/jniLibs/armeabi-v7a/libbitkitcore.so b/bindings/android/lib/src/main/jniLibs/armeabi-v7a/libbitkitcore.so index 5bcbd4f..550af43 100755 Binary files a/bindings/android/lib/src/main/jniLibs/armeabi-v7a/libbitkitcore.so and b/bindings/android/lib/src/main/jniLibs/armeabi-v7a/libbitkitcore.so differ diff --git a/bindings/android/lib/src/main/jniLibs/x86/libbitkitcore.so b/bindings/android/lib/src/main/jniLibs/x86/libbitkitcore.so index a95d738..79eb570 100755 Binary files a/bindings/android/lib/src/main/jniLibs/x86/libbitkitcore.so and b/bindings/android/lib/src/main/jniLibs/x86/libbitkitcore.so differ diff --git a/bindings/android/lib/src/main/jniLibs/x86_64/libbitkitcore.so b/bindings/android/lib/src/main/jniLibs/x86_64/libbitkitcore.so index ecc8673..bccab15 100755 Binary files a/bindings/android/lib/src/main/jniLibs/x86_64/libbitkitcore.so and b/bindings/android/lib/src/main/jniLibs/x86_64/libbitkitcore.so differ diff --git a/bindings/android/lib/src/main/kotlin/com/synonym/bitkitcore/bitkitcore.common.kt b/bindings/android/lib/src/main/kotlin/com/synonym/bitkitcore/bitkitcore.common.kt index 790c192..3b8ca9a 100644 --- a/bindings/android/lib/src/main/kotlin/com/synonym/bitkitcore/bitkitcore.common.kt +++ b/bindings/android/lib/src/main/kotlin/com/synonym/bitkitcore/bitkitcore.common.kt @@ -1780,7 +1780,7 @@ public data class TrezorCallMessageResult ( /** * Error message (empty on success) */ - val `error`: kotlin.String, + val `error`: kotlin.String, /** * Structured error code (None on success or when the native error is generic) */ @@ -2199,7 +2199,7 @@ public data class TrezorTransportReadResult ( /** * Error message (empty on success) */ - val `error`: kotlin.String, + val `error`: kotlin.String, /** * Structured error code (None on success or when the native error is generic) */ @@ -2241,7 +2241,7 @@ public data class TrezorTransportWriteResult ( /** * Error message (empty on success) */ - val `error`: kotlin.String, + val `error`: kotlin.String, /** * Structured error code (None on success or when the native error is generic) */ @@ -4071,7 +4071,7 @@ public enum class TrezorScriptType { @kotlinx.serialization.Serializable public enum class TrezorTransportErrorCode { - + /** * Device is busy and the caller should back off before retrying. */ @@ -4409,6 +4409,8 @@ public enum class WordCount { + + diff --git a/bindings/ios/BitkitCore.xcframework.zip b/bindings/ios/BitkitCore.xcframework.zip index f4c5b71..46036ec 100644 Binary files a/bindings/ios/BitkitCore.xcframework.zip and b/bindings/ios/BitkitCore.xcframework.zip differ diff --git a/bindings/ios/BitkitCore.xcframework/ios-arm64-simulator/libbitkitcore.a b/bindings/ios/BitkitCore.xcframework/ios-arm64-simulator/libbitkitcore.a index 90663ab..ab72755 100644 Binary files a/bindings/ios/BitkitCore.xcframework/ios-arm64-simulator/libbitkitcore.a and b/bindings/ios/BitkitCore.xcframework/ios-arm64-simulator/libbitkitcore.a differ diff --git a/bindings/ios/BitkitCore.xcframework/ios-arm64/libbitkitcore.a b/bindings/ios/BitkitCore.xcframework/ios-arm64/libbitkitcore.a index c3d68eb..9c66050 100644 Binary files a/bindings/ios/BitkitCore.xcframework/ios-arm64/libbitkitcore.a and b/bindings/ios/BitkitCore.xcframework/ios-arm64/libbitkitcore.a differ diff --git a/bindings/ios/bitkitcore.swift b/bindings/ios/bitkitcore.swift index d3d5611..b7cbe6d 100644 --- a/bindings/ios/bitkitcore.swift +++ b/bindings/ios/bitkitcore.swift @@ -10057,7 +10057,7 @@ public struct TrezorCallMessageResult { */data: Data, /** * Error message (empty on success) - */error: String, + */error: String, /** * Structured error code (None on success or when the native error is generic) */errorCode: TrezorTransportErrorCode?) { @@ -10117,7 +10117,7 @@ public struct FfiConverterTypeTrezorCallMessageResult: FfiConverterRustBuffer { success: FfiConverterBool.read(from: &buf), messageType: FfiConverterUInt16.read(from: &buf), data: FfiConverterData.read(from: &buf), - error: FfiConverterString.read(from: &buf), + error: FfiConverterString.read(from: &buf), errorCode: FfiConverterOptionTypeTrezorTransportErrorCode.read(from: &buf) ) } @@ -11719,7 +11719,7 @@ public struct TrezorTransportReadResult { */data: Data, /** * Error message (empty on success) - */error: String, + */error: String, /** * Structured error code (None on success or when the native error is generic) */errorCode: TrezorTransportErrorCode?) { @@ -11773,7 +11773,7 @@ public struct FfiConverterTypeTrezorTransportReadResult: FfiConverterRustBuffer try TrezorTransportReadResult( success: FfiConverterBool.read(from: &buf), data: FfiConverterData.read(from: &buf), - error: FfiConverterString.read(from: &buf), + error: FfiConverterString.read(from: &buf), errorCode: FfiConverterOptionTypeTrezorTransportErrorCode.read(from: &buf) ) } @@ -11827,7 +11827,7 @@ public struct TrezorTransportWriteResult { */success: Bool, /** * Error message (empty on success) - */error: String, + */error: String, /** * Structured error code (None on success or when the native error is generic) */errorCode: TrezorTransportErrorCode?) { @@ -11875,7 +11875,7 @@ public struct FfiConverterTypeTrezorTransportWriteResult: FfiConverterRustBuffer return try TrezorTransportWriteResult( success: FfiConverterBool.read(from: &buf), - error: FfiConverterString.read(from: &buf), + error: FfiConverterString.read(from: &buf), errorCode: FfiConverterOptionTypeTrezorTransportErrorCode.read(from: &buf) ) } @@ -17581,7 +17581,7 @@ extension TrezorScriptType: Codable {} */ public enum TrezorTransportErrorCode { - + /** * Device is busy and the caller should back off before retrying. */ @@ -17602,20 +17602,20 @@ public struct FfiConverterTypeTrezorTransportErrorCode: FfiConverterRustBuffer { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorTransportErrorCode { let variant: Int32 = try readInt(&buf) switch variant { - + case 1: return .deviceBusy - + default: throw UniffiInternalError.unexpectedEnumCase } } public static func write(_ value: TrezorTransportErrorCode, into buf: inout [UInt8]) { switch value { - - + + case .deviceBusy: writeInt(&buf, Int32(1)) - + } } } diff --git a/bindings/python/bitkitcore/bitkitcore.py b/bindings/python/bitkitcore/bitkitcore.py index 3b4a16c..75d575f 100644 --- a/bindings/python/bitkitcore/bitkitcore.py +++ b/bindings/python/bitkitcore/bitkitcore.py @@ -14538,7 +14538,7 @@ class TrezorTransportErrorCode(enum.Enum): Device is busy and the caller should back off before retrying. """ - + class _UniffiConverterTypeTrezorTransportErrorCode(_UniffiConverterRustBuffer): diff --git a/bindings/python/bitkitcore/libbitkitcore.dylib b/bindings/python/bitkitcore/libbitkitcore.dylib index 640e861..ca272ce 100755 Binary files a/bindings/python/bitkitcore/libbitkitcore.dylib and b/bindings/python/bitkitcore/libbitkitcore.dylib differ diff --git a/bindings/python/setup.py b/bindings/python/setup.py index bcc643a..99362a4 100644 --- a/bindings/python/setup.py +++ b/bindings/python/setup.py @@ -2,7 +2,7 @@ setup( name="bitkitcore", - version="0.3.2", + version="0.3.3", packages=find_packages(), package_data={ "bitkitcore": ["*.so", "*.dylib", "*.dll"], diff --git a/src/modules/trezor/tests.rs b/src/modules/trezor/tests.rs index 05bf81e..6ac4d7e 100644 --- a/src/modules/trezor/tests.rs +++ b/src/modules/trezor/tests.rs @@ -161,6 +161,88 @@ mod tests { assert!(matches!(err, TrezorError::NotConnected)); } + // The following tests start from raw Trezor protocol `Failure` codes + // (FailureType in proto/messages-common.proto) and assert they surface as + // typed bitkit-core errors through the existing conversion path — i.e. a + // wrong-PIN `Failure { code: 7 }` reaches the app as TrezorError::InvalidPin + // rather than a generic device error. + + #[test] + fn test_failure_code_pin_invalid_surfaces_as_invalid_pin() { + use trezor_connect_rs::error::DeviceError; + use trezor_connect_rs::TrezorError as TcError; + + // Failure_PinInvalid = 7 + let tc_err = TcError::Device(DeviceError::from_failure( + Some(7), + "invalid pin".to_string(), + )); + let err: TrezorError = tc_err.into(); + + assert!(matches!(err, TrezorError::InvalidPin)); + } + + #[test] + fn test_failure_code_pin_cancelled_surfaces_as_pin_cancelled() { + use trezor_connect_rs::error::DeviceError; + use trezor_connect_rs::TrezorError as TcError; + + // Failure_PinCancelled = 6 + let tc_err = TcError::Device(DeviceError::from_failure(Some(6), "cancelled".to_string())); + let err: TrezorError = tc_err.into(); + + assert!(matches!(err, TrezorError::PinCancelled)); + } + + #[test] + fn test_failure_code_pin_expected_surfaces_as_pin_required() { + use trezor_connect_rs::error::DeviceError; + use trezor_connect_rs::TrezorError as TcError; + + // Failure_PinExpected = 5 + let tc_err = TcError::Device(DeviceError::from_failure( + Some(5), + "pin expected".to_string(), + )); + let err: TrezorError = tc_err.into(); + + assert!(matches!(err, TrezorError::PinRequired)); + } + + #[test] + fn test_failure_code_action_cancelled_surfaces_as_user_cancelled() { + use trezor_connect_rs::error::DeviceError; + use trezor_connect_rs::TrezorError as TcError; + + // Failure_ActionCancelled = 4 + let tc_err = TcError::Device(DeviceError::from_failure( + Some(4), + "action cancelled".to_string(), + )); + let err: TrezorError = tc_err.into(); + + assert!(matches!(err, TrezorError::UserCancelled)); + } + + #[test] + fn test_failure_code_unknown_stays_generic_device_error() { + use trezor_connect_rs::error::DeviceError; + use trezor_connect_rs::TrezorError as TcError; + + // Unknown code (Failure_FirmwareError = 99) must remain a generic device + // error so existing behavior is preserved. + let tc_err = TcError::Device(DeviceError::from_failure(Some(99), "boom".to_string())); + let err: TrezorError = tc_err.into(); + + match err { + TrezorError::DeviceError { error_details } => { + assert!(error_details.contains("99")); + assert!(error_details.contains("boom")); + } + other => panic!("expected generic DeviceError, got {other:?}"), + } + } + #[test] fn test_error_conversion_pairing_required() { use trezor_connect_rs::error::ThpError;