diff --git a/Cargo.lock b/Cargo.lock index 42e39c7..f17e7ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -475,7 +475,7 @@ dependencies = [ [[package]] name = "bitkitcore" -version = "0.4.1" +version = "0.4.2" dependencies = [ "android_logger", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index fbe42a2..849b979 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bitkitcore" -version = "0.4.1" +version = "0.4.2" edition = "2021" [lib] diff --git a/Package.swift b/Package.swift index 1421230..0b4c24d 100644 --- a/Package.swift +++ b/Package.swift @@ -3,8 +3,8 @@ import PackageDescription -let tag = "v0.4.1" -let checksum = "867bea0633b707c5da7eef38439b4a91310540cad07baa11be4ddf43da60b0f7" +let tag = "v0.4.2" +let checksum = "4d33cb126c8427668db63969950742822fd0ce9ee67043886fba7140f01a6365" 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 e48f767..9172785 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.4.1 +version=0.4.2 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 f5b8946..b2a27fb 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 31f15ed..690fcdf 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 1229d19..e15a504 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 41a56a8..27c88e9 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.android.kt b/bindings/android/lib/src/main/kotlin/com/synonym/bitkitcore/bitkitcore.android.kt index 99affbe..c086be2 100644 --- a/bindings/android/lib/src/main/kotlin/com/synonym/bitkitcore/bitkitcore.android.kt +++ b/bindings/android/lib/src/main/kotlin/com/synonym/bitkitcore/bitkitcore.android.kt @@ -1519,6 +1519,8 @@ internal typealias UniffiVTableCallbackInterfaceTrezorUiCallbackUniffiByValue = + + @@ -1918,6 +1920,9 @@ internal object IntegrityCheckingUniffiLib : Library { if (uniffi_bitkitcore_checksum_func_scan_legacy_rn_native_segwit_recovery_funds() != 52496.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } + if (uniffi_bitkitcore_checksum_func_serialized_extended_pubkey() != 12807.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } if (uniffi_bitkitcore_checksum_func_start_pubky_auth() != 18158.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } @@ -2456,6 +2461,9 @@ internal object IntegrityCheckingUniffiLib : Library { external fun uniffi_bitkitcore_checksum_func_scan_legacy_rn_native_segwit_recovery_funds( ): Short @JvmStatic + external fun uniffi_bitkitcore_checksum_func_serialized_extended_pubkey( + ): Short + @JvmStatic external fun uniffi_bitkitcore_checksum_func_start_pubky_auth( ): Short @JvmStatic @@ -3447,6 +3455,11 @@ internal object UniffiLib : Library { `bip39Passphrase`: RustBufferByValue, ): Long @JvmStatic + external fun uniffi_bitkitcore_fn_func_serialized_extended_pubkey( + `xpub`: RustBufferByValue, + uniffiCallStatus: UniffiRustCallStatus, + ): RustBufferByValue + @JvmStatic external fun uniffi_bitkitcore_fn_func_start_pubky_auth( `caps`: RustBufferByValue, ): Long @@ -10041,6 +10054,44 @@ public object FfiConverterTypeNetworkType: FfiConverterRustBuffer { +public object OnchainExceptionErrorHandler : UniffiRustCallStatusErrorHandler { + override fun lift(errorBuf: RustBufferByValue): OnchainException = FfiConverterTypeOnchainError.lift(errorBuf) +} + +public object FfiConverterTypeOnchainError : FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): OnchainException { + return when (buf.getInt()) { + 1 -> OnchainException.InvalidExtendedPublicKey( + FfiConverterString.read(buf), + ) + else -> throw RuntimeException("invalid error enum value, something is very wrong!!") + } + } + + override fun allocationSize(value: OnchainException): ULong { + return when (value) { + is OnchainException.InvalidExtendedPublicKey -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + + FfiConverterString.allocationSize(value.`errorDetails`) + ) + } + } + + override fun write(value: OnchainException, buf: ByteBuffer) { + when (value) { + is OnchainException.InvalidExtendedPublicKey -> { + buf.putInt(1) + FfiConverterString.write(value.`errorDetails`, buf) + Unit + } + }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } + } +} + + + + public object FfiConverterTypePassphraseResponse : FfiConverterRustBuffer{ override fun read(buf: ByteBuffer): PassphraseResponse { @@ -15119,6 +15170,16 @@ public suspend fun `scanLegacyRnNativeSegwitRecoveryFunds`(`mnemonicPhrase`: kot ) } +@Throws(OnchainException::class) +public fun `serializedExtendedPubkey`(`xpub`: kotlin.String): kotlin.ByteArray { + return FfiConverterByteArray.lift(uniffiRustCallWithError(OnchainExceptionErrorHandler) { uniffiRustCallStatus -> + UniffiLib.uniffi_bitkitcore_fn_func_serialized_extended_pubkey( + FfiConverterString.lower(`xpub`), + uniffiRustCallStatus, + ) + }) +} + @Throws(PubkyException::class, kotlin.coroutines.cancellation.CancellationException::class) public suspend fun `startPubkyAuth`(`caps`: kotlin.String): kotlin.String { return uniffiRustCallAsync( 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 bb0f867..d9538bb 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 @@ -3600,6 +3600,21 @@ public enum class NetworkType { + +public sealed class OnchainException: kotlin.Exception() { + + public class InvalidExtendedPublicKey( + public val `errorDetails`: kotlin.String, + ) : OnchainException() { + override val message: String + get() = "errorDetails=${ `errorDetails` }" + } + +} + + + + @kotlinx.serialization.Serializable public sealed class PassphraseResponse { @@ -4490,8 +4505,6 @@ public enum class WordCount { - - diff --git a/bindings/ios/BitkitCore.xcframework.zip b/bindings/ios/BitkitCore.xcframework.zip index 6e923f9..a438d90 100644 Binary files a/bindings/ios/BitkitCore.xcframework.zip and b/bindings/ios/BitkitCore.xcframework.zip differ diff --git a/bindings/ios/BitkitCore.xcframework/Info.plist b/bindings/ios/BitkitCore.xcframework/Info.plist index b7357e0..478a88f 100644 --- a/bindings/ios/BitkitCore.xcframework/Info.plist +++ b/bindings/ios/BitkitCore.xcframework/Info.plist @@ -10,7 +10,7 @@ HeadersPath Headers LibraryIdentifier - ios-arm64-simulator + ios-arm64 LibraryPath libbitkitcore.a SupportedArchitectures @@ -19,8 +19,6 @@ SupportedPlatform ios - SupportedPlatformVariant - simulator BinaryPath @@ -28,7 +26,7 @@ HeadersPath Headers LibraryIdentifier - ios-arm64 + ios-arm64-simulator LibraryPath libbitkitcore.a SupportedArchitectures @@ -37,6 +35,8 @@ SupportedPlatform ios + SupportedPlatformVariant + simulator CFBundlePackageType diff --git a/bindings/ios/BitkitCore.xcframework/ios-arm64-simulator/Headers/bitkitcoreFFI.h b/bindings/ios/BitkitCore.xcframework/ios-arm64-simulator/Headers/bitkitcoreFFI.h index 608cd28..f8b299d 100644 --- a/bindings/ios/BitkitCore.xcframework/ios-arm64-simulator/Headers/bitkitcoreFFI.h +++ b/bindings/ios/BitkitCore.xcframework/ios-arm64-simulator/Headers/bitkitcoreFFI.h @@ -1111,6 +1111,11 @@ RustBuffer uniffi_bitkitcore_fn_func_resolve_pubky_url(RustBuffer uri, RustCallS uint64_t uniffi_bitkitcore_fn_func_scan_legacy_rn_native_segwit_recovery_funds(RustBuffer mnemonic_phrase, RustBuffer network, RustBuffer electrum_url, uint32_t index_limit, RustBuffer bip39_passphrase ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_SERIALIZED_EXTENDED_PUBKEY +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_SERIALIZED_EXTENDED_PUBKEY +RustBuffer uniffi_bitkitcore_fn_func_serialized_extended_pubkey(RustBuffer xpub, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_START_PUBKY_AUTH #define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_START_PUBKY_AUTH uint64_t uniffi_bitkitcore_fn_func_start_pubky_auth(RustBuffer caps @@ -2341,6 +2346,12 @@ uint16_t uniffi_bitkitcore_checksum_func_resolve_pubky_url(void #define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_SCAN_LEGACY_RN_NATIVE_SEGWIT_RECOVERY_FUNDS uint16_t uniffi_bitkitcore_checksum_func_scan_legacy_rn_native_segwit_recovery_funds(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_SERIALIZED_EXTENDED_PUBKEY +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_SERIALIZED_EXTENDED_PUBKEY +uint16_t uniffi_bitkitcore_checksum_func_serialized_extended_pubkey(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_START_PUBKY_AUTH diff --git a/bindings/ios/BitkitCore.xcframework/ios-arm64-simulator/libbitkitcore.a b/bindings/ios/BitkitCore.xcframework/ios-arm64-simulator/libbitkitcore.a index ca8fede..c727837 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/Headers/bitkitcoreFFI.h b/bindings/ios/BitkitCore.xcframework/ios-arm64/Headers/bitkitcoreFFI.h index 608cd28..f8b299d 100644 --- a/bindings/ios/BitkitCore.xcframework/ios-arm64/Headers/bitkitcoreFFI.h +++ b/bindings/ios/BitkitCore.xcframework/ios-arm64/Headers/bitkitcoreFFI.h @@ -1111,6 +1111,11 @@ RustBuffer uniffi_bitkitcore_fn_func_resolve_pubky_url(RustBuffer uri, RustCallS uint64_t uniffi_bitkitcore_fn_func_scan_legacy_rn_native_segwit_recovery_funds(RustBuffer mnemonic_phrase, RustBuffer network, RustBuffer electrum_url, uint32_t index_limit, RustBuffer bip39_passphrase ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_SERIALIZED_EXTENDED_PUBKEY +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_SERIALIZED_EXTENDED_PUBKEY +RustBuffer uniffi_bitkitcore_fn_func_serialized_extended_pubkey(RustBuffer xpub, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_START_PUBKY_AUTH #define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_START_PUBKY_AUTH uint64_t uniffi_bitkitcore_fn_func_start_pubky_auth(RustBuffer caps @@ -2341,6 +2346,12 @@ uint16_t uniffi_bitkitcore_checksum_func_resolve_pubky_url(void #define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_SCAN_LEGACY_RN_NATIVE_SEGWIT_RECOVERY_FUNDS uint16_t uniffi_bitkitcore_checksum_func_scan_legacy_rn_native_segwit_recovery_funds(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_SERIALIZED_EXTENDED_PUBKEY +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_SERIALIZED_EXTENDED_PUBKEY +uint16_t uniffi_bitkitcore_checksum_func_serialized_extended_pubkey(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_START_PUBKY_AUTH diff --git a/bindings/ios/BitkitCore.xcframework/ios-arm64/libbitkitcore.a b/bindings/ios/BitkitCore.xcframework/ios-arm64/libbitkitcore.a index 34b9ab4..e5cd22b 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 267fcaa..51568c7 100644 --- a/bindings/ios/bitkitcore.swift +++ b/bindings/ios/bitkitcore.swift @@ -16506,6 +16506,84 @@ extension NetworkType: Codable {} + +public enum OnchainError: Swift.Error { + + + + case InvalidExtendedPublicKey(errorDetails: String + ) +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeOnchainError: FfiConverterRustBuffer { + typealias SwiftType = OnchainError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> OnchainError { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .InvalidExtendedPublicKey( + errorDetails: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: OnchainError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .InvalidExtendedPublicKey(errorDetails): + writeInt(&buf, Int32(1)) + FfiConverterString.write(errorDetails, into: &buf) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeOnchainError_lift(_ buf: RustBuffer) throws -> OnchainError { + return try FfiConverterTypeOnchainError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeOnchainError_lower(_ value: OnchainError) -> RustBuffer { + return FfiConverterTypeOnchainError.lower(value) +} + + +extension OnchainError: Equatable, Hashable {} + +extension OnchainError: Codable {} + + + + +extension OnchainError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. @@ -21806,6 +21884,13 @@ public func scanLegacyRnNativeSegwitRecoveryFunds(mnemonicPhrase: String, networ errorHandler: FfiConverterTypeSweepError_lift ) } +public func serializedExtendedPubkey(xpub: String)throws -> Data { + return try FfiConverterData.lift(try rustCallWithError(FfiConverterTypeOnchainError_lift) { + uniffi_bitkitcore_fn_func_serialized_extended_pubkey( + FfiConverterString.lower(xpub),$0 + ) +}) +} public func startPubkyAuth(caps: String)async throws -> String { return try await uniffiRustCallAsync( @@ -22775,6 +22860,9 @@ private let initializationResult: InitializationResult = { if (uniffi_bitkitcore_checksum_func_scan_legacy_rn_native_segwit_recovery_funds() != 52496) { return InitializationResult.apiChecksumMismatch } + if (uniffi_bitkitcore_checksum_func_serialized_extended_pubkey() != 12807) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_bitkitcore_checksum_func_start_pubky_auth() != 18158) { return InitializationResult.apiChecksumMismatch } diff --git a/bindings/ios/bitkitcoreFFI.h b/bindings/ios/bitkitcoreFFI.h index 608cd28..f8b299d 100644 --- a/bindings/ios/bitkitcoreFFI.h +++ b/bindings/ios/bitkitcoreFFI.h @@ -1111,6 +1111,11 @@ RustBuffer uniffi_bitkitcore_fn_func_resolve_pubky_url(RustBuffer uri, RustCallS uint64_t uniffi_bitkitcore_fn_func_scan_legacy_rn_native_segwit_recovery_funds(RustBuffer mnemonic_phrase, RustBuffer network, RustBuffer electrum_url, uint32_t index_limit, RustBuffer bip39_passphrase ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_SERIALIZED_EXTENDED_PUBKEY +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_SERIALIZED_EXTENDED_PUBKEY +RustBuffer uniffi_bitkitcore_fn_func_serialized_extended_pubkey(RustBuffer xpub, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_START_PUBKY_AUTH #define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_START_PUBKY_AUTH uint64_t uniffi_bitkitcore_fn_func_start_pubky_auth(RustBuffer caps @@ -2341,6 +2346,12 @@ uint16_t uniffi_bitkitcore_checksum_func_resolve_pubky_url(void #define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_SCAN_LEGACY_RN_NATIVE_SEGWIT_RECOVERY_FUNDS uint16_t uniffi_bitkitcore_checksum_func_scan_legacy_rn_native_segwit_recovery_funds(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_SERIALIZED_EXTENDED_PUBKEY +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_SERIALIZED_EXTENDED_PUBKEY +uint16_t uniffi_bitkitcore_checksum_func_serialized_extended_pubkey(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_START_PUBKY_AUTH diff --git a/bindings/python/bitkitcore/bitkitcore.py b/bindings/python/bitkitcore/bitkitcore.py index fa24beb..7b85998 100644 --- a/bindings/python/bitkitcore/bitkitcore.py +++ b/bindings/python/bitkitcore/bitkitcore.py @@ -699,6 +699,8 @@ def _uniffi_check_api_checksums(lib): raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") if lib.uniffi_bitkitcore_checksum_func_scan_legacy_rn_native_segwit_recovery_funds() != 52496: raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + if lib.uniffi_bitkitcore_checksum_func_serialized_extended_pubkey() != 12807: + raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") if lib.uniffi_bitkitcore_checksum_func_start_pubky_auth() != 18158: raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") if lib.uniffi_bitkitcore_checksum_func_test_notification() != 32857: @@ -1783,6 +1785,11 @@ class _UniffiVTableCallbackInterfaceTrezorUiCallback(ctypes.Structure): _UniffiRustBuffer, ) _UniffiLib.uniffi_bitkitcore_fn_func_scan_legacy_rn_native_segwit_recovery_funds.restype = ctypes.c_uint64 +_UniffiLib.uniffi_bitkitcore_fn_func_serialized_extended_pubkey.argtypes = ( + _UniffiRustBuffer, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.uniffi_bitkitcore_fn_func_serialized_extended_pubkey.restype = _UniffiRustBuffer _UniffiLib.uniffi_bitkitcore_fn_func_start_pubky_auth.argtypes = ( _UniffiRustBuffer, ) @@ -2601,6 +2608,9 @@ class _UniffiVTableCallbackInterfaceTrezorUiCallback(ctypes.Structure): _UniffiLib.uniffi_bitkitcore_checksum_func_scan_legacy_rn_native_segwit_recovery_funds.argtypes = ( ) _UniffiLib.uniffi_bitkitcore_checksum_func_scan_legacy_rn_native_segwit_recovery_funds.restype = ctypes.c_uint16 +_UniffiLib.uniffi_bitkitcore_checksum_func_serialized_extended_pubkey.argtypes = ( +) +_UniffiLib.uniffi_bitkitcore_checksum_func_serialized_extended_pubkey.restype = ctypes.c_uint16 _UniffiLib.uniffi_bitkitcore_checksum_func_start_pubky_auth.argtypes = ( ) _UniffiLib.uniffi_bitkitcore_checksum_func_start_pubky_auth.restype = ctypes.c_uint16 @@ -13272,6 +13282,56 @@ def write(value, buf): +# OnchainError +# We want to define each variant as a nested class that's also a subclass, +# which is tricky in Python. To accomplish this we're going to create each +# class separately, then manually add the child classes to the base class's +# __dict__. All of this happens in dummy class to avoid polluting the module +# namespace. +class OnchainError(Exception): + pass + +_UniffiTempOnchainError = OnchainError + +class OnchainError: # type: ignore + class InvalidExtendedPublicKey(_UniffiTempOnchainError): + def __init__(self, error_details): + super().__init__(", ".join([ + "error_details={!r}".format(error_details), + ])) + self.error_details = error_details + + def __repr__(self): + return "OnchainError.InvalidExtendedPublicKey({})".format(str(self)) + _UniffiTempOnchainError.InvalidExtendedPublicKey = InvalidExtendedPublicKey # type: ignore + +OnchainError = _UniffiTempOnchainError # type: ignore +del _UniffiTempOnchainError + + +class _UniffiConverterTypeOnchainError(_UniffiConverterRustBuffer): + @staticmethod + def read(buf): + variant = buf.read_i32() + if variant == 1: + return OnchainError.InvalidExtendedPublicKey( + _UniffiConverterString.read(buf), + ) + raise InternalError("Raw enum value doesn't match any cases") + + @staticmethod + def check_lower(value): + if isinstance(value, OnchainError.InvalidExtendedPublicKey): + _UniffiConverterString.check_lower(value.error_details) + return + + @staticmethod + def write(value, buf): + if isinstance(value, OnchainError.InvalidExtendedPublicKey): + buf.write_i32(1) + _UniffiConverterString.write(value.error_details, buf) + + @@ -20805,6 +20865,13 @@ async def scan_legacy_rn_native_segwit_recovery_funds(mnemonic_phrase: "str",net _UniffiConverterTypeSweepError, ) + +def serialized_extended_pubkey(xpub: "str") -> "bytes": + _UniffiConverterString.check_lower(xpub) + + return _UniffiConverterBytes.lift(_uniffi_rust_call_with_error(_UniffiConverterTypeOnchainError,_UniffiLib.uniffi_bitkitcore_fn_func_serialized_extended_pubkey, + _UniffiConverterString.lower(xpub))) + async def start_pubky_auth(caps: "str") -> "str": _UniffiConverterString.check_lower(caps) @@ -21535,6 +21602,7 @@ def wipe_all_transaction_details() -> None: "ManualRefundStateEnum", "Network", "NetworkType", + "OnchainError", "PassphraseResponse", "PaymentState", "PaymentType", @@ -21767,6 +21835,7 @@ def wipe_all_transaction_details() -> None: "reset_pre_activity_metadata_tags", "resolve_pubky_url", "scan_legacy_rn_native_segwit_recovery_funds", + "serialized_extended_pubkey", "start_pubky_auth", "test_notification", "transaction_details_from_json", @@ -21816,4 +21885,3 @@ def wipe_all_transaction_details() -> None: "TrezorTransportCallback", "TrezorUiCallback", ] - diff --git a/bindings/python/bitkitcore/libbitkitcore.dylib b/bindings/python/bitkitcore/libbitkitcore.dylib index ee3e21d..5c2cc7d 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 550ab36..af17377 100644 --- a/bindings/python/setup.py +++ b/bindings/python/setup.py @@ -2,7 +2,7 @@ setup( name="bitkitcore", - version="0.4.1", + version="0.4.2", packages=find_packages(), package_data={ "bitkitcore": ["*.so", "*.dylib", "*.dll"], diff --git a/build_ios.sh b/build_ios.sh index 1bf1787..0a1f1f7 100755 --- a/build_ios.sh +++ b/build_ios.sh @@ -33,6 +33,11 @@ echo "Building for iOS targets..." cargo build --release --target=aarch64-apple-ios-sim cargo build --release --target=aarch64-apple-ios +# Keep release archives below GitHub's file-size limit without removing exported symbols. +echo "Stripping debug symbols from iOS libraries..." +xcrun strip -S ./target/aarch64-apple-ios-sim/release/libbitkitcore.a +xcrun strip -S ./target/aarch64-apple-ios/release/libbitkitcore.a + # Generate Swift bindings echo "Generating Swift bindings..." # First, ensure any existing generated files are removed @@ -103,4 +108,4 @@ echo "New checksum: $CHECKSUM" echo "Updating Package.swift with new checksum..." python3 ./update_package.py --checksum "$CHECKSUM" || { echo "Failed to update Package.swift"; exit 1; } -echo "iOS build process completed successfully!" \ No newline at end of file +echo "iOS build process completed successfully!" diff --git a/src/lib.rs b/src/lib.rs index f8d162c..147a7e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,9 +52,9 @@ use crate::onchain::{ broadcast_raw_tx, get_account_info, get_address_info, get_transaction_detail, get_transaction_history, AccountInfoError, AccountInfoResult, AccountType, AddressError, BroadcastError, GetAddressResponse, GetAddressesResponse, LegacyRnCloseRecoveryScanResult, - LegacyRnCloseRecoverySweepPreview, Network, SingleAddressInfoResult, SweepError, SweepResult, - SweepTransactionPreview, SweepableBalances, TransactionDetail, TransactionHistoryResult, - ValidationResult, DEFAULT_GAP_LIMIT, + LegacyRnCloseRecoverySweepPreview, Network, OnchainError, SingleAddressInfoResult, SweepError, + SweepResult, SweepTransactionPreview, SweepableBalances, TransactionDetail, + TransactionHistoryResult, ValidationResult, DEFAULT_GAP_LIMIT, }; use crate::onchain::{compose_transaction, ComposeParams, ComposeResult}; use crate::onchain::{ @@ -216,6 +216,11 @@ pub fn validate_bitcoin_address(address: String) -> Result Result, OnchainError> { + onchain::serialized_extended_pubkey(&xpub) +} + #[uniffi::export] pub fn generate_mnemonic(word_count: Option) -> Result { let external_word_count = word_count.map(|wc| wc.into()); diff --git a/src/modules/onchain/errors.rs b/src/modules/onchain/errors.rs index 505a2fc..f8c0e4b 100644 --- a/src/modules/onchain/errors.rs +++ b/src/modules/onchain/errors.rs @@ -1,5 +1,12 @@ use thiserror::Error; +#[derive(uniffi::Error, Debug, Error)] +#[non_exhaustive] +pub enum OnchainError { + #[error("Invalid extended public key: {error_details}")] + InvalidExtendedPublicKey { error_details: String }, +} + #[derive(uniffi::Error, Debug, Error)] #[non_exhaustive] pub enum AddressError { diff --git a/src/modules/onchain/extended_pubkey.rs b/src/modules/onchain/extended_pubkey.rs new file mode 100644 index 0000000..1691853 --- /dev/null +++ b/src/modules/onchain/extended_pubkey.rs @@ -0,0 +1,80 @@ +use std::str::FromStr; + +use bitcoin::bip32::Xpub; + +use super::OnchainError; + +/// Decode a standard BIP32 extended public key into its canonical 78-byte payload. +pub fn serialized_extended_pubkey(xpub: &str) -> Result, OnchainError> { + Xpub::from_str(xpub) + .map(|extended_pubkey| extended_pubkey.encode().to_vec()) + .map_err(|error| OnchainError::InvalidExtendedPublicKey { + error_details: error.to_string(), + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + const MAINNET_XPUB: &str = "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"; + const MAINNET_SERIALIZED_HEX: &str = "0488b21e000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d5080339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2"; + const TESTNET_TPUB: &str = "tpubDDWohsp5dx2iMJ9N7iHbgAEDhH4BJB9NWW1fEW3yA3AFNDREmpzteCXNqppMLUmKFY5q5e3PXtS5CuqWCQbYcGhpPqYAgQSYdwknW9J6sQv"; + const TESTNET_SERIALIZED_HEX: &str = "043587cf03caafd489800000004b5fcc4a5fe210d9fba6616b4db1d025237dd7f035101f11f562401bc710469902e0bf22b51a6a49e0b149b995670d0ed9bb1fd99417748bacefba88fae655572d"; + + #[test] + fn mainnet_xpub_returns_canonical_78_byte_payload() { + let serialized = serialized_extended_pubkey(MAINNET_XPUB).unwrap(); + + assert_eq!(serialized.len(), 78); + assert_eq!(hex::encode(serialized), MAINNET_SERIALIZED_HEX); + } + + #[test] + fn testnet_and_regtest_tpub_returns_canonical_78_byte_payload() { + let serialized = serialized_extended_pubkey(TESTNET_TPUB).unwrap(); + + assert_eq!(serialized.len(), 78); + assert_eq!(hex::encode(serialized), TESTNET_SERIALIZED_HEX); + } + + #[test] + fn invalid_base58_character_is_rejected() { + let invalid_xpub = format!("0{}", &MAINNET_XPUB[1..]); + + assert!(matches!( + serialized_extended_pubkey(&invalid_xpub), + Err(OnchainError::InvalidExtendedPublicKey { .. }) + )); + } + + #[test] + fn invalid_base58check_checksum_is_rejected() { + let invalid_xpub = format!("{}1", &MAINNET_XPUB[..MAINNET_XPUB.len() - 1]); + + assert!(matches!( + serialized_extended_pubkey(&invalid_xpub), + Err(OnchainError::InvalidExtendedPublicKey { .. }) + )); + } + + #[test] + fn base58check_payload_without_valid_extended_public_key_is_rejected() { + let mut payload = [0_u8; 78]; + payload[..4].copy_from_slice(&[0x04, 0x88, 0xb2, 0x1e]); + let encoded = bitcoin::base58::encode_check(&payload); + + assert!(matches!( + serialized_extended_pubkey(&encoded), + Err(OnchainError::InvalidExtendedPublicKey { .. }) + )); + } + + #[test] + fn returned_bytes_round_trip_through_rust_bitcoin_serializer() { + let serialized = serialized_extended_pubkey(MAINNET_XPUB).unwrap(); + let decoded = Xpub::decode(&serialized).unwrap(); + + assert_eq!(decoded.to_string(), MAINNET_XPUB); + } +} diff --git a/src/modules/onchain/mod.rs b/src/modules/onchain/mod.rs index e4c3907..81ce0e9 100644 --- a/src/modules/onchain/mod.rs +++ b/src/modules/onchain/mod.rs @@ -1,11 +1,13 @@ mod compose; mod errors; +mod extended_pubkey; mod implementation; mod listener; mod types; pub use compose::compose_transaction; -pub use errors::{AccountInfoError, AddressError, BroadcastError, SweepError}; +pub use errors::{AccountInfoError, AddressError, BroadcastError, OnchainError, SweepError}; +pub use extended_pubkey::serialized_extended_pubkey; pub use implementation::{ broadcast_raw_tx, build_descriptors, derive_base_path, detect_account_type, detect_network_from_key, get_account_info, get_address_info, get_transaction_detail,