From 71496cd56a1c3330253f9b7143c243d2ee852d09 Mon Sep 17 00:00:00 2001 From: Steve Myers Date: Tue, 23 Nov 2021 22:46:38 -0800 Subject: [PATCH] Simplify build.sh and add template bdkFFI.xcframework to repo --- .gitignore | 10 +- Sources/BitcoinDevKit/BitcoinDevKit.swift | 2113 ----------------- bdkFFI.xcframework/Info.plist | 52 + .../Headers/bdkFFI-umbrella.h | 4 + .../bdkFFI.framework/Modules/module.modulemap | 6 + .../Headers/bdkFFI-umbrella.h | 4 + .../bdkFFI.framework/Modules/module.modulemap | 6 + .../Headers/bdkFFI-umbrella.h | 4 + .../bdkFFI.framework/Modules/module.modulemap | 6 + build.sh | 82 +- 10 files changed, 120 insertions(+), 2167 deletions(-) delete mode 100644 Sources/BitcoinDevKit/BitcoinDevKit.swift create mode 100644 bdkFFI.xcframework/Info.plist create mode 100644 bdkFFI.xcframework/ios-arm64/bdkFFI.framework/Headers/bdkFFI-umbrella.h create mode 100644 bdkFFI.xcframework/ios-arm64/bdkFFI.framework/Modules/module.modulemap create mode 100644 bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/Headers/bdkFFI-umbrella.h create mode 100644 bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/Modules/module.modulemap create mode 100644 bdkFFI.xcframework/macos-x86_64/bdkFFI.framework/Headers/bdkFFI-umbrella.h create mode 100644 bdkFFI.xcframework/macos-x86_64/bdkFFI.framework/Modules/module.modulemap diff --git a/.gitignore b/.gitignore index b18ca41..c850c2d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,8 +6,8 @@ xcuserdata/ DerivedData/ .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata -/*.xcframework -!bdkFFI-umbrella.h -!module.modulemap -!info.plist -*.xcframework.zip +bdkFFI.xcframework.zip +bdkFFI +bdkFFI.h +bdk.swift +BitcoinDevKit.swift diff --git a/Sources/BitcoinDevKit/BitcoinDevKit.swift b/Sources/BitcoinDevKit/BitcoinDevKit.swift deleted file mode 100644 index 1c9432e..0000000 --- a/Sources/BitcoinDevKit/BitcoinDevKit.swift +++ /dev/null @@ -1,2113 +0,0 @@ -// This file was autogenerated by some hot garbage in the `uniffi` crate. -// Trust me, you don't want to mess with it! -import Foundation - -// Depending on the consumer's build setup, the low-level FFI code -// might be in a separate module, or it might be compiled inline into -// this module. This is a bit of light hackery to work with both. -#if canImport(bdkFFI) -import bdkFFI -#endif - -fileprivate extension RustBuffer { - // Allocate a new buffer, copying the contents of a `UInt8` array. - init(bytes: [UInt8]) { - let rbuf = bytes.withUnsafeBufferPointer { ptr in - RustBuffer.from(ptr) - } - self.init(capacity: rbuf.capacity, len: rbuf.len, data: rbuf.data) - } - - static func from(_ ptr: UnsafeBufferPointer) -> RustBuffer { - try! rustCall { ffi_bdk_2e4d_rustbuffer_from_bytes(ForeignBytes(bufferPointer: ptr), $0) } - } - - // Frees the buffer in place. - // The buffer must not be used after this is called. - func deallocate() { - try! rustCall { ffi_bdk_2e4d_rustbuffer_free(self, $0) } - } -} - -fileprivate extension ForeignBytes { - init(bufferPointer: UnsafeBufferPointer) { - self.init(len: Int32(bufferPointer.count), data: bufferPointer.baseAddress) - } -} - -// For every type used in the interface, we provide helper methods for conveniently -// lifting and lowering that type from C-compatible data, and for reading and writing -// values of that type in a buffer. - -// Helper classes/extensions that don't change. -// Someday, this will be in a libray of its own. - -fileprivate extension Data { - init(rustBuffer: RustBuffer) { - // TODO: This copies the buffer. Can we read directly from a - // Rust buffer? - self.init(bytes: rustBuffer.data!, count: Int(rustBuffer.len)) - } -} - -// A helper class to read values out of a byte buffer. -fileprivate class Reader { - let data: Data - var offset: Data.Index - - init(data: Data) { - self.data = data - self.offset = 0 - } - - // Reads an integer at the current offset, in big-endian order, and advances - // the offset on success. Throws if reading the integer would move the - // offset past the end of the buffer. - func readInt() throws -> T { - let range = offset...size - guard data.count >= range.upperBound else { - throw UniffiInternalError.bufferOverflow - } - if T.self == UInt8.self { - let value = data[offset] - offset += 1 - return value as! T - } - var value: T = 0 - let _ = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0, from: range)}) - offset = range.upperBound - return value.bigEndian - } - - // Reads an arbitrary number of bytes, to be used to read - // raw bytes, this is useful when lifting strings - func readBytes(count: Int) throws -> Array { - let range = offset..<(offset+count) - guard data.count >= range.upperBound else { - throw UniffiInternalError.bufferOverflow - } - var value = [UInt8](repeating: 0, count: count) - value.withUnsafeMutableBufferPointer({ buffer in - data.copyBytes(to: buffer, from: range) - }) - offset = range.upperBound - return value - } - - // Reads a float at the current offset. - @inlinable - func readFloat() throws -> Float { - return Float(bitPattern: try readInt()) - } - - // Reads a float at the current offset. - @inlinable - func readDouble() throws -> Double { - return Double(bitPattern: try readInt()) - } - - // Indicates if the offset has reached the end of the buffer. - @inlinable - func hasRemaining() -> Bool { - return offset < data.count - } -} - -// A helper class to write values into a byte buffer. -fileprivate class Writer { - var bytes: [UInt8] - var offset: Array.Index - - init() { - self.bytes = [] - self.offset = 0 - } - - func writeBytes(_ byteArr: S) where S: Sequence, S.Element == UInt8 { - bytes.append(contentsOf: byteArr) - } - - // Writes an integer in big-endian order. - // - // Warning: make sure what you are trying to write - // is in the correct type! - func writeInt(_ value: T) { - var value = value.bigEndian - withUnsafeBytes(of: &value) { bytes.append(contentsOf: $0) } - } - - @inlinable - func writeFloat(_ value: Float) { - writeInt(value.bitPattern) - } - - @inlinable - func writeDouble(_ value: Double) { - writeInt(value.bitPattern) - } -} - - -// Types conforming to `Serializable` can be read and written in a bytebuffer. -fileprivate protocol Serializable { - func write(into: Writer) - static func read(from: Reader) throws -> Self -} - -// Types confirming to `ViaFfi` can be transferred back-and-for over the FFI. -// This is analogous to the Rust trait of the same name. -fileprivate protocol ViaFfi: Serializable { - associatedtype FfiType - static func lift(_ v: FfiType) throws -> Self - func lower() -> FfiType -} - -// Types conforming to `Primitive` pass themselves directly over the FFI. -fileprivate protocol Primitive {} - -extension Primitive { - fileprivate typealias FfiType = Self - - fileprivate static func lift(_ v: Self) throws -> Self { - return v - } - - fileprivate func lower() -> Self { - return self - } -} - -// Types conforming to `ViaFfiUsingByteBuffer` lift and lower into a bytebuffer. -// Use this for complex types where it's hard to write a custom lift/lower. -fileprivate protocol ViaFfiUsingByteBuffer: Serializable {} - -extension ViaFfiUsingByteBuffer { - fileprivate typealias FfiType = RustBuffer - - fileprivate static func lift(_ buf: FfiType) throws -> Self { - let reader = Reader(data: Data(rustBuffer: buf)) - let value = try Self.read(from: reader) - if reader.hasRemaining() { - throw UniffiInternalError.incompleteData - } - buf.deallocate() - return value - } - - fileprivate func lower() -> FfiType { - let writer = Writer() - self.write(into: writer) - return RustBuffer(bytes: writer.bytes) - } -} -// An error type for FFI errors. These errors occur at the UniFFI level, not -// the library level. -fileprivate enum UniffiInternalError: LocalizedError { - case bufferOverflow - case incompleteData - case unexpectedOptionalTag - case unexpectedEnumCase - case unexpectedNullPointer - case unexpectedRustCallStatusCode - case unexpectedRustCallError - case unexpectedStaleHandle - case rustPanic(_ message: String) - - public var errorDescription: String? { - switch self { - case .bufferOverflow: return "Reading the requested value would read past the end of the buffer" - case .incompleteData: return "The buffer still has data after lifting its containing value" - case .unexpectedOptionalTag: return "Unexpected optional tag; should be 0 or 1" - case .unexpectedEnumCase: return "Raw enum value doesn't match any cases" - case .unexpectedNullPointer: return "Raw pointer value was null" - case .unexpectedRustCallStatusCode: return "Unexpected RustCallStatus code" - case .unexpectedRustCallError: return "CALL_ERROR but no errorClass specified" - case .unexpectedStaleHandle: return "The object in the handle map has been dropped already" - case let .rustPanic(message): return message - } - } -} - -fileprivate let CALL_SUCCESS: Int8 = 0 -fileprivate let CALL_ERROR: Int8 = 1 -fileprivate let CALL_PANIC: Int8 = 2 - -fileprivate extension RustCallStatus { - init() { - self.init( - code: CALL_SUCCESS, - errorBuf: RustBuffer.init( - capacity: 0, - len: 0, - data: nil - ) - ) - } -} - -private func rustCall(_ callback: (UnsafeMutablePointer) -> T) throws -> T { - try makeRustCall(callback, errorHandler: { - $0.deallocate() - return UniffiInternalError.unexpectedRustCallError - }) -} - -private func rustCallWithError(_ errorClass: E.Type, _ callback: (UnsafeMutablePointer) -> T) throws -> T { - try makeRustCall(callback, errorHandler: { return try E.lift($0) }) -} - -private func makeRustCall(_ callback: (UnsafeMutablePointer) -> T, errorHandler: (RustBuffer) throws -> Error) throws -> T { - var callStatus = RustCallStatus.init() - let returnedVal = callback(&callStatus) - switch callStatus.code { - case CALL_SUCCESS: - return returnedVal - - case CALL_ERROR: - throw try errorHandler(callStatus.errorBuf) - - case CALL_PANIC: - // When the rust code sees a panic, it tries to construct a RustBuffer - // with the message. But if that code panics, then it just sends back - // an empty buffer. - if callStatus.errorBuf.len > 0 { - throw UniffiInternalError.rustPanic(try String.lift(callStatus.errorBuf)) - } else { - callStatus.errorBuf.deallocate() - throw UniffiInternalError.rustPanic("Rust panic") - } - - default: - throw UniffiInternalError.unexpectedRustCallStatusCode - } -} -// Protocols for converters we'll implement in templates - -fileprivate protocol FfiConverter { - associatedtype SwiftType - associatedtype FfiType - - static func lift(_ ffiValue: FfiType) throws -> SwiftType - static func lower(_ value: SwiftType) -> FfiType - - static func read(from: Reader) throws -> SwiftType - static func write(_ value: SwiftType, into: Writer) -} - -fileprivate protocol FfiConverterUsingByteBuffer: FfiConverter where FfiType == RustBuffer { - // Empty, because we want to declare some helper methods in the extension below. -} - -extension FfiConverterUsingByteBuffer { - static func lower(_ value: SwiftType) -> FfiType { - let writer = Writer() - Self.write(value, into: writer) - return RustBuffer(bytes: writer.bytes) - } - - static func lift(_ buf: FfiType) throws -> SwiftType { - let reader = Reader(data: Data(rustBuffer: buf)) - let value = try Self.read(from: reader) - if reader.hasRemaining() { - throw UniffiInternalError.incompleteData - } - buf.deallocate() - return value - } -} - -// Helpers for structural types. Note that because of canonical_names, it /should/ be impossible -// to make another `FfiConverterSequence` etc just using the UDL. -fileprivate enum FfiConverterSequence { - static func write(_ value: [T], into buf: Writer, writeItem: (T, Writer) -> Void) { - let len = Int32(value.count) - buf.writeInt(len) - for item in value { - writeItem(item, buf) - } - } - - static func read(from buf: Reader, readItem: (Reader) throws -> T) throws -> [T] { - let len: Int32 = try buf.readInt() - var seq = [T]() - seq.reserveCapacity(Int(len)) - for _ in 0 ..< len { - seq.append(try readItem(buf)) - } - return seq - } -} - -fileprivate enum FfiConverterOptional { - static func write(_ value: T?, into buf: Writer, writeItem: (T, Writer) -> Void) { - guard let value = value else { - buf.writeInt(Int8(0)) - return - } - buf.writeInt(Int8(1)) - writeItem(value, buf) - } - - static func read(from buf: Reader, readItem: (Reader) throws -> T) throws -> T? { - switch try buf.readInt() as Int8 { - case 0: return nil - case 1: return try readItem(buf) - default: throw UniffiInternalError.unexpectedOptionalTag - } - } -} - -fileprivate enum FfiConverterDictionary { - static func write(_ value: [String: T], into buf: Writer, writeItem: (String, T, Writer) -> Void) { - let len = Int32(value.count) - buf.writeInt(len) - for (key, value) in value { - writeItem(key, value, buf) - } - } - - static func read(from buf: Reader, readItem: (Reader) throws -> (String, T)) throws -> [String: T] { - let len: Int32 = try buf.readInt() - var dict = [String: T]() - dict.reserveCapacity(Int(len)) - for _ in 0..(f: () throws -> T) rethrows -> T { - self.lock() - defer { self.unlock() } - return try f() - } -} - -fileprivate typealias Handle = UInt64 -fileprivate class ConcurrentHandleMap { - private var leftMap: [Handle: T] = [:] - private var counter: [Handle: UInt64] = [:] - private var rightMap: [ObjectIdentifier: Handle] = [:] - - private let lock = NSLock() - private var currentHandle: Handle = 0 - private let stride: Handle = 1 - - func insert(obj: T) -> Handle { - lock.withLock { - let id = ObjectIdentifier(obj as AnyObject) - let handle = rightMap[id] ?? { - currentHandle += stride - let handle = currentHandle - leftMap[handle] = obj - rightMap[id] = handle - return handle - }() - counter[handle] = (counter[handle] ?? 0) + 1 - return handle - } - } - - func get(handle: Handle) -> T? { - lock.withLock { - leftMap[handle] - } - } - - func delete(handle: Handle) { - remove(handle: handle) - } - - @discardableResult - func remove(handle: Handle) -> T? { - lock.withLock { - defer { counter[handle] = (counter[handle] ?? 1) - 1 } - guard counter[handle] == 1 else { return leftMap[handle] } - let obj = leftMap.removeValue(forKey: handle) - if let obj = obj { - rightMap.removeValue(forKey: ObjectIdentifier(obj as AnyObject)) - } - return obj - } - } -} - -// Magic number for the Rust proxy to call using the same mechanism as every other method, -// to free the callback once it's dropped by Rust. -private let IDX_CALLBACK_FREE: Int32 = 0 - -fileprivate class FfiConverterCallbackInterface { - fileprivate let handleMap = ConcurrentHandleMap() - - func drop(handle: Handle) { - handleMap.remove(handle: handle) - } - - func lift(_ handle: Handle) throws -> CallbackInterface { - guard let callback = handleMap.get(handle: handle) else { - throw UniffiInternalError.unexpectedStaleHandle - } - return callback - } - - func read(from buf: Reader) throws -> CallbackInterface { - let handle: Handle = try buf.readInt() - return try lift(handle) - } - - func lower(_ v: CallbackInterface) -> Handle { - let handle = handleMap.insert(obj: v) - return handle - // assert(handleMap.get(handle: obj) == v, "Handle map is not returning the object we just placed there. This is a bug in the HandleMap.") - } - - func write(_ v: CallbackInterface, into buf: Writer) { - buf.writeInt(lower(v)) - } -} - -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. - -public enum Network { - - case bitcoin - case testnet - case signet - case regtest -} - -extension Network: ViaFfiUsingByteBuffer, ViaFfi { - fileprivate static func read(from buf: Reader) throws -> Network { - let variant: Int32 = try buf.readInt() - switch variant { - - case 1: return .bitcoin - case 2: return .testnet - case 3: return .signet - case 4: return .regtest - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - fileprivate func write(into buf: Writer) { - switch self { - - - case .bitcoin: - buf.writeInt(Int32(1)) - - - case .testnet: - buf.writeInt(Int32(2)) - - - case .signet: - buf.writeInt(Int32(3)) - - - case .regtest: - buf.writeInt(Int32(4)) - - } - } -} - - -extension Network: Equatable, Hashable {} - - -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. - -public enum DatabaseConfig { - - case memory(junk: String ) - case sled(config: SledDbConfiguration ) -} - -extension DatabaseConfig: ViaFfiUsingByteBuffer, ViaFfi { - fileprivate static func read(from buf: Reader) throws -> DatabaseConfig { - let variant: Int32 = try buf.readInt() - switch variant { - - case 1: return .memory( - junk: try String.read(from: buf) - ) - case 2: return .sled( - config: try SledDbConfiguration.read(from: buf) - ) - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - fileprivate func write(into buf: Writer) { - switch self { - - - case let .memory(junk): - buf.writeInt(Int32(1)) - junk.write(into: buf) - - - - case let .sled(config): - buf.writeInt(Int32(2)) - config.write(into: buf) - - - } - } -} - - -extension DatabaseConfig: Equatable, Hashable {} - - -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. - -public enum Transaction { - - case unconfirmed(details: TransactionDetails ) - case confirmed(details: TransactionDetails, confirmation: Confirmation ) -} - -extension Transaction: ViaFfiUsingByteBuffer, ViaFfi { - fileprivate static func read(from buf: Reader) throws -> Transaction { - let variant: Int32 = try buf.readInt() - switch variant { - - case 1: return .unconfirmed( - details: try TransactionDetails.read(from: buf) - ) - case 2: return .confirmed( - details: try TransactionDetails.read(from: buf), - confirmation: try Confirmation.read(from: buf) - ) - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - fileprivate func write(into buf: Writer) { - switch self { - - - case let .unconfirmed(details): - buf.writeInt(Int32(1)) - details.write(into: buf) - - - - case let .confirmed(details,confirmation): - buf.writeInt(Int32(2)) - details.write(into: buf) - confirmation.write(into: buf) - - - } - } -} - - -extension Transaction: Equatable, Hashable {} - - -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. - -public enum BlockchainConfig { - - case electrum(config: ElectrumConfig ) - case esplora(config: EsploraConfig ) -} - -extension BlockchainConfig: ViaFfiUsingByteBuffer, ViaFfi { - fileprivate static func read(from buf: Reader) throws -> BlockchainConfig { - let variant: Int32 = try buf.readInt() - switch variant { - - case 1: return .electrum( - config: try ElectrumConfig.read(from: buf) - ) - case 2: return .esplora( - config: try EsploraConfig.read(from: buf) - ) - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - fileprivate func write(into buf: Writer) { - switch self { - - - case let .electrum(config): - buf.writeInt(Int32(1)) - config.write(into: buf) - - - - case let .esplora(config): - buf.writeInt(Int32(2)) - config.write(into: buf) - - - } - } -} - - -extension BlockchainConfig: Equatable, Hashable {} - - -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. - -public enum MnemonicType { - - case words12 - case words15 - case words18 - case words21 - case words24 -} - -extension MnemonicType: ViaFfiUsingByteBuffer, ViaFfi { - fileprivate static func read(from buf: Reader) throws -> MnemonicType { - let variant: Int32 = try buf.readInt() - switch variant { - - case 1: return .words12 - case 2: return .words15 - case 3: return .words18 - case 4: return .words21 - case 5: return .words24 - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - fileprivate func write(into buf: Writer) { - switch self { - - - case .words12: - buf.writeInt(Int32(1)) - - - case .words15: - buf.writeInt(Int32(2)) - - - case .words18: - buf.writeInt(Int32(3)) - - - case .words21: - buf.writeInt(Int32(4)) - - - case .words24: - buf.writeInt(Int32(5)) - - } - } -} - - -extension MnemonicType: Equatable, Hashable {} - - - -public func generateExtendedKey(network: Network, mnemonicType: MnemonicType, password: String? ) throws -> ExtendedKeyInfo { - let _retval = try - - - rustCallWithError(BdkError.self) { - - bdk_2e4d_generate_extended_key(network.lower(), mnemonicType.lower(), FfiConverterOptionString.lower(password) , $0) -} - return try ExtendedKeyInfo.lift(_retval) -} - - - -public func restoreExtendedKey(network: Network, mnemonic: String, password: String? ) throws -> ExtendedKeyInfo { - let _retval = try - - - rustCallWithError(BdkError.self) { - - bdk_2e4d_restore_extended_key(network.lower(), mnemonic.lower(), FfiConverterOptionString.lower(password) , $0) -} - return try ExtendedKeyInfo.lift(_retval) -} - - - -public protocol OfflineWalletProtocol { - func getNewAddress() -> String - func getLastUnusedAddress() -> String - func getBalance() throws -> UInt64 - func sign(psbt: PartiallySignedBitcoinTransaction ) throws - func getTransactions() throws -> [Transaction] - -} - -public class OfflineWallet: OfflineWalletProtocol { - fileprivate let pointer: UnsafeMutableRawPointer - - // TODO: We'd like this to be `private` but for Swifty reasons, - // we can't implement `ViaFfi` without making this `required` and we can't - // make it `required` without making it `public`. - required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { - self.pointer = pointer - } - public convenience init(descriptor: String, network: Network, databaseConfig: DatabaseConfig ) throws { - self.init(unsafeFromRawPointer: try - - - rustCallWithError(BdkError.self) { - - bdk_2e4d_OfflineWallet_new(descriptor.lower(), network.lower(), databaseConfig.lower() , $0) -}) - } - - deinit { - try! rustCall { ffi_bdk_2e4d_OfflineWallet_object_free(pointer, $0) } - } - - - - - public func getNewAddress() -> String { - let _retval = try! - rustCall() { - - bdk_2e4d_OfflineWallet_get_new_address(self.pointer, $0 - ) -} - return try! String.lift(_retval) - } - public func getLastUnusedAddress() -> String { - let _retval = try! - rustCall() { - - bdk_2e4d_OfflineWallet_get_last_unused_address(self.pointer, $0 - ) -} - return try! String.lift(_retval) - } - public func getBalance() throws -> UInt64 { - let _retval = try - rustCallWithError(BdkError.self) { - - bdk_2e4d_OfflineWallet_get_balance(self.pointer, $0 - ) -} - return try UInt64.lift(_retval) - } - public func sign(psbt: PartiallySignedBitcoinTransaction ) throws { - try - rustCallWithError(BdkError.self) { - - bdk_2e4d_OfflineWallet_sign(self.pointer, psbt.lower() , $0 - ) -} - } - public func getTransactions() throws -> [Transaction] { - let _retval = try - rustCallWithError(BdkError.self) { - - bdk_2e4d_OfflineWallet_get_transactions(self.pointer, $0 - ) -} - return try FfiConverterSequenceEnumTransaction.lift(_retval) - } - -} - - -fileprivate extension OfflineWallet { - fileprivate typealias FfiType = UnsafeMutableRawPointer - - fileprivate static func read(from buf: Reader) throws -> Self { - let v: UInt64 = try buf.readInt() - // The Rust code won't compile if a pointer won't fit in a UInt64. - // We have to go via `UInt` because that's the thing that's the size of a pointer. - let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v)) - if (ptr == nil) { - throw UniffiInternalError.unexpectedNullPointer - } - return try self.lift(ptr!) - } - - fileprivate func write(into buf: Writer) { - // This fiddling is because `Int` is the thing that's the same size as a pointer. - // The Rust code won't compile if a pointer won't fit in a `UInt64`. - buf.writeInt(UInt64(bitPattern: Int64(Int(bitPattern: self.lower())))) - } - - fileprivate static func lift(_ pointer: UnsafeMutableRawPointer) throws -> Self { - return Self(unsafeFromRawPointer: pointer) - } - - fileprivate func lower() -> UnsafeMutableRawPointer { - return self.pointer - } -} - -// Ideally this would be `fileprivate`, but Swift says: -// """ -// 'private' modifier cannot be used with extensions that declare protocol conformances -// """ -extension OfflineWallet : ViaFfi, Serializable {} - - -public protocol OnlineWalletProtocol { - func getNewAddress() -> String - func getLastUnusedAddress() -> String - func getBalance() throws -> UInt64 - func sign(psbt: PartiallySignedBitcoinTransaction ) throws - func getTransactions() throws -> [Transaction] - func getNetwork() -> Network - func sync(progressUpdate: BdkProgress, maxAddressParam: UInt32? ) throws - func broadcast(psbt: PartiallySignedBitcoinTransaction ) throws -> Transaction - -} - -public class OnlineWallet: OnlineWalletProtocol { - fileprivate let pointer: UnsafeMutableRawPointer - - // TODO: We'd like this to be `private` but for Swifty reasons, - // we can't implement `ViaFfi` without making this `required` and we can't - // make it `required` without making it `public`. - required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { - self.pointer = pointer - } - public convenience init(descriptor: String, changeDescriptor: String?, network: Network, databaseConfig: DatabaseConfig, blockchainConfig: BlockchainConfig ) throws { - self.init(unsafeFromRawPointer: try - - - rustCallWithError(BdkError.self) { - - bdk_2e4d_OnlineWallet_new(descriptor.lower(), FfiConverterOptionString.lower(changeDescriptor), network.lower(), databaseConfig.lower(), blockchainConfig.lower() , $0) -}) - } - - deinit { - try! rustCall { ffi_bdk_2e4d_OnlineWallet_object_free(pointer, $0) } - } - - - - - public func getNewAddress() -> String { - let _retval = try! - rustCall() { - - bdk_2e4d_OnlineWallet_get_new_address(self.pointer, $0 - ) -} - return try! String.lift(_retval) - } - public func getLastUnusedAddress() -> String { - let _retval = try! - rustCall() { - - bdk_2e4d_OnlineWallet_get_last_unused_address(self.pointer, $0 - ) -} - return try! String.lift(_retval) - } - public func getBalance() throws -> UInt64 { - let _retval = try - rustCallWithError(BdkError.self) { - - bdk_2e4d_OnlineWallet_get_balance(self.pointer, $0 - ) -} - return try UInt64.lift(_retval) - } - public func sign(psbt: PartiallySignedBitcoinTransaction ) throws { - try - rustCallWithError(BdkError.self) { - - bdk_2e4d_OnlineWallet_sign(self.pointer, psbt.lower() , $0 - ) -} - } - public func getTransactions() throws -> [Transaction] { - let _retval = try - rustCallWithError(BdkError.self) { - - bdk_2e4d_OnlineWallet_get_transactions(self.pointer, $0 - ) -} - return try FfiConverterSequenceEnumTransaction.lift(_retval) - } - public func getNetwork() -> Network { - let _retval = try! - rustCall() { - - bdk_2e4d_OnlineWallet_get_network(self.pointer, $0 - ) -} - return try! Network.lift(_retval) - } - public func sync(progressUpdate: BdkProgress, maxAddressParam: UInt32? ) throws { - try - rustCallWithError(BdkError.self) { - - bdk_2e4d_OnlineWallet_sync(self.pointer, ffiConverterCallbackInterfaceBdkProgress.lower(progressUpdate), FfiConverterOptionUInt32.lower(maxAddressParam) , $0 - ) -} - } - public func broadcast(psbt: PartiallySignedBitcoinTransaction ) throws -> Transaction { - let _retval = try - rustCallWithError(BdkError.self) { - - bdk_2e4d_OnlineWallet_broadcast(self.pointer, psbt.lower() , $0 - ) -} - return try Transaction.lift(_retval) - } - -} - - -fileprivate extension OnlineWallet { - fileprivate typealias FfiType = UnsafeMutableRawPointer - - fileprivate static func read(from buf: Reader) throws -> Self { - let v: UInt64 = try buf.readInt() - // The Rust code won't compile if a pointer won't fit in a UInt64. - // We have to go via `UInt` because that's the thing that's the size of a pointer. - let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v)) - if (ptr == nil) { - throw UniffiInternalError.unexpectedNullPointer - } - return try self.lift(ptr!) - } - - fileprivate func write(into buf: Writer) { - // This fiddling is because `Int` is the thing that's the same size as a pointer. - // The Rust code won't compile if a pointer won't fit in a `UInt64`. - buf.writeInt(UInt64(bitPattern: Int64(Int(bitPattern: self.lower())))) - } - - fileprivate static func lift(_ pointer: UnsafeMutableRawPointer) throws -> Self { - return Self(unsafeFromRawPointer: pointer) - } - - fileprivate func lower() -> UnsafeMutableRawPointer { - return self.pointer - } -} - -// Ideally this would be `fileprivate`, but Swift says: -// """ -// 'private' modifier cannot be used with extensions that declare protocol conformances -// """ -extension OnlineWallet : ViaFfi, Serializable {} - - -public protocol PartiallySignedBitcoinTransactionProtocol { - -} - -public class PartiallySignedBitcoinTransaction: PartiallySignedBitcoinTransactionProtocol { - fileprivate let pointer: UnsafeMutableRawPointer - - // TODO: We'd like this to be `private` but for Swifty reasons, - // we can't implement `ViaFfi` without making this `required` and we can't - // make it `required` without making it `public`. - required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { - self.pointer = pointer - } - public convenience init(wallet: OnlineWallet, recipient: String, amount: UInt64, feeRate: Float? ) throws { - self.init(unsafeFromRawPointer: try - - - rustCallWithError(BdkError.self) { - - bdk_2e4d_PartiallySignedBitcoinTransaction_new(wallet.lower(), recipient.lower(), amount.lower(), FfiConverterOptionFloat.lower(feeRate) , $0) -}) - } - - deinit { - try! rustCall { ffi_bdk_2e4d_PartiallySignedBitcoinTransaction_object_free(pointer, $0) } - } - - - - - -} - - -fileprivate extension PartiallySignedBitcoinTransaction { - fileprivate typealias FfiType = UnsafeMutableRawPointer - - fileprivate static func read(from buf: Reader) throws -> Self { - let v: UInt64 = try buf.readInt() - // The Rust code won't compile if a pointer won't fit in a UInt64. - // We have to go via `UInt` because that's the thing that's the size of a pointer. - let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v)) - if (ptr == nil) { - throw UniffiInternalError.unexpectedNullPointer - } - return try self.lift(ptr!) - } - - fileprivate func write(into buf: Writer) { - // This fiddling is because `Int` is the thing that's the same size as a pointer. - // The Rust code won't compile if a pointer won't fit in a `UInt64`. - buf.writeInt(UInt64(bitPattern: Int64(Int(bitPattern: self.lower())))) - } - - fileprivate static func lift(_ pointer: UnsafeMutableRawPointer) throws -> Self { - return Self(unsafeFromRawPointer: pointer) - } - - fileprivate func lower() -> UnsafeMutableRawPointer { - return self.pointer - } -} - -// Ideally this would be `fileprivate`, but Swift says: -// """ -// 'private' modifier cannot be used with extensions that declare protocol conformances -// """ -extension PartiallySignedBitcoinTransaction : ViaFfi, Serializable {} - -public struct SledDbConfiguration { - public var path: String - public var treeName: String - - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init(path: String, treeName: String ) { - self.path = path - self.treeName = treeName - } -} - - -extension SledDbConfiguration: Equatable, Hashable { - public static func ==(lhs: SledDbConfiguration, rhs: SledDbConfiguration) -> Bool { - if lhs.path != rhs.path { - return false - } - if lhs.treeName != rhs.treeName { - return false - } - return true - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(path) - hasher.combine(treeName) - } -} - - -fileprivate extension SledDbConfiguration { - static func read(from buf: Reader) throws -> SledDbConfiguration { - return try SledDbConfiguration( - path: String.read(from: buf), - treeName: String.read(from: buf) - ) - } - - func write(into buf: Writer) { - self.path.write(into: buf) - self.treeName.write(into: buf) - } -} - -extension SledDbConfiguration: ViaFfiUsingByteBuffer, ViaFfi {} - -public struct TransactionDetails { - public var fees: UInt64? - public var received: UInt64 - public var sent: UInt64 - public var txid: String - - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init(fees: UInt64?, received: UInt64, sent: UInt64, txid: String ) { - self.fees = fees - self.received = received - self.sent = sent - self.txid = txid - } -} - - -extension TransactionDetails: Equatable, Hashable { - public static func ==(lhs: TransactionDetails, rhs: TransactionDetails) -> Bool { - if lhs.fees != rhs.fees { - return false - } - if lhs.received != rhs.received { - return false - } - if lhs.sent != rhs.sent { - return false - } - if lhs.txid != rhs.txid { - return false - } - return true - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(fees) - hasher.combine(received) - hasher.combine(sent) - hasher.combine(txid) - } -} - - -fileprivate extension TransactionDetails { - static func read(from buf: Reader) throws -> TransactionDetails { - return try TransactionDetails( - fees: FfiConverterOptionUInt64.read(from: buf), - received: UInt64.read(from: buf), - sent: UInt64.read(from: buf), - txid: String.read(from: buf) - ) - } - - func write(into buf: Writer) { - FfiConverterOptionUInt64.write(self.fees, into: buf) - self.received.write(into: buf) - self.sent.write(into: buf) - self.txid.write(into: buf) - } -} - -extension TransactionDetails: ViaFfiUsingByteBuffer, ViaFfi {} - -public struct Confirmation { - public var height: UInt32 - public var timestamp: UInt64 - - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init(height: UInt32, timestamp: UInt64 ) { - self.height = height - self.timestamp = timestamp - } -} - - -extension Confirmation: Equatable, Hashable { - public static func ==(lhs: Confirmation, rhs: Confirmation) -> Bool { - if lhs.height != rhs.height { - return false - } - if lhs.timestamp != rhs.timestamp { - return false - } - return true - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(height) - hasher.combine(timestamp) - } -} - - -fileprivate extension Confirmation { - static func read(from buf: Reader) throws -> Confirmation { - return try Confirmation( - height: UInt32.read(from: buf), - timestamp: UInt64.read(from: buf) - ) - } - - func write(into buf: Writer) { - self.height.write(into: buf) - self.timestamp.write(into: buf) - } -} - -extension Confirmation: ViaFfiUsingByteBuffer, ViaFfi {} - -public struct ElectrumConfig { - public var url: String - public var socks5: String? - public var retry: UInt8 - public var timeout: UInt8? - public var stopGap: UInt64 - - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init(url: String, socks5: String?, retry: UInt8, timeout: UInt8?, stopGap: UInt64 ) { - self.url = url - self.socks5 = socks5 - self.retry = retry - self.timeout = timeout - self.stopGap = stopGap - } -} - - -extension ElectrumConfig: Equatable, Hashable { - public static func ==(lhs: ElectrumConfig, rhs: ElectrumConfig) -> Bool { - if lhs.url != rhs.url { - return false - } - if lhs.socks5 != rhs.socks5 { - return false - } - if lhs.retry != rhs.retry { - return false - } - if lhs.timeout != rhs.timeout { - return false - } - if lhs.stopGap != rhs.stopGap { - return false - } - return true - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(url) - hasher.combine(socks5) - hasher.combine(retry) - hasher.combine(timeout) - hasher.combine(stopGap) - } -} - - -fileprivate extension ElectrumConfig { - static func read(from buf: Reader) throws -> ElectrumConfig { - return try ElectrumConfig( - url: String.read(from: buf), - socks5: FfiConverterOptionString.read(from: buf), - retry: UInt8.read(from: buf), - timeout: FfiConverterOptionUInt8.read(from: buf), - stopGap: UInt64.read(from: buf) - ) - } - - func write(into buf: Writer) { - self.url.write(into: buf) - FfiConverterOptionString.write(self.socks5, into: buf) - self.retry.write(into: buf) - FfiConverterOptionUInt8.write(self.timeout, into: buf) - self.stopGap.write(into: buf) - } -} - -extension ElectrumConfig: ViaFfiUsingByteBuffer, ViaFfi {} - -public struct EsploraConfig { - public var baseUrl: String - public var proxy: String? - public var timeoutRead: UInt64 - public var timeoutWrite: UInt64 - public var stopGap: UInt64 - - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init(baseUrl: String, proxy: String?, timeoutRead: UInt64, timeoutWrite: UInt64, stopGap: UInt64 ) { - self.baseUrl = baseUrl - self.proxy = proxy - self.timeoutRead = timeoutRead - self.timeoutWrite = timeoutWrite - self.stopGap = stopGap - } -} - - -extension EsploraConfig: Equatable, Hashable { - public static func ==(lhs: EsploraConfig, rhs: EsploraConfig) -> Bool { - if lhs.baseUrl != rhs.baseUrl { - return false - } - if lhs.proxy != rhs.proxy { - return false - } - if lhs.timeoutRead != rhs.timeoutRead { - return false - } - if lhs.timeoutWrite != rhs.timeoutWrite { - return false - } - if lhs.stopGap != rhs.stopGap { - return false - } - return true - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(baseUrl) - hasher.combine(proxy) - hasher.combine(timeoutRead) - hasher.combine(timeoutWrite) - hasher.combine(stopGap) - } -} - - -fileprivate extension EsploraConfig { - static func read(from buf: Reader) throws -> EsploraConfig { - return try EsploraConfig( - baseUrl: String.read(from: buf), - proxy: FfiConverterOptionString.read(from: buf), - timeoutRead: UInt64.read(from: buf), - timeoutWrite: UInt64.read(from: buf), - stopGap: UInt64.read(from: buf) - ) - } - - func write(into buf: Writer) { - self.baseUrl.write(into: buf) - FfiConverterOptionString.write(self.proxy, into: buf) - self.timeoutRead.write(into: buf) - self.timeoutWrite.write(into: buf) - self.stopGap.write(into: buf) - } -} - -extension EsploraConfig: ViaFfiUsingByteBuffer, ViaFfi {} - -public struct ExtendedKeyInfo { - public var mnemonic: String - public var xprv: String - public var fingerprint: String - - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init(mnemonic: String, xprv: String, fingerprint: String ) { - self.mnemonic = mnemonic - self.xprv = xprv - self.fingerprint = fingerprint - } -} - - -extension ExtendedKeyInfo: Equatable, Hashable { - public static func ==(lhs: ExtendedKeyInfo, rhs: ExtendedKeyInfo) -> Bool { - if lhs.mnemonic != rhs.mnemonic { - return false - } - if lhs.xprv != rhs.xprv { - return false - } - if lhs.fingerprint != rhs.fingerprint { - return false - } - return true - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(mnemonic) - hasher.combine(xprv) - hasher.combine(fingerprint) - } -} - - -fileprivate extension ExtendedKeyInfo { - static func read(from buf: Reader) throws -> ExtendedKeyInfo { - return try ExtendedKeyInfo( - mnemonic: String.read(from: buf), - xprv: String.read(from: buf), - fingerprint: String.read(from: buf) - ) - } - - func write(into buf: Writer) { - self.mnemonic.write(into: buf) - self.xprv.write(into: buf) - self.fingerprint.write(into: buf) - } -} - -extension ExtendedKeyInfo: ViaFfiUsingByteBuffer, ViaFfi {} - -public enum BdkError { - - - - // Simple error enums only carry a message - case InvalidU32Bytes(message: String) - - // Simple error enums only carry a message - case Generic(message: String) - - // Simple error enums only carry a message - case ScriptDoesntHaveAddressForm(message: String) - - // Simple error enums only carry a message - case NoRecipients(message: String) - - // Simple error enums only carry a message - case NoUtxosSelected(message: String) - - // Simple error enums only carry a message - case OutputBelowDustLimit(message: String) - - // Simple error enums only carry a message - case InsufficientFunds(message: String) - - // Simple error enums only carry a message - case BnBTotalTriesExceeded(message: String) - - // Simple error enums only carry a message - case BnBNoExactMatch(message: String) - - // Simple error enums only carry a message - case UnknownUtxo(message: String) - - // Simple error enums only carry a message - case TransactionNotFound(message: String) - - // Simple error enums only carry a message - case TransactionConfirmed(message: String) - - // Simple error enums only carry a message - case IrreplaceableTransaction(message: String) - - // Simple error enums only carry a message - case FeeRateTooLow(message: String) - - // Simple error enums only carry a message - case FeeTooLow(message: String) - - // Simple error enums only carry a message - case FeeRateUnavailable(message: String) - - // Simple error enums only carry a message - case MissingKeyOrigin(message: String) - - // Simple error enums only carry a message - case Key(message: String) - - // Simple error enums only carry a message - case ChecksumMismatch(message: String) - - // Simple error enums only carry a message - case SpendingPolicyRequired(message: String) - - // Simple error enums only carry a message - case InvalidPolicyPathError(message: String) - - // Simple error enums only carry a message - case Signer(message: String) - - // Simple error enums only carry a message - case InvalidNetwork(message: String) - - // Simple error enums only carry a message - case InvalidProgressValue(message: String) - - // Simple error enums only carry a message - case ProgressUpdateError(message: String) - - // Simple error enums only carry a message - case InvalidOutpoint(message: String) - - // Simple error enums only carry a message - case Descriptor(message: String) - - // Simple error enums only carry a message - case AddressValidator(message: String) - - // Simple error enums only carry a message - case Encode(message: String) - - // Simple error enums only carry a message - case Miniscript(message: String) - - // Simple error enums only carry a message - case Bip32(message: String) - - // Simple error enums only carry a message - case Secp256k1(message: String) - - // Simple error enums only carry a message - case Json(message: String) - - // Simple error enums only carry a message - case Hex(message: String) - - // Simple error enums only carry a message - case Psbt(message: String) - - // Simple error enums only carry a message - case PsbtParse(message: String) - - // Simple error enums only carry a message - case Electrum(message: String) - - // Simple error enums only carry a message - case Esplora(message: String) - - // Simple error enums only carry a message - case Sled(message: String) - -} - -extension BdkError: ViaFfiUsingByteBuffer, ViaFfi { - fileprivate static func read(from buf: Reader) throws -> BdkError { - let variant: Int32 = try buf.readInt() - switch variant { - - - - - case 1: return .InvalidU32Bytes( - message: try String.read(from: buf) - ) - - case 2: return .Generic( - message: try String.read(from: buf) - ) - - case 3: return .ScriptDoesntHaveAddressForm( - message: try String.read(from: buf) - ) - - case 4: return .NoRecipients( - message: try String.read(from: buf) - ) - - case 5: return .NoUtxosSelected( - message: try String.read(from: buf) - ) - - case 6: return .OutputBelowDustLimit( - message: try String.read(from: buf) - ) - - case 7: return .InsufficientFunds( - message: try String.read(from: buf) - ) - - case 8: return .BnBTotalTriesExceeded( - message: try String.read(from: buf) - ) - - case 9: return .BnBNoExactMatch( - message: try String.read(from: buf) - ) - - case 10: return .UnknownUtxo( - message: try String.read(from: buf) - ) - - case 11: return .TransactionNotFound( - message: try String.read(from: buf) - ) - - case 12: return .TransactionConfirmed( - message: try String.read(from: buf) - ) - - case 13: return .IrreplaceableTransaction( - message: try String.read(from: buf) - ) - - case 14: return .FeeRateTooLow( - message: try String.read(from: buf) - ) - - case 15: return .FeeTooLow( - message: try String.read(from: buf) - ) - - case 16: return .FeeRateUnavailable( - message: try String.read(from: buf) - ) - - case 17: return .MissingKeyOrigin( - message: try String.read(from: buf) - ) - - case 18: return .Key( - message: try String.read(from: buf) - ) - - case 19: return .ChecksumMismatch( - message: try String.read(from: buf) - ) - - case 20: return .SpendingPolicyRequired( - message: try String.read(from: buf) - ) - - case 21: return .InvalidPolicyPathError( - message: try String.read(from: buf) - ) - - case 22: return .Signer( - message: try String.read(from: buf) - ) - - case 23: return .InvalidNetwork( - message: try String.read(from: buf) - ) - - case 24: return .InvalidProgressValue( - message: try String.read(from: buf) - ) - - case 25: return .ProgressUpdateError( - message: try String.read(from: buf) - ) - - case 26: return .InvalidOutpoint( - message: try String.read(from: buf) - ) - - case 27: return .Descriptor( - message: try String.read(from: buf) - ) - - case 28: return .AddressValidator( - message: try String.read(from: buf) - ) - - case 29: return .Encode( - message: try String.read(from: buf) - ) - - case 30: return .Miniscript( - message: try String.read(from: buf) - ) - - case 31: return .Bip32( - message: try String.read(from: buf) - ) - - case 32: return .Secp256k1( - message: try String.read(from: buf) - ) - - case 33: return .Json( - message: try String.read(from: buf) - ) - - case 34: return .Hex( - message: try String.read(from: buf) - ) - - case 35: return .Psbt( - message: try String.read(from: buf) - ) - - case 36: return .PsbtParse( - message: try String.read(from: buf) - ) - - case 37: return .Electrum( - message: try String.read(from: buf) - ) - - case 38: return .Esplora( - message: try String.read(from: buf) - ) - - case 39: return .Sled( - message: try String.read(from: buf) - ) - - - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - fileprivate func write(into buf: Writer) { - switch self { - - - - - case let .InvalidU32Bytes(message): - buf.writeInt(Int32(1)) - message.write(into: buf) - case let .Generic(message): - buf.writeInt(Int32(2)) - message.write(into: buf) - case let .ScriptDoesntHaveAddressForm(message): - buf.writeInt(Int32(3)) - message.write(into: buf) - case let .NoRecipients(message): - buf.writeInt(Int32(4)) - message.write(into: buf) - case let .NoUtxosSelected(message): - buf.writeInt(Int32(5)) - message.write(into: buf) - case let .OutputBelowDustLimit(message): - buf.writeInt(Int32(6)) - message.write(into: buf) - case let .InsufficientFunds(message): - buf.writeInt(Int32(7)) - message.write(into: buf) - case let .BnBTotalTriesExceeded(message): - buf.writeInt(Int32(8)) - message.write(into: buf) - case let .BnBNoExactMatch(message): - buf.writeInt(Int32(9)) - message.write(into: buf) - case let .UnknownUtxo(message): - buf.writeInt(Int32(10)) - message.write(into: buf) - case let .TransactionNotFound(message): - buf.writeInt(Int32(11)) - message.write(into: buf) - case let .TransactionConfirmed(message): - buf.writeInt(Int32(12)) - message.write(into: buf) - case let .IrreplaceableTransaction(message): - buf.writeInt(Int32(13)) - message.write(into: buf) - case let .FeeRateTooLow(message): - buf.writeInt(Int32(14)) - message.write(into: buf) - case let .FeeTooLow(message): - buf.writeInt(Int32(15)) - message.write(into: buf) - case let .FeeRateUnavailable(message): - buf.writeInt(Int32(16)) - message.write(into: buf) - case let .MissingKeyOrigin(message): - buf.writeInt(Int32(17)) - message.write(into: buf) - case let .Key(message): - buf.writeInt(Int32(18)) - message.write(into: buf) - case let .ChecksumMismatch(message): - buf.writeInt(Int32(19)) - message.write(into: buf) - case let .SpendingPolicyRequired(message): - buf.writeInt(Int32(20)) - message.write(into: buf) - case let .InvalidPolicyPathError(message): - buf.writeInt(Int32(21)) - message.write(into: buf) - case let .Signer(message): - buf.writeInt(Int32(22)) - message.write(into: buf) - case let .InvalidNetwork(message): - buf.writeInt(Int32(23)) - message.write(into: buf) - case let .InvalidProgressValue(message): - buf.writeInt(Int32(24)) - message.write(into: buf) - case let .ProgressUpdateError(message): - buf.writeInt(Int32(25)) - message.write(into: buf) - case let .InvalidOutpoint(message): - buf.writeInt(Int32(26)) - message.write(into: buf) - case let .Descriptor(message): - buf.writeInt(Int32(27)) - message.write(into: buf) - case let .AddressValidator(message): - buf.writeInt(Int32(28)) - message.write(into: buf) - case let .Encode(message): - buf.writeInt(Int32(29)) - message.write(into: buf) - case let .Miniscript(message): - buf.writeInt(Int32(30)) - message.write(into: buf) - case let .Bip32(message): - buf.writeInt(Int32(31)) - message.write(into: buf) - case let .Secp256k1(message): - buf.writeInt(Int32(32)) - message.write(into: buf) - case let .Json(message): - buf.writeInt(Int32(33)) - message.write(into: buf) - case let .Hex(message): - buf.writeInt(Int32(34)) - message.write(into: buf) - case let .Psbt(message): - buf.writeInt(Int32(35)) - message.write(into: buf) - case let .PsbtParse(message): - buf.writeInt(Int32(36)) - message.write(into: buf) - case let .Electrum(message): - buf.writeInt(Int32(37)) - message.write(into: buf) - case let .Esplora(message): - buf.writeInt(Int32(38)) - message.write(into: buf) - case let .Sled(message): - buf.writeInt(Int32(39)) - message.write(into: buf) - } - } -} - - -extension BdkError: Equatable, Hashable {} - -extension BdkError: Error { } - - -// Declaration and FfiConverters for BdkProgress Callback Interface - -public protocol BdkProgress : AnyObject { - func update(progress: Float, message: String? ) - -} - -// The ForeignCallback that is passed to Rust. -fileprivate let foreignCallbackCallbackInterfaceBdkProgress : ForeignCallback = - { (handle: Handle, method: Int32, args: RustBuffer) -> RustBuffer in - func invokeUpdate(_ swiftCallbackInterface: BdkProgress, _ args: RustBuffer) throws -> RustBuffer { - defer { args.deallocate() } - - let reader = Reader(data: Data(rustBuffer: args)) - swiftCallbackInterface.update( - progress: try Float.read(from: reader), - message: try FfiConverterOptionString.read(from: reader) - ) - return RustBuffer() - // TODO catch errors and report them back to Rust. - // https://github.com/mozilla/uniffi-rs/issues/351 - - } - - - let cb = try! ffiConverterCallbackInterfaceBdkProgress.lift(handle) - switch method { - case IDX_CALLBACK_FREE: - ffiConverterCallbackInterfaceBdkProgress.drop(handle: handle) - return RustBuffer() - case 1: return try! invokeUpdate(cb, args) - - // This should never happen, because an out of bounds method index won't - // ever be used. Once we can catch errors, we should return an InternalError. - // https://github.com/mozilla/uniffi-rs/issues/351 - default: return RustBuffer() - } - } - -// The ffiConverter which transforms the Callbacks in to Handles to pass to Rust. -private let ffiConverterCallbackInterfaceBdkProgress: FfiConverterCallbackInterface = { - try! rustCall { (err: UnsafeMutablePointer) in - ffi_bdk_2e4d_BdkProgress_init_callback(foreignCallbackCallbackInterfaceBdkProgress, err) - } - return FfiConverterCallbackInterface() -}() -extension UInt8: Primitive, ViaFfi { - fileprivate static func read(from buf: Reader) throws -> Self { - return try self.lift(buf.readInt()) - } - - fileprivate func write(into buf: Writer) { - buf.writeInt(self.lower()) - } -} -extension UInt32: Primitive, ViaFfi { - fileprivate static func read(from buf: Reader) throws -> Self { - return try self.lift(buf.readInt()) - } - - fileprivate func write(into buf: Writer) { - buf.writeInt(self.lower()) - } -} -extension UInt64: Primitive, ViaFfi { - fileprivate static func read(from buf: Reader) throws -> Self { - return try self.lift(buf.readInt()) - } - - fileprivate func write(into buf: Writer) { - buf.writeInt(self.lower()) - } -} -extension Float: Primitive, ViaFfi { - fileprivate static func read(from buf: Reader) throws -> Self { - return try self.lift(buf.readFloat()) - } - - fileprivate func write(into buf: Writer) { - buf.writeFloat(self.lower()) - } -} -extension String: ViaFfi { - fileprivate typealias FfiType = RustBuffer - - fileprivate static func lift(_ v: FfiType) throws -> Self { - defer { - v.deallocate() - } - if v.data == nil { - return String() - } - let bytes = UnsafeBufferPointer(start: v.data!, count: Int(v.len)) - return String(bytes: bytes, encoding: String.Encoding.utf8)! - } - - fileprivate func lower() -> FfiType { - return self.utf8CString.withUnsafeBufferPointer { ptr in - // The swift string gives us int8_t, we want uint8_t. - ptr.withMemoryRebound(to: UInt8.self) { ptr in - // The swift string gives us a trailing null byte, we don't want it. - let buf = UnsafeBufferPointer(rebasing: ptr.prefix(upTo: ptr.count - 1)) - return RustBuffer.from(buf) - } - } - } - - fileprivate static func read(from buf: Reader) throws -> Self { - let len: Int32 = try buf.readInt() - return String(bytes: try buf.readBytes(count: Int(len)), encoding: String.Encoding.utf8)! - } - - fileprivate func write(into buf: Writer) { - let len = Int32(self.utf8.count) - buf.writeInt(len) - buf.writeBytes(self.utf8) - } -} -// Helper code for OfflineWallet class is found in ObjectTemplate.swift -// Helper code for OnlineWallet class is found in ObjectTemplate.swift -// Helper code for PartiallySignedBitcoinTransaction class is found in ObjectTemplate.swift -// Helper code for Confirmation record is found in RecordTemplate.swift -// Helper code for ElectrumConfig record is found in RecordTemplate.swift -// Helper code for EsploraConfig record is found in RecordTemplate.swift -// Helper code for ExtendedKeyInfo record is found in RecordTemplate.swift -// Helper code for SledDbConfiguration record is found in RecordTemplate.swift -// Helper code for TransactionDetails record is found in RecordTemplate.swift -// Helper code for BlockchainConfig enum is found in EnumTemplate.swift -// Helper code for DatabaseConfig enum is found in EnumTemplate.swift -// Helper code for MnemonicType enum is found in EnumTemplate.swift -// Helper code for Network enum is found in EnumTemplate.swift -// Helper code for Transaction enum is found in EnumTemplate.swift -// Helper code for BdkError error is found in ErrorTemplate.swift - -fileprivate enum FfiConverterOptionUInt8: FfiConverterUsingByteBuffer { - typealias SwiftType = UInt8? - - static func write(_ value: SwiftType, into buf: Writer) { - FfiConverterOptional.write(value, into: buf) { item, buf in - item.write(into: buf) - } - } - - static func read(from buf: Reader) throws -> SwiftType { - try FfiConverterOptional.read(from: buf) { buf in - try UInt8.read(from: buf) - } - } -} - -fileprivate enum FfiConverterOptionUInt32: FfiConverterUsingByteBuffer { - typealias SwiftType = UInt32? - - static func write(_ value: SwiftType, into buf: Writer) { - FfiConverterOptional.write(value, into: buf) { item, buf in - item.write(into: buf) - } - } - - static func read(from buf: Reader) throws -> SwiftType { - try FfiConverterOptional.read(from: buf) { buf in - try UInt32.read(from: buf) - } - } -} - -fileprivate enum FfiConverterOptionUInt64: FfiConverterUsingByteBuffer { - typealias SwiftType = UInt64? - - static func write(_ value: SwiftType, into buf: Writer) { - FfiConverterOptional.write(value, into: buf) { item, buf in - item.write(into: buf) - } - } - - static func read(from buf: Reader) throws -> SwiftType { - try FfiConverterOptional.read(from: buf) { buf in - try UInt64.read(from: buf) - } - } -} - -fileprivate enum FfiConverterOptionFloat: FfiConverterUsingByteBuffer { - typealias SwiftType = Float? - - static func write(_ value: SwiftType, into buf: Writer) { - FfiConverterOptional.write(value, into: buf) { item, buf in - item.write(into: buf) - } - } - - static func read(from buf: Reader) throws -> SwiftType { - try FfiConverterOptional.read(from: buf) { buf in - try Float.read(from: buf) - } - } -} - -fileprivate enum FfiConverterOptionString: FfiConverterUsingByteBuffer { - typealias SwiftType = String? - - static func write(_ value: SwiftType, into buf: Writer) { - FfiConverterOptional.write(value, into: buf) { item, buf in - item.write(into: buf) - } - } - - static func read(from buf: Reader) throws -> SwiftType { - try FfiConverterOptional.read(from: buf) { buf in - try String.read(from: buf) - } - } -} - -fileprivate enum FfiConverterSequenceEnumTransaction: FfiConverterUsingByteBuffer { - typealias SwiftType = [Transaction] - - static func write(_ value: SwiftType, into buf: Writer) { - FfiConverterSequence.write(value, into: buf) { (item, buf) in - item.write(into: buf) - } - } - - static func read(from buf: Reader) throws -> SwiftType { - try FfiConverterSequence.read(from: buf) { buf in - try Transaction.read(from: buf) - } - } -} - - -/** - * Top level initializers and tear down methods. - * - * This is generated by uniffi. - */ -public enum BdkLifecycle { - /** - * Initialize the FFI and Rust library. This should be only called once per application. - */ - func initialize() { - - // No initialization code needed - - } -} \ No newline at end of file diff --git a/bdkFFI.xcframework/Info.plist b/bdkFFI.xcframework/Info.plist new file mode 100644 index 0000000..3b0157a --- /dev/null +++ b/bdkFFI.xcframework/Info.plist @@ -0,0 +1,52 @@ + + + + + AvailableLibraries + + + LibraryIdentifier + macos-x86_64 + LibraryPath + bdkFFI.framework + SupportedArchitectures + + x86_64 + + SupportedPlatform + macos + + + LibraryIdentifier + ios-arm64 + LibraryPath + bdkFFI.framework + SupportedArchitectures + + arm64 + + SupportedPlatform + ios + + + LibraryIdentifier + ios-arm64_x86_64-simulator + LibraryPath + bdkFFI.framework + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + ios + SupportedPlatformVariant + simulator + + + CFBundlePackageType + XFWK + XCFrameworkFormatVersion + 1.0 + + diff --git a/bdkFFI.xcframework/ios-arm64/bdkFFI.framework/Headers/bdkFFI-umbrella.h b/bdkFFI.xcframework/ios-arm64/bdkFFI.framework/Headers/bdkFFI-umbrella.h new file mode 100644 index 0000000..c10f2d9 --- /dev/null +++ b/bdkFFI.xcframework/ios-arm64/bdkFFI.framework/Headers/bdkFFI-umbrella.h @@ -0,0 +1,4 @@ +// This is the "umbrella header" for our combined Rust code library. +// It needs to import all of the individual headers. + +#import "bdkFFI.h" diff --git a/bdkFFI.xcframework/ios-arm64/bdkFFI.framework/Modules/module.modulemap b/bdkFFI.xcframework/ios-arm64/bdkFFI.framework/Modules/module.modulemap new file mode 100644 index 0000000..1b7f601 --- /dev/null +++ b/bdkFFI.xcframework/ios-arm64/bdkFFI.framework/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module bdkFFI { + umbrella header "bdkFFI-umbrella.h" + + export * + module * { export * } +} diff --git a/bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/Headers/bdkFFI-umbrella.h b/bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/Headers/bdkFFI-umbrella.h new file mode 100644 index 0000000..c10f2d9 --- /dev/null +++ b/bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/Headers/bdkFFI-umbrella.h @@ -0,0 +1,4 @@ +// This is the "umbrella header" for our combined Rust code library. +// It needs to import all of the individual headers. + +#import "bdkFFI.h" diff --git a/bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/Modules/module.modulemap b/bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/Modules/module.modulemap new file mode 100644 index 0000000..1b7f601 --- /dev/null +++ b/bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module bdkFFI { + umbrella header "bdkFFI-umbrella.h" + + export * + module * { export * } +} diff --git a/bdkFFI.xcframework/macos-x86_64/bdkFFI.framework/Headers/bdkFFI-umbrella.h b/bdkFFI.xcframework/macos-x86_64/bdkFFI.framework/Headers/bdkFFI-umbrella.h new file mode 100644 index 0000000..c10f2d9 --- /dev/null +++ b/bdkFFI.xcframework/macos-x86_64/bdkFFI.framework/Headers/bdkFFI-umbrella.h @@ -0,0 +1,4 @@ +// This is the "umbrella header" for our combined Rust code library. +// It needs to import all of the individual headers. + +#import "bdkFFI.h" diff --git a/bdkFFI.xcframework/macos-x86_64/bdkFFI.framework/Modules/module.modulemap b/bdkFFI.xcframework/macos-x86_64/bdkFFI.framework/Modules/module.modulemap new file mode 100644 index 0000000..1b7f601 --- /dev/null +++ b/bdkFFI.xcframework/macos-x86_64/bdkFFI.framework/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module bdkFFI { + umbrella header "bdkFFI-umbrella.h" + + export * + module * { export * } +} diff --git a/build.sh b/build.sh index 2e12ca5..c278b29 100755 --- a/build.sh +++ b/build.sh @@ -1,75 +1,59 @@ #!/usr/bin/env bash set -euo pipefail -BUILD_PROFILE=release -BDKFFI_DIR=bdk-ffi -TARGET_DIR=$BDKFFI_DIR/target -STATIC_LIB_NAME=libbdkffi.a -SWIFT_DIR="$BDKFFI_DIR/bindings/bdk-swift" -XCFRAMEWORK_NAME="bdkFFI" -XCFRAMEWORK_ROOT="$XCFRAMEWORK_NAME.xcframework" -XCFRAMEWORK_COMMON="$XCFRAMEWORK_ROOT/common/$XCFRAMEWORK_NAME.framework" - -## build bdk-ffi rust libs -echo "Build bdk-ffi rust library" -pushd $BDKFFI_DIR +## confirm bdk-ffi rust lib builds +pushd bdk-ffi +echo "Confirm bdk-ffi rust lib builds" cargo build --release -echo "Generate bdk-ffi swift bindings" -uniffi-bindgen generate src/bdk.udl --no-format --out-dir bindings/bdk-swift/ --language swift -swiftc -module-name bdk -emit-library -o libbdkffi.dylib -emit-module -emit-module-path bindings/bdk-swift/ -parse-as-library -L target/release/ -lbdkffi -Xcc -fmodule-map-file=bindings/bdk-swift/$XCFRAMEWORK_NAME.modulemap bindings/bdk-swift/bdk.swift -suppress-warnings - -## build bdk-ffi rust libs into xcframework -echo "Build bdk-ffi libs into swift xcframework" +## build bdk-ffi rust libs for apple targets +echo "Build bdk-ffi libs for apple targets" TARGET_TRIPLES=("x86_64-apple-darwin" "x86_64-apple-ios" "aarch64-apple-ios") for TARGET in ${TARGET_TRIPLES[@]}; do echo "Build bdk-ffi lib for target $TARGET" cargo build --release --target $TARGET - echo $? done +echo "Generate bdk-ffi swift bindings" +uniffi-bindgen generate src/bdk.udl --no-format --out-dir ../Sources/BitcoinDevKit --language swift popd -## Manually construct xcframework +# rename bdk.swift bindings to BitcoinDevKit.swift +mv Sources/BitcoinDevKit/bdk.swift Sources/BitcoinDevKit/BitcoinDevKit.swift -# Cleanup prior build -rm -rf "$XCFRAMEWORK_ROOT" -rm -f $XCFRAMEWORK_ROOT.zip +# copy bdkFFI.h to bdkFFI.xcframework platforms +PLATFORMS=("macos-x86_64" "ios-arm64_x86_64-simulator" "ios-arm64") +for PLATFORM in ${PLATFORMS[@]}; do + cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/$PLATFORM/bdkFFI.framework/Headers +done -# Common files -mkdir -p "$XCFRAMEWORK_COMMON/Modules" -cp "$SWIFT_DIR/module.modulemap" "$XCFRAMEWORK_COMMON/Modules/" -mkdir -p "$XCFRAMEWORK_COMMON/Headers" -cp "$SWIFT_DIR/$XCFRAMEWORK_NAME-umbrella.h" "$XCFRAMEWORK_COMMON/Headers" -cp "$SWIFT_DIR/$XCFRAMEWORK_NAME.h" "$XCFRAMEWORK_COMMON/Headers" +# remove unneed .h and .modulemap files +rm Sources/BitcoinDevKit/bdkFFI.h +rm Sources/BitcoinDevkit/bdkFFI.modulemap -# macOS x86_64 hardware -mkdir -p "$XCFRAMEWORK_ROOT/macos-x86_64" -cp -R "$XCFRAMEWORK_COMMON" "$XCFRAMEWORK_ROOT/macos-x86_64/$XCFRAMEWORK_NAME.framework" -cp "$TARGET_DIR/x86_64-apple-darwin/$BUILD_PROFILE/$STATIC_LIB_NAME" "$XCFRAMEWORK_ROOT/macos-x86_64/$XCFRAMEWORK_NAME.framework/$XCFRAMEWORK_NAME" +# add bdkFFI libs to bdkFFI.xcframework -# iOS hardware -mkdir -p "$XCFRAMEWORK_ROOT/ios-arm64" -cp -R "$XCFRAMEWORK_COMMON" "$XCFRAMEWORK_ROOT/ios-arm64/$XCFRAMEWORK_NAME.framework" -cp "$TARGET_DIR/aarch64-apple-ios/$BUILD_PROFILE/$STATIC_LIB_NAME" "$XCFRAMEWORK_ROOT/ios-arm64/$XCFRAMEWORK_NAME.framework/$XCFRAMEWORK_NAME" +# macos-x86_64 platform +cp bdk-ffi/target/x86_64-apple-darwin/release/libbdkffi.a bdkFFI.xcframework/macos-x86_64/bdkFFI.framework/bdkFFI -# iOS simulator, currently x86_64 only (need to make fat binary to add M1) -mkdir -p "$XCFRAMEWORK_ROOT/ios-arm64_x86_64-simulator" -cp -R "$XCFRAMEWORK_COMMON" "$XCFRAMEWORK_ROOT/ios-arm64_x86_64-simulator/$XCFRAMEWORK_NAME.framework" -cp "$TARGET_DIR/x86_64-apple-ios/$BUILD_PROFILE/$STATIC_LIB_NAME" "$XCFRAMEWORK_ROOT/ios-arm64_x86_64-simulator/$XCFRAMEWORK_NAME.framework/$XCFRAMEWORK_NAME" +# ios-arm64 platform +cp bdk-ffi/target/aarch64-apple-ios/release/libbdkffi.a bdkFFI.xcframework/ios-arm64/bdkFFI.framework/bdkFFI + +# ios-arm64_x86_64-simulator, currently x86_64 only (need to make fat binary to add M1) +cp bdk-ffi/target/x86_64-apple-ios/release/libbdkffi.a bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/bdkFFI -# Set up the metadata for the XCFramework as a whole. -cp "$SWIFT_DIR/Info.plist" "$XCFRAMEWORK_ROOT/Info.plist" # TODO add license info -# Remove common -rm -rf "$XCFRAMEWORK_ROOT/common" +# remove any existing bdkFFI.xcframework.zip +rm bdkFFI.xcframework.zip -# Zip it all up into a bundle for distribution. -zip -9 -r "$XCFRAMEWORK_ROOT.zip" "$XCFRAMEWORK_ROOT" +# zip bdkFFI.xcframework directory into a bundle for distribution +zip -9 -r bdkFFI.xcframework.zip bdkFFI.xcframework +# compute bdkFFI.xcframework.zip checksum +echo checksum: swift package compute-checksum bdkFFI.xcframework.zip -# Cleanup build ? -# rm -rf "$XCFRAMEWORK_ROOT" +# TODO update Package.swift with checksum +# TODO upload zip to github release