diff --git a/AGENTS.md b/AGENTS.md index 8af49d29a..47db70692 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,20 +21,56 @@ cargo build --profile=release-smaller ``` ### Testing + +#### Unit tests (no external infrastructure needed) +```bash +cargo test --lib +``` + +#### Standard integration tests +Uses the `electrsd` crate to auto-download and spawn `bitcoind` + `electrs` binaries. +To use pre-downloaded binaries, set `BITCOIND_EXE` and `ELECTRS_EXE` env vars. + +On macOS, the default file descriptor limit (256) is too low for spawning `bitcoind` + +`electrs`. Raise it before running integration tests: + +Test files: `tests/integration_tests_rust.rs`, `tests/reorg_test.rs`, +`tests/multi_address_types_tests.rs` + ```bash -# Run all tests -cargo test +ulimit -n 10240 && cargo test +``` -# Run a specific test +#### Run a specific test +```bash cargo test test_name +``` -# Run tests with specific features +#### Run tests with UniFFI bindings feature +```bash cargo test --features "uniffi" +``` + +#### CLN integration tests +Requires Docker and `socat` for RPC socket forwarding. +```bash +docker compose -f docker-compose-cln.yml up -d +RUSTFLAGS="--cfg cln_test" cargo test --test integration_tests_cln +``` -# Integration tests with specific backends -cargo test --cfg cln_test # Core Lightning tests -cargo test --cfg lnd_test # LND tests -cargo test --cfg vss_test # VSS (Versioned Storage Service) tests +#### LND integration tests +Requires Docker and CMake (>=3.5, <4.0) for `lnd_grpc_rust`. +Set `LND_DATA_DIR`, `LND_CERT_PATH`, and `LND_MACAROON_PATH` env vars. +```bash +docker compose -f docker-compose-lnd.yml up -d +RUSTFLAGS="--cfg lnd_test" cargo test --test integration_tests_lnd +``` + +#### VSS integration tests +Requires PostgreSQL and a running VSS server (from `lightningdevkit/vss-server`). +Set `TEST_VSS_BASE_URL` env var. +```bash +RUSTFLAGS="--cfg vss_test" cargo test --test integration_tests_vss ``` ### Code Quality @@ -200,6 +236,7 @@ marked as skipped for CI), you must fix it before declaring success. - NEVER suggest manually adding @Serializable annotations to generated Kotlin bindings - ALWAYS run `cargo fmt` before committing to ensure consistent code formatting - ALWAYS move imports to the top of the file when applicable (no inline imports in functions) +- ALWAYS add unit tests for new business logic — untested code will be flagged in review - Run `./bindgen.sh` in the background when bindings need regeneration (it is long-running) ## Bindings Generation Command diff --git a/CHANGELOG.md b/CHANGELOG.md index 953ab1afb..db5f6b8ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,10 @@ - Switched from forked rust-lightning (`ovitrif/rust-lightning#0.2-electrum-fix`) back to official upstream crates.io releases. The Electrum sync fix (PR #4341) is now in upstream `lightning-transaction-sync` v0.2.1. Also picks up `lightning` v0.2.2 fixes. +- Fixed FS→KV channel data migration blindly overwriting newer state. The migration now skips + writing a channel monitor when the KV store already holds one with a newer or equal `update_id`, + and skips the channel manager when one already exists. Read or deserialization failures fail-closed + to prevent silent data loss. ## Synonym Fork Additions @@ -36,7 +40,6 @@ 255 and a warning is logged. **Breaking change:** existing struct-literal construction of `ElectrumSyncConfig` must add the new field or switch to `ElectrumSyncConfig { .., ..Default::default() }`. - - Added `OnchainPayment::calculate_send_all_fee()` to preview the fee for a drain / send-all transaction before broadcasting (fee-calculation counterpart of `send_all_to_address`) - Added runtime APIs for dynamic address type management: diff --git a/Package.swift b/Package.swift index 73c1fef8e..df80b2e2f 100644 --- a/Package.swift +++ b/Package.swift @@ -4,7 +4,7 @@ import PackageDescription let tag = "v0.7.0-rc.33" -let checksum = "82e318ac605e04d87f1aa24c920c06627346d9500c6395db45cb23f159e200f0" +let checksum = "a6bc32bf63117e80141f9e4cc529d33e16e141460b269125f4150e1251a1108a" let url = "https://github.com/synonymdev/ldk-node/releases/download/\(tag)/LDKNodeFFI.xcframework.zip" let package = Package( diff --git a/bindings/kotlin/ldk-node-android/lib/src/main/jniLibs/arm64-v8a/libldk_node.so b/bindings/kotlin/ldk-node-android/lib/src/main/jniLibs/arm64-v8a/libldk_node.so index a3c890372..945ec12b1 100755 Binary files a/bindings/kotlin/ldk-node-android/lib/src/main/jniLibs/arm64-v8a/libldk_node.so and b/bindings/kotlin/ldk-node-android/lib/src/main/jniLibs/arm64-v8a/libldk_node.so differ diff --git a/bindings/kotlin/ldk-node-android/lib/src/main/jniLibs/armeabi-v7a/libldk_node.so b/bindings/kotlin/ldk-node-android/lib/src/main/jniLibs/armeabi-v7a/libldk_node.so index e3422f752..71ba6c29b 100755 Binary files a/bindings/kotlin/ldk-node-android/lib/src/main/jniLibs/armeabi-v7a/libldk_node.so and b/bindings/kotlin/ldk-node-android/lib/src/main/jniLibs/armeabi-v7a/libldk_node.so differ diff --git a/bindings/kotlin/ldk-node-android/lib/src/main/jniLibs/x86_64/libldk_node.so b/bindings/kotlin/ldk-node-android/lib/src/main/jniLibs/x86_64/libldk_node.so index 3edb943db..9841b8bde 100755 Binary files a/bindings/kotlin/ldk-node-android/lib/src/main/jniLibs/x86_64/libldk_node.so and b/bindings/kotlin/ldk-node-android/lib/src/main/jniLibs/x86_64/libldk_node.so differ diff --git a/bindings/kotlin/ldk-node-jvm/lib/src/main/resources/darwin-aarch64/libldk_node.dylib b/bindings/kotlin/ldk-node-jvm/lib/src/main/resources/darwin-aarch64/libldk_node.dylib index 5a363752f..2051b151b 100644 Binary files a/bindings/kotlin/ldk-node-jvm/lib/src/main/resources/darwin-aarch64/libldk_node.dylib and b/bindings/kotlin/ldk-node-jvm/lib/src/main/resources/darwin-aarch64/libldk_node.dylib differ diff --git a/bindings/kotlin/ldk-node-jvm/lib/src/main/resources/darwin-x86-64/libldk_node.dylib b/bindings/kotlin/ldk-node-jvm/lib/src/main/resources/darwin-x86-64/libldk_node.dylib index 6cc224d80..bb880a77c 100644 Binary files a/bindings/kotlin/ldk-node-jvm/lib/src/main/resources/darwin-x86-64/libldk_node.dylib and b/bindings/kotlin/ldk-node-jvm/lib/src/main/resources/darwin-x86-64/libldk_node.dylib differ diff --git a/bindings/swift/Sources/LDKNode/LDKNode.swift b/bindings/swift/Sources/LDKNode/LDKNode.swift index 735d99fc3..24a98dfc7 100644 --- a/bindings/swift/Sources/LDKNode/LDKNode.swift +++ b/bindings/swift/Sources/LDKNode/LDKNode.swift @@ -13,7 +13,7 @@ import Foundation #endif private extension RustBuffer { - // Allocate a new buffer, copying the contents of a `UInt8` array. + /// Allocate a new buffer, copying the contents of a `UInt8` array. init(bytes: [UInt8]) { let rbuf = bytes.withUnsafeBufferPointer { ptr in RustBuffer.from(ptr) @@ -29,8 +29,8 @@ private extension RustBuffer { try! rustCall { ffi_ldk_node_rustbuffer_from_bytes(ForeignBytes(bufferPointer: ptr), $0) } } - // Frees the buffer in place. - // The buffer must not be used after this is called. + /// Frees the buffer in place. + /// The buffer must not be used after this is called. func deallocate() { try! rustCall { ffi_ldk_node_rustbuffer_free(self, $0) } } @@ -77,9 +77,9 @@ private func createReader(data: Data) -> (data: Data, offset: Data.Index) { (data: data, 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. +/// 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. private func readInt(_ reader: inout (data: Data, offset: Data.Index)) throws -> T { let range = reader.offset ..< reader.offset + MemoryLayout.size guard reader.data.count >= range.upperBound else { @@ -96,8 +96,8 @@ private func readInt(_ reader: inout (data: Data, offset: return value.bigEndian } -// Reads an arbitrary number of bytes, to be used to read -// raw bytes, this is useful when lifting strings +/// Reads an arbitrary number of bytes, to be used to read +/// raw bytes, this is useful when lifting strings private func readBytes(_ reader: inout (data: Data, offset: Data.Index), count: Int) throws -> [UInt8] { let range = reader.offset ..< (reader.offset + count) guard reader.data.count >= range.upperBound else { @@ -111,17 +111,17 @@ private func readBytes(_ reader: inout (data: Data, offset: Data.Index), count: return value } -// Reads a float at the current offset. +/// Reads a float at the current offset. private func readFloat(_ reader: inout (data: Data, offset: Data.Index)) throws -> Float { return try Float(bitPattern: readInt(&reader)) } -// Reads a float at the current offset. +/// Reads a float at the current offset. private func readDouble(_ reader: inout (data: Data, offset: Data.Index)) throws -> Double { return try Double(bitPattern: readInt(&reader)) } -// Indicates if the offset has reached the end of the buffer. +/// Indicates if the offset has reached the end of the buffer. private func hasRemaining(_ reader: (data: Data, offset: Data.Index)) -> Bool { return reader.offset < reader.data.count } @@ -134,14 +134,14 @@ private func createWriter() -> [UInt8] { return [] } -private func writeBytes(_ writer: inout [UInt8], _ byteArr: S) where S: Sequence, S.Element == UInt8 { +private func writeBytes(_ writer: inout [UInt8], _ byteArr: S) where S.Element == UInt8 { writer.append(contentsOf: byteArr) } -// Writes an integer in big-endian order. -// -// Warning: make sure what you are trying to write -// is in the correct type! +/// Writes an integer in big-endian order. +/// +/// Warning: make sure what you are trying to write +/// is in the correct type! private func writeInt(_ writer: inout [UInt8], _ value: T) { var value = value.bigEndian withUnsafeBytes(of: &value) { writer.append(contentsOf: $0) } @@ -155,8 +155,8 @@ private func writeDouble(_ writer: inout [UInt8], _ value: Double) { writeInt(&writer, value.bitPattern) } -// Protocol for types that transfer other types across the FFI. This is -// analogous to the Rust trait of the same name. +/// Protocol for types that transfer other types across the FFI. This is +/// analogous to the Rust trait of the same name. private protocol FfiConverter { associatedtype FfiType associatedtype SwiftType @@ -167,7 +167,7 @@ private protocol FfiConverter { static func write(_ value: SwiftType, into buf: inout [UInt8]) } -// Types conforming to `Primitive` pass themselves directly over the FFI. +/// Types conforming to `Primitive` pass themselves directly over the FFI. private protocol FfiConverterPrimitive: FfiConverter where FfiType == SwiftType {} extension FfiConverterPrimitive { @@ -186,8 +186,8 @@ extension FfiConverterPrimitive { } } -// Types conforming to `FfiConverterRustBuffer` lift and lower into a `RustBuffer`. -// Used for complex types where it's hard to write a custom lift/lower. +/// Types conforming to `FfiConverterRustBuffer` lift and lower into a `RustBuffer`. +/// Used for complex types where it's hard to write a custom lift/lower. private protocol FfiConverterRustBuffer: FfiConverter where FfiType == RustBuffer {} extension FfiConverterRustBuffer { @@ -214,8 +214,8 @@ extension FfiConverterRustBuffer { } } -// An error type for FFI errors. These errors occur at the UniFFI level, not -// the library level. +/// An error type for FFI errors. These errors occur at the UniFFI level, not +/// the library level. private enum UniffiInternalError: LocalizedError { case bufferOverflow case incompleteData @@ -601,7 +601,7 @@ open class Bolt11Invoice: { fileprivate let pointer: UnsafeMutableRawPointer! - /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. + // Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -862,7 +862,7 @@ open class Bolt11Payment: { fileprivate let pointer: UnsafeMutableRawPointer! - /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. + // Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -906,12 +906,13 @@ open class Bolt11Payment: try! rustCall { uniffi_ldk_node_fn_free_bolt11payment(pointer, $0) } } - open func claimForHash(paymentHash: PaymentHash, claimableAmountMsat: UInt64, preimage: PaymentPreimage) throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_bolt11payment_claim_for_hash(self.uniffiClonePointer(), - FfiConverterTypePaymentHash.lower(paymentHash), - FfiConverterUInt64.lower(claimableAmountMsat), - FfiConverterTypePaymentPreimage.lower(preimage), $0) - } + open func claimForHash(paymentHash: PaymentHash, claimableAmountMsat: UInt64, preimage: PaymentPreimage) throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_bolt11payment_claim_for_hash(self.uniffiClonePointer(), + FfiConverterTypePaymentHash.lower(paymentHash), + FfiConverterUInt64.lower(claimableAmountMsat), + FfiConverterTypePaymentPreimage.lower(preimage), $0) + } } open func estimateRoutingFees(invoice: Bolt11Invoice) throws -> UInt64 { @@ -929,10 +930,11 @@ open class Bolt11Payment: }) } - open func failForHash(paymentHash: PaymentHash) throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_bolt11payment_fail_for_hash(self.uniffiClonePointer(), - FfiConverterTypePaymentHash.lower(paymentHash), $0) - } + open func failForHash(paymentHash: PaymentHash) throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_bolt11payment_fail_for_hash(self.uniffiClonePointer(), + FfiConverterTypePaymentHash.lower(paymentHash), $0) + } } open func receive(amountMsat: UInt64, description: Bolt11InvoiceDescription, expirySecs: UInt32) throws -> Bolt11Invoice { @@ -1019,19 +1021,21 @@ open class Bolt11Payment: }) } - open func sendProbes(invoice: Bolt11Invoice, routeParameters: RouteParametersConfig?) throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_bolt11payment_send_probes(self.uniffiClonePointer(), - FfiConverterTypeBolt11Invoice.lower(invoice), - FfiConverterOptionTypeRouteParametersConfig.lower(routeParameters), $0) - } + open func sendProbes(invoice: Bolt11Invoice, routeParameters: RouteParametersConfig?) throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_bolt11payment_send_probes(self.uniffiClonePointer(), + FfiConverterTypeBolt11Invoice.lower(invoice), + FfiConverterOptionTypeRouteParametersConfig.lower(routeParameters), $0) + } } - open func sendProbesUsingAmount(invoice: Bolt11Invoice, amountMsat: UInt64, routeParameters: RouteParametersConfig?) throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_bolt11payment_send_probes_using_amount(self.uniffiClonePointer(), - FfiConverterTypeBolt11Invoice.lower(invoice), - FfiConverterUInt64.lower(amountMsat), - FfiConverterOptionTypeRouteParametersConfig.lower(routeParameters), $0) - } + open func sendProbesUsingAmount(invoice: Bolt11Invoice, amountMsat: UInt64, routeParameters: RouteParametersConfig?) throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_bolt11payment_send_probes_using_amount(self.uniffiClonePointer(), + FfiConverterTypeBolt11Invoice.lower(invoice), + FfiConverterUInt64.lower(amountMsat), + FfiConverterOptionTypeRouteParametersConfig.lower(routeParameters), $0) + } } open func sendUsingAmount(invoice: Bolt11Invoice, amountMsat: UInt64, routeParameters: RouteParametersConfig?) throws -> PaymentId { @@ -1138,7 +1142,7 @@ open class Bolt12Invoice: { fileprivate let pointer: UnsafeMutableRawPointer! - /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. + // Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -1383,7 +1387,7 @@ open class Bolt12Payment: { fileprivate let pointer: UnsafeMutableRawPointer! - /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. + // Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -1497,10 +1501,11 @@ open class Bolt12Payment: }) } - open func setPathsToStaticInvoiceServer(paths: Data) throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_bolt12payment_set_paths_to_static_invoice_server(self.uniffiClonePointer(), - FfiConverterData.lower(paths), $0) - } + open func setPathsToStaticInvoiceServer(paths: Data) throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_bolt12payment_set_paths_to_static_invoice_server(self.uniffiClonePointer(), + FfiConverterData.lower(paths), $0) + } } } @@ -1616,7 +1621,7 @@ open class Builder: { fileprivate let pointer: UnsafeMutableRawPointer! - /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. + // Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -1653,8 +1658,7 @@ open class Builder: public convenience init() { let pointer = try! rustCall { - uniffi_ldk_node_fn_constructor_builder_new($0 - ) + uniffi_ldk_node_fn_constructor_builder_new($0) } self.init(unsafeFromRawPointer: pointer) } @@ -1715,162 +1719,186 @@ open class Builder: }) } - open func setAddressType(addressType: AddressType) { try! rustCall { - uniffi_ldk_node_fn_method_builder_set_address_type(self.uniffiClonePointer(), - FfiConverterTypeAddressType.lower(addressType), $0) - } + open func setAddressType(addressType: AddressType) { + try! rustCall { + uniffi_ldk_node_fn_method_builder_set_address_type(self.uniffiClonePointer(), + FfiConverterTypeAddressType.lower(addressType), $0) + } } - open func setAddressTypesToMonitor(addressTypesToMonitor: [AddressType]) { try! rustCall { - uniffi_ldk_node_fn_method_builder_set_address_types_to_monitor(self.uniffiClonePointer(), - FfiConverterSequenceTypeAddressType.lower(addressTypesToMonitor), $0) - } + open func setAddressTypesToMonitor(addressTypesToMonitor: [AddressType]) { + try! rustCall { + uniffi_ldk_node_fn_method_builder_set_address_types_to_monitor(self.uniffiClonePointer(), + FfiConverterSequenceTypeAddressType.lower(addressTypesToMonitor), $0) + } } - open func setAnnouncementAddresses(announcementAddresses: [SocketAddress]) throws { try rustCallWithError(FfiConverterTypeBuildError.lift) { - uniffi_ldk_node_fn_method_builder_set_announcement_addresses(self.uniffiClonePointer(), - FfiConverterSequenceTypeSocketAddress.lower(announcementAddresses), $0) - } + open func setAnnouncementAddresses(announcementAddresses: [SocketAddress]) throws { + try rustCallWithError(FfiConverterTypeBuildError.lift) { + uniffi_ldk_node_fn_method_builder_set_announcement_addresses(self.uniffiClonePointer(), + FfiConverterSequenceTypeSocketAddress.lower(announcementAddresses), $0) + } } - open func setAsyncPaymentsRole(role: AsyncPaymentsRole?) throws { try rustCallWithError(FfiConverterTypeBuildError.lift) { - uniffi_ldk_node_fn_method_builder_set_async_payments_role(self.uniffiClonePointer(), - FfiConverterOptionTypeAsyncPaymentsRole.lower(role), $0) - } + open func setAsyncPaymentsRole(role: AsyncPaymentsRole?) throws { + try rustCallWithError(FfiConverterTypeBuildError.lift) { + uniffi_ldk_node_fn_method_builder_set_async_payments_role(self.uniffiClonePointer(), + FfiConverterOptionTypeAsyncPaymentsRole.lower(role), $0) + } } - open func setChainSourceBitcoindRest(restHost: String, restPort: UInt16, rpcHost: String, rpcPort: UInt16, rpcUser: String, rpcPassword: String) { try! rustCall { - uniffi_ldk_node_fn_method_builder_set_chain_source_bitcoind_rest(self.uniffiClonePointer(), - FfiConverterString.lower(restHost), - FfiConverterUInt16.lower(restPort), - FfiConverterString.lower(rpcHost), - FfiConverterUInt16.lower(rpcPort), - FfiConverterString.lower(rpcUser), - FfiConverterString.lower(rpcPassword), $0) - } + open func setChainSourceBitcoindRest(restHost: String, restPort: UInt16, rpcHost: String, rpcPort: UInt16, rpcUser: String, rpcPassword: String) { + try! rustCall { + uniffi_ldk_node_fn_method_builder_set_chain_source_bitcoind_rest(self.uniffiClonePointer(), + FfiConverterString.lower(restHost), + FfiConverterUInt16.lower(restPort), + FfiConverterString.lower(rpcHost), + FfiConverterUInt16.lower(rpcPort), + FfiConverterString.lower(rpcUser), + FfiConverterString.lower(rpcPassword), $0) + } } - open func setChainSourceBitcoindRpc(rpcHost: String, rpcPort: UInt16, rpcUser: String, rpcPassword: String) { try! rustCall { - uniffi_ldk_node_fn_method_builder_set_chain_source_bitcoind_rpc(self.uniffiClonePointer(), - FfiConverterString.lower(rpcHost), - FfiConverterUInt16.lower(rpcPort), - FfiConverterString.lower(rpcUser), - FfiConverterString.lower(rpcPassword), $0) - } + open func setChainSourceBitcoindRpc(rpcHost: String, rpcPort: UInt16, rpcUser: String, rpcPassword: String) { + try! rustCall { + uniffi_ldk_node_fn_method_builder_set_chain_source_bitcoind_rpc(self.uniffiClonePointer(), + FfiConverterString.lower(rpcHost), + FfiConverterUInt16.lower(rpcPort), + FfiConverterString.lower(rpcUser), + FfiConverterString.lower(rpcPassword), $0) + } } - open func setChainSourceElectrum(serverUrl: String, config: ElectrumSyncConfig?) { try! rustCall { - uniffi_ldk_node_fn_method_builder_set_chain_source_electrum(self.uniffiClonePointer(), - FfiConverterString.lower(serverUrl), - FfiConverterOptionTypeElectrumSyncConfig.lower(config), $0) - } + open func setChainSourceElectrum(serverUrl: String, config: ElectrumSyncConfig?) { + try! rustCall { + uniffi_ldk_node_fn_method_builder_set_chain_source_electrum(self.uniffiClonePointer(), + FfiConverterString.lower(serverUrl), + FfiConverterOptionTypeElectrumSyncConfig.lower(config), $0) + } } - open func setChainSourceEsplora(serverUrl: String, config: EsploraSyncConfig?) { try! rustCall { - uniffi_ldk_node_fn_method_builder_set_chain_source_esplora(self.uniffiClonePointer(), - FfiConverterString.lower(serverUrl), - FfiConverterOptionTypeEsploraSyncConfig.lower(config), $0) - } + open func setChainSourceEsplora(serverUrl: String, config: EsploraSyncConfig?) { + try! rustCall { + uniffi_ldk_node_fn_method_builder_set_chain_source_esplora(self.uniffiClonePointer(), + FfiConverterString.lower(serverUrl), + FfiConverterOptionTypeEsploraSyncConfig.lower(config), $0) + } } - open func setChannelDataMigration(migration: ChannelDataMigration) { try! rustCall { - uniffi_ldk_node_fn_method_builder_set_channel_data_migration(self.uniffiClonePointer(), - FfiConverterTypeChannelDataMigration.lower(migration), $0) - } + open func setChannelDataMigration(migration: ChannelDataMigration) { + try! rustCall { + uniffi_ldk_node_fn_method_builder_set_channel_data_migration(self.uniffiClonePointer(), + FfiConverterTypeChannelDataMigration.lower(migration), $0) + } } - open func setCustomLogger(logWriter: LogWriter) { try! rustCall { - uniffi_ldk_node_fn_method_builder_set_custom_logger(self.uniffiClonePointer(), - FfiConverterTypeLogWriter.lower(logWriter), $0) - } + open func setCustomLogger(logWriter: LogWriter) { + try! rustCall { + uniffi_ldk_node_fn_method_builder_set_custom_logger(self.uniffiClonePointer(), + FfiConverterTypeLogWriter.lower(logWriter), $0) + } } - open func setEntropyBip39Mnemonic(mnemonic: Mnemonic, passphrase: String?) { try! rustCall { - uniffi_ldk_node_fn_method_builder_set_entropy_bip39_mnemonic(self.uniffiClonePointer(), - FfiConverterTypeMnemonic.lower(mnemonic), - FfiConverterOptionString.lower(passphrase), $0) - } + open func setEntropyBip39Mnemonic(mnemonic: Mnemonic, passphrase: String?) { + try! rustCall { + uniffi_ldk_node_fn_method_builder_set_entropy_bip39_mnemonic(self.uniffiClonePointer(), + FfiConverterTypeMnemonic.lower(mnemonic), + FfiConverterOptionString.lower(passphrase), $0) + } } - open func setEntropySeedBytes(seedBytes: [UInt8]) throws { try rustCallWithError(FfiConverterTypeBuildError.lift) { - uniffi_ldk_node_fn_method_builder_set_entropy_seed_bytes(self.uniffiClonePointer(), - FfiConverterSequenceUInt8.lower(seedBytes), $0) - } + open func setEntropySeedBytes(seedBytes: [UInt8]) throws { + try rustCallWithError(FfiConverterTypeBuildError.lift) { + uniffi_ldk_node_fn_method_builder_set_entropy_seed_bytes(self.uniffiClonePointer(), + FfiConverterSequenceUInt8.lower(seedBytes), $0) + } } - open func setEntropySeedPath(seedPath: String) { try! rustCall { - uniffi_ldk_node_fn_method_builder_set_entropy_seed_path(self.uniffiClonePointer(), - FfiConverterString.lower(seedPath), $0) - } + open func setEntropySeedPath(seedPath: String) { + try! rustCall { + uniffi_ldk_node_fn_method_builder_set_entropy_seed_path(self.uniffiClonePointer(), + FfiConverterString.lower(seedPath), $0) + } } - open func setFilesystemLogger(logFilePath: String?, maxLogLevel: LogLevel?) { try! rustCall { - uniffi_ldk_node_fn_method_builder_set_filesystem_logger(self.uniffiClonePointer(), - FfiConverterOptionString.lower(logFilePath), - FfiConverterOptionTypeLogLevel.lower(maxLogLevel), $0) - } + open func setFilesystemLogger(logFilePath: String?, maxLogLevel: LogLevel?) { + try! rustCall { + uniffi_ldk_node_fn_method_builder_set_filesystem_logger(self.uniffiClonePointer(), + FfiConverterOptionString.lower(logFilePath), + FfiConverterOptionTypeLogLevel.lower(maxLogLevel), $0) + } } - open func setGossipSourceP2p() { try! rustCall { - uniffi_ldk_node_fn_method_builder_set_gossip_source_p2p(self.uniffiClonePointer(), $0) - } + open func setGossipSourceP2p() { + try! rustCall { + uniffi_ldk_node_fn_method_builder_set_gossip_source_p2p(self.uniffiClonePointer(), $0) + } } - open func setGossipSourceRgs(rgsServerUrl: String) { try! rustCall { - uniffi_ldk_node_fn_method_builder_set_gossip_source_rgs(self.uniffiClonePointer(), - FfiConverterString.lower(rgsServerUrl), $0) - } + open func setGossipSourceRgs(rgsServerUrl: String) { + try! rustCall { + uniffi_ldk_node_fn_method_builder_set_gossip_source_rgs(self.uniffiClonePointer(), + FfiConverterString.lower(rgsServerUrl), $0) + } } - open func setLiquiditySourceLsps1(nodeId: PublicKey, address: SocketAddress, token: String?) { try! rustCall { - uniffi_ldk_node_fn_method_builder_set_liquidity_source_lsps1(self.uniffiClonePointer(), - FfiConverterTypePublicKey.lower(nodeId), - FfiConverterTypeSocketAddress.lower(address), - FfiConverterOptionString.lower(token), $0) - } + open func setLiquiditySourceLsps1(nodeId: PublicKey, address: SocketAddress, token: String?) { + try! rustCall { + uniffi_ldk_node_fn_method_builder_set_liquidity_source_lsps1(self.uniffiClonePointer(), + FfiConverterTypePublicKey.lower(nodeId), + FfiConverterTypeSocketAddress.lower(address), + FfiConverterOptionString.lower(token), $0) + } } - open func setLiquiditySourceLsps2(nodeId: PublicKey, address: SocketAddress, token: String?) { try! rustCall { - uniffi_ldk_node_fn_method_builder_set_liquidity_source_lsps2(self.uniffiClonePointer(), - FfiConverterTypePublicKey.lower(nodeId), - FfiConverterTypeSocketAddress.lower(address), - FfiConverterOptionString.lower(token), $0) - } + open func setLiquiditySourceLsps2(nodeId: PublicKey, address: SocketAddress, token: String?) { + try! rustCall { + uniffi_ldk_node_fn_method_builder_set_liquidity_source_lsps2(self.uniffiClonePointer(), + FfiConverterTypePublicKey.lower(nodeId), + FfiConverterTypeSocketAddress.lower(address), + FfiConverterOptionString.lower(token), $0) + } } - open func setListeningAddresses(listeningAddresses: [SocketAddress]) throws { try rustCallWithError(FfiConverterTypeBuildError.lift) { - uniffi_ldk_node_fn_method_builder_set_listening_addresses(self.uniffiClonePointer(), - FfiConverterSequenceTypeSocketAddress.lower(listeningAddresses), $0) - } + open func setListeningAddresses(listeningAddresses: [SocketAddress]) throws { + try rustCallWithError(FfiConverterTypeBuildError.lift) { + uniffi_ldk_node_fn_method_builder_set_listening_addresses(self.uniffiClonePointer(), + FfiConverterSequenceTypeSocketAddress.lower(listeningAddresses), $0) + } } - open func setLogFacadeLogger() { try! rustCall { - uniffi_ldk_node_fn_method_builder_set_log_facade_logger(self.uniffiClonePointer(), $0) - } + open func setLogFacadeLogger() { + try! rustCall { + uniffi_ldk_node_fn_method_builder_set_log_facade_logger(self.uniffiClonePointer(), $0) + } } - open func setNetwork(network: Network) { try! rustCall { - uniffi_ldk_node_fn_method_builder_set_network(self.uniffiClonePointer(), - FfiConverterTypeNetwork.lower(network), $0) - } + open func setNetwork(network: Network) { + try! rustCall { + uniffi_ldk_node_fn_method_builder_set_network(self.uniffiClonePointer(), + FfiConverterTypeNetwork.lower(network), $0) + } } - open func setNodeAlias(nodeAlias: String) throws { try rustCallWithError(FfiConverterTypeBuildError.lift) { - uniffi_ldk_node_fn_method_builder_set_node_alias(self.uniffiClonePointer(), - FfiConverterString.lower(nodeAlias), $0) - } + open func setNodeAlias(nodeAlias: String) throws { + try rustCallWithError(FfiConverterTypeBuildError.lift) { + uniffi_ldk_node_fn_method_builder_set_node_alias(self.uniffiClonePointer(), + FfiConverterString.lower(nodeAlias), $0) + } } - open func setPathfindingScoresSource(url: String) { try! rustCall { - uniffi_ldk_node_fn_method_builder_set_pathfinding_scores_source(self.uniffiClonePointer(), - FfiConverterString.lower(url), $0) - } + open func setPathfindingScoresSource(url: String) { + try! rustCall { + uniffi_ldk_node_fn_method_builder_set_pathfinding_scores_source(self.uniffiClonePointer(), + FfiConverterString.lower(url), $0) + } } - open func setStorageDirPath(storageDirPath: String) { try! rustCall { - uniffi_ldk_node_fn_method_builder_set_storage_dir_path(self.uniffiClonePointer(), - FfiConverterString.lower(storageDirPath), $0) - } + open func setStorageDirPath(storageDirPath: String) { + try! rustCall { + uniffi_ldk_node_fn_method_builder_set_storage_dir_path(self.uniffiClonePointer(), + FfiConverterString.lower(storageDirPath), $0) + } } } @@ -1934,7 +1962,7 @@ open class FeeRate: { fileprivate let pointer: UnsafeMutableRawPointer! - /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. + // Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -2071,7 +2099,7 @@ open class Lsps1Liquidity: { fileprivate let pointer: UnsafeMutableRawPointer! - /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. + // Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -2189,7 +2217,7 @@ open class LogWriterImpl: { fileprivate let pointer: UnsafeMutableRawPointer! - /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. + // Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -2233,25 +2261,26 @@ open class LogWriterImpl: try! rustCall { uniffi_ldk_node_fn_free_logwriter(pointer, $0) } } - open func log(record: LogRecord) { try! rustCall { - uniffi_ldk_node_fn_method_logwriter_log(self.uniffiClonePointer(), - FfiConverterTypeLogRecord.lower(record), $0) - } + open func log(record: LogRecord) { + try! rustCall { + uniffi_ldk_node_fn_method_logwriter_log(self.uniffiClonePointer(), + FfiConverterTypeLogRecord.lower(record), $0) + } } } -// 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. +/// 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 // Callback return codes private let UNIFFI_CALLBACK_SUCCESS: Int32 = 0 private let UNIFFI_CALLBACK_ERROR: Int32 = 1 private let UNIFFI_CALLBACK_UNEXPECTED_ERROR: Int32 = 2 -// Put the implementation in a struct so we don't pollute the top-level namespace +/// Put the implementation in a struct so we don't pollute the top-level namespace private enum UniffiCallbackInterfaceLogWriter { - // Create the VTable using a series of closures. - // Swift automatically converts these into C callback functions. + /// Create the VTable using a series of closures. + /// Swift automatically converts these into C callback functions. static var vtable: UniffiVTableCallbackInterfaceLogWriter = .init( log: { ( uniffiHandle: UInt64, @@ -2356,7 +2385,7 @@ open class NetworkGraph: { fileprivate let pointer: UnsafeMutableRawPointer! - /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. + // Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -2579,7 +2608,7 @@ open class Node: { fileprivate let pointer: UnsafeMutableRawPointer! - /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. + // Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -2623,19 +2652,21 @@ open class Node: try! rustCall { uniffi_ldk_node_fn_free_node(pointer, $0) } } - open func addAddressTypeToMonitor(addressType: AddressType, seedBytes: [UInt8]) throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_node_add_address_type_to_monitor(self.uniffiClonePointer(), - FfiConverterTypeAddressType.lower(addressType), - FfiConverterSequenceUInt8.lower(seedBytes), $0) - } + open func addAddressTypeToMonitor(addressType: AddressType, seedBytes: [UInt8]) throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_node_add_address_type_to_monitor(self.uniffiClonePointer(), + FfiConverterTypeAddressType.lower(addressType), + FfiConverterSequenceUInt8.lower(seedBytes), $0) + } } - open func addAddressTypeToMonitorWithMnemonic(addressType: AddressType, mnemonic: Mnemonic, passphrase: String?) throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_node_add_address_type_to_monitor_with_mnemonic(self.uniffiClonePointer(), - FfiConverterTypeAddressType.lower(addressType), - FfiConverterTypeMnemonic.lower(mnemonic), - FfiConverterOptionString.lower(passphrase), $0) - } + open func addAddressTypeToMonitorWithMnemonic(addressType: AddressType, mnemonic: Mnemonic, passphrase: String?) throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_node_add_address_type_to_monitor_with_mnemonic(self.uniffiClonePointer(), + FfiConverterTypeAddressType.lower(addressType), + FfiConverterTypeMnemonic.lower(mnemonic), + FfiConverterOptionString.lower(passphrase), $0) + } } open func announcementAddresses() -> [SocketAddress]? { @@ -2656,11 +2687,12 @@ open class Node: }) } - open func closeChannel(userChannelId: UserChannelId, counterpartyNodeId: PublicKey) throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_node_close_channel(self.uniffiClonePointer(), - FfiConverterTypeUserChannelId.lower(userChannelId), - FfiConverterTypePublicKey.lower(counterpartyNodeId), $0) - } + open func closeChannel(userChannelId: UserChannelId, counterpartyNodeId: PublicKey) throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_node_close_channel(self.uniffiClonePointer(), + FfiConverterTypeUserChannelId.lower(userChannelId), + FfiConverterTypePublicKey.lower(counterpartyNodeId), $0) + } } open func config() -> Config { @@ -2669,12 +2701,13 @@ open class Node: }) } - open func connect(nodeId: PublicKey, address: SocketAddress, persist: Bool) throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_node_connect(self.uniffiClonePointer(), - FfiConverterTypePublicKey.lower(nodeId), - FfiConverterTypeSocketAddress.lower(address), - FfiConverterBool.lower(persist), $0) - } + open func connect(nodeId: PublicKey, address: SocketAddress, persist: Bool) throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_node_connect(self.uniffiClonePointer(), + FfiConverterTypePublicKey.lower(nodeId), + FfiConverterTypeSocketAddress.lower(address), + FfiConverterBool.lower(persist), $0) + } } open func currentSyncIntervals() -> RuntimeSyncIntervals { @@ -2683,15 +2716,17 @@ open class Node: }) } - open func disconnect(nodeId: PublicKey) throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_node_disconnect(self.uniffiClonePointer(), - FfiConverterTypePublicKey.lower(nodeId), $0) - } + open func disconnect(nodeId: PublicKey) throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_node_disconnect(self.uniffiClonePointer(), + FfiConverterTypePublicKey.lower(nodeId), $0) + } } - open func eventHandled() throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_node_event_handled(self.uniffiClonePointer(), $0) - } + open func eventHandled() throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_node_event_handled(self.uniffiClonePointer(), $0) + } } open func exportPathfindingScores() throws -> Data { @@ -2700,12 +2735,13 @@ open class Node: }) } - open func forceCloseChannel(userChannelId: UserChannelId, counterpartyNodeId: PublicKey, reason: String?) throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_node_force_close_channel(self.uniffiClonePointer(), - FfiConverterTypeUserChannelId.lower(userChannelId), - FfiConverterTypePublicKey.lower(counterpartyNodeId), - FfiConverterOptionString.lower(reason), $0) - } + open func forceCloseChannel(userChannelId: UserChannelId, counterpartyNodeId: PublicKey, reason: String?) throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_node_force_close_channel(self.uniffiClonePointer(), + FfiConverterTypeUserChannelId.lower(userChannelId), + FfiConverterTypePublicKey.lower(counterpartyNodeId), + FfiConverterOptionString.lower(reason), $0) + } } open func getAddressBalance(addressStr: String) throws -> UInt64 { @@ -2846,31 +2882,35 @@ open class Node: }) } - open func removeAddressTypeFromMonitor(addressType: AddressType) throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_node_remove_address_type_from_monitor(self.uniffiClonePointer(), - FfiConverterTypeAddressType.lower(addressType), $0) - } + open func removeAddressTypeFromMonitor(addressType: AddressType) throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_node_remove_address_type_from_monitor(self.uniffiClonePointer(), + FfiConverterTypeAddressType.lower(addressType), $0) + } } - open func removePayment(paymentId: PaymentId) throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_node_remove_payment(self.uniffiClonePointer(), - FfiConverterTypePaymentId.lower(paymentId), $0) - } + open func removePayment(paymentId: PaymentId) throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_node_remove_payment(self.uniffiClonePointer(), + FfiConverterTypePaymentId.lower(paymentId), $0) + } } - open func setPrimaryAddressType(addressType: AddressType, seedBytes: [UInt8]) throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_node_set_primary_address_type(self.uniffiClonePointer(), - FfiConverterTypeAddressType.lower(addressType), - FfiConverterSequenceUInt8.lower(seedBytes), $0) - } + open func setPrimaryAddressType(addressType: AddressType, seedBytes: [UInt8]) throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_node_set_primary_address_type(self.uniffiClonePointer(), + FfiConverterTypeAddressType.lower(addressType), + FfiConverterSequenceUInt8.lower(seedBytes), $0) + } } - open func setPrimaryAddressTypeWithMnemonic(addressType: AddressType, mnemonic: Mnemonic, passphrase: String?) throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_node_set_primary_address_type_with_mnemonic(self.uniffiClonePointer(), - FfiConverterTypeAddressType.lower(addressType), - FfiConverterTypeMnemonic.lower(mnemonic), - FfiConverterOptionString.lower(passphrase), $0) - } + open func setPrimaryAddressTypeWithMnemonic(addressType: AddressType, mnemonic: Mnemonic, passphrase: String?) throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_node_set_primary_address_type_with_mnemonic(self.uniffiClonePointer(), + FfiConverterTypeAddressType.lower(addressType), + FfiConverterTypeMnemonic.lower(mnemonic), + FfiConverterOptionString.lower(passphrase), $0) + } } open func signMessage(msg: [UInt8]) -> String { @@ -2880,21 +2920,23 @@ open class Node: }) } - open func spliceIn(userChannelId: UserChannelId, counterpartyNodeId: PublicKey, spliceAmountSats: UInt64) throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_node_splice_in(self.uniffiClonePointer(), - FfiConverterTypeUserChannelId.lower(userChannelId), - FfiConverterTypePublicKey.lower(counterpartyNodeId), - FfiConverterUInt64.lower(spliceAmountSats), $0) - } + open func spliceIn(userChannelId: UserChannelId, counterpartyNodeId: PublicKey, spliceAmountSats: UInt64) throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_node_splice_in(self.uniffiClonePointer(), + FfiConverterTypeUserChannelId.lower(userChannelId), + FfiConverterTypePublicKey.lower(counterpartyNodeId), + FfiConverterUInt64.lower(spliceAmountSats), $0) + } } - open func spliceOut(userChannelId: UserChannelId, counterpartyNodeId: PublicKey, address: Address, spliceAmountSats: UInt64) throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_node_splice_out(self.uniffiClonePointer(), - FfiConverterTypeUserChannelId.lower(userChannelId), - FfiConverterTypePublicKey.lower(counterpartyNodeId), - FfiConverterTypeAddress.lower(address), - FfiConverterUInt64.lower(spliceAmountSats), $0) - } + open func spliceOut(userChannelId: UserChannelId, counterpartyNodeId: PublicKey, address: Address, spliceAmountSats: UInt64) throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_node_splice_out(self.uniffiClonePointer(), + FfiConverterTypeUserChannelId.lower(userChannelId), + FfiConverterTypePublicKey.lower(counterpartyNodeId), + FfiConverterTypeAddress.lower(address), + FfiConverterUInt64.lower(spliceAmountSats), $0) + } } open func spontaneousPayment() -> SpontaneousPayment { @@ -2903,9 +2945,10 @@ open class Node: }) } - open func start() throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_node_start(self.uniffiClonePointer(), $0) - } + open func start() throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_node_start(self.uniffiClonePointer(), $0) + } } open func status() -> NodeStatus { @@ -2914,14 +2957,16 @@ open class Node: }) } - open func stop() throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_node_stop(self.uniffiClonePointer(), $0) - } + open func stop() throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_node_stop(self.uniffiClonePointer(), $0) + } } - open func syncWallets() throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_node_sync_wallets(self.uniffiClonePointer(), $0) - } + open func syncWallets() throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_node_sync_wallets(self.uniffiClonePointer(), $0) + } } open func unifiedQrPayment() -> UnifiedQrPayment { @@ -2930,18 +2975,20 @@ open class Node: }) } - open func updateChannelConfig(userChannelId: UserChannelId, counterpartyNodeId: PublicKey, channelConfig: ChannelConfig) throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_node_update_channel_config(self.uniffiClonePointer(), - FfiConverterTypeUserChannelId.lower(userChannelId), - FfiConverterTypePublicKey.lower(counterpartyNodeId), - FfiConverterTypeChannelConfig.lower(channelConfig), $0) - } + open func updateChannelConfig(userChannelId: UserChannelId, counterpartyNodeId: PublicKey, channelConfig: ChannelConfig) throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_node_update_channel_config(self.uniffiClonePointer(), + FfiConverterTypeUserChannelId.lower(userChannelId), + FfiConverterTypePublicKey.lower(counterpartyNodeId), + FfiConverterTypeChannelConfig.lower(channelConfig), $0) + } } - open func updateSyncIntervals(intervals: RuntimeSyncIntervals) throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_node_update_sync_intervals(self.uniffiClonePointer(), - FfiConverterTypeRuntimeSyncIntervals.lower(intervals), $0) - } + open func updateSyncIntervals(intervals: RuntimeSyncIntervals) throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_node_update_sync_intervals(self.uniffiClonePointer(), + FfiConverterTypeRuntimeSyncIntervals.lower(intervals), $0) + } } open func verifySignature(msg: [UInt8], sig: String, pkey: PublicKey) -> Bool { @@ -3041,7 +3088,7 @@ open class Offer: { fileprivate let pointer: UnsafeMutableRawPointer! - /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. + // Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -3269,7 +3316,7 @@ open class OnchainPayment: { fileprivate let pointer: UnsafeMutableRawPointer! - /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. + // Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -3483,7 +3530,7 @@ open class Refund: { fileprivate let pointer: UnsafeMutableRawPointer! - /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. + // Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -3685,7 +3732,7 @@ open class SpontaneousPayment: { fileprivate let pointer: UnsafeMutableRawPointer! - /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. + // Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -3738,11 +3785,12 @@ open class SpontaneousPayment: }) } - open func sendProbes(amountMsat: UInt64, nodeId: PublicKey) throws { try rustCallWithError(FfiConverterTypeNodeError.lift) { - uniffi_ldk_node_fn_method_spontaneouspayment_send_probes(self.uniffiClonePointer(), - FfiConverterUInt64.lower(amountMsat), - FfiConverterTypePublicKey.lower(nodeId), $0) - } + open func sendProbes(amountMsat: UInt64, nodeId: PublicKey) throws { + try rustCallWithError(FfiConverterTypeNodeError.lift) { + uniffi_ldk_node_fn_method_spontaneouspayment_send_probes(self.uniffiClonePointer(), + FfiConverterUInt64.lower(amountMsat), + FfiConverterTypePublicKey.lower(nodeId), $0) + } } open func sendWithCustomTlvs(amountMsat: UInt64, nodeId: PublicKey, routeParameters: RouteParametersConfig?, customTlvs: [CustomTlvRecord]) throws -> PaymentId { @@ -3835,7 +3883,7 @@ open class UnifiedQrPayment: { fileprivate let pointer: UnsafeMutableRawPointer! - /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. + // Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -3953,7 +4001,7 @@ open class VssHeaderProvider: { fileprivate let pointer: UnsafeMutableRawPointer! - /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. + // Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -4066,8 +4114,8 @@ public struct AddressTypeBalance { public var totalSats: UInt64 public var spendableSats: UInt64 - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(totalSats: UInt64, spendableSats: UInt64) { self.totalSats = totalSats self.spendableSats = spendableSats @@ -4127,8 +4175,8 @@ public struct AnchorChannelsConfig { public var trustedPeersNoReserve: [PublicKey] public var perChannelReserveSats: UInt64 - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(trustedPeersNoReserve: [PublicKey], perChannelReserveSats: UInt64) { self.trustedPeersNoReserve = trustedPeersNoReserve self.perChannelReserveSats = perChannelReserveSats @@ -4189,8 +4237,8 @@ public struct BackgroundSyncConfig { public var lightningWalletSyncIntervalSecs: UInt64 public var feeRateCacheUpdateIntervalSecs: UInt64 - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(onchainWalletSyncIntervalSecs: UInt64, lightningWalletSyncIntervalSecs: UInt64, feeRateCacheUpdateIntervalSecs: UInt64) { self.onchainWalletSyncIntervalSecs = onchainWalletSyncIntervalSecs self.lightningWalletSyncIntervalSecs = lightningWalletSyncIntervalSecs @@ -4261,8 +4309,8 @@ public struct BalanceDetails { public var lightningBalances: [LightningBalance] public var pendingBalancesFromChannelClosures: [PendingSweepBalance] - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(totalOnchainBalanceSats: UInt64, spendableOnchainBalanceSats: UInt64, totalAnchorChannelsReserveSats: UInt64, totalLightningBalanceSats: UInt64, lightningBalances: [LightningBalance], pendingBalancesFromChannelClosures: [PendingSweepBalance]) { self.totalOnchainBalanceSats = totalOnchainBalanceSats self.spendableOnchainBalanceSats = spendableOnchainBalanceSats @@ -4350,8 +4398,8 @@ public struct BestBlock { public var blockHash: BlockHash public var height: UInt32 - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(blockHash: BlockHash, height: UInt32) { self.blockHash = blockHash self.height = height @@ -4415,8 +4463,8 @@ public struct ChannelConfig { public var forceCloseAvoidanceMaxFeeSatoshis: UInt64 public var acceptUnderpayingHtlcs: Bool - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(forwardingFeeProportionalMillionths: UInt32, forwardingFeeBaseMsat: UInt32, cltvExpiryDelta: UInt16, maxDustHtlcExposure: MaxDustHtlcExposure, forceCloseAvoidanceMaxFeeSatoshis: UInt64, acceptUnderpayingHtlcs: Bool) { self.forwardingFeeProportionalMillionths = forwardingFeeProportionalMillionths self.forwardingFeeBaseMsat = forwardingFeeBaseMsat @@ -4504,8 +4552,8 @@ public struct ChannelDataMigration { public var channelManager: [UInt8]? public var channelMonitors: [[UInt8]] - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(channelManager: [UInt8]?, channelMonitors: [[UInt8]]) { self.channelManager = channelManager self.channelMonitors = channelMonitors @@ -4595,8 +4643,8 @@ public struct ChannelDetails { public var config: ChannelConfig public var claimableOnCloseSats: UInt64? - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(channelId: ChannelId, counterpartyNodeId: PublicKey, fundingTxo: OutPoint?, shortChannelId: UInt64?, outboundScidAlias: UInt64?, inboundScidAlias: UInt64?, channelValueSats: UInt64, unspendablePunishmentReserve: UInt64?, userChannelId: UserChannelId, feerateSatPer1000Weight: UInt32, outboundCapacityMsat: UInt64, inboundCapacityMsat: UInt64, confirmationsRequired: UInt32?, confirmations: UInt32?, isOutbound: Bool, isChannelReady: Bool, isUsable: Bool, isAnnounced: Bool, cltvExpiryDelta: UInt16?, counterpartyUnspendablePunishmentReserve: UInt64, counterpartyOutboundHtlcMinimumMsat: UInt64?, counterpartyOutboundHtlcMaximumMsat: UInt64?, counterpartyForwardingInfoFeeBaseMsat: UInt32?, counterpartyForwardingInfoFeeProportionalMillionths: UInt32?, counterpartyForwardingInfoCltvExpiryDelta: UInt16?, nextOutboundHtlcLimitMsat: UInt64, nextOutboundHtlcMinimumMsat: UInt64, forceCloseSpendDelay: UInt16?, inboundHtlcMinimumMsat: UInt64, inboundHtlcMaximumMsat: UInt64?, config: ChannelConfig, claimableOnCloseSats: UInt64?) { self.channelId = channelId self.counterpartyNodeId = counterpartyNodeId @@ -4869,8 +4917,8 @@ public struct ChannelInfo { public var twoToOne: ChannelUpdateInfo? public var capacitySats: UInt64? - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(nodeOne: NodeId, oneToTwo: ChannelUpdateInfo?, nodeTwo: NodeId, twoToOne: ChannelUpdateInfo?, capacitySats: UInt64?) { self.nodeOne = nodeOne self.oneToTwo = oneToTwo @@ -4955,8 +5003,8 @@ public struct ChannelUpdateInfo { public var htlcMaximumMsat: UInt64 public var fees: RoutingFees - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(lastUpdate: UInt32, enabled: Bool, cltvExpiryDelta: UInt16, htlcMinimumMsat: UInt64, htlcMaximumMsat: UInt64, fees: RoutingFees) { self.lastUpdate = lastUpdate self.enabled = enabled @@ -5054,8 +5102,8 @@ public struct Config { public var addressType: AddressType public var addressTypesToMonitor: [AddressType] - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(storageDirPath: String, network: Network, listeningAddresses: [SocketAddress]?, announcementAddresses: [SocketAddress]?, nodeAlias: NodeAlias?, trustedPeers0conf: [PublicKey], probingLiquidityLimitMultiplier: UInt64, anchorChannelsConfig: AnchorChannelsConfig?, routeParameters: RouteParametersConfig?, includeUntrustedPendingInSpendable: Bool, addressType: AddressType, addressTypesToMonitor: [AddressType]) { self.storageDirPath = storageDirPath self.network = network @@ -5185,8 +5233,8 @@ public struct CustomTlvRecord { public var typeNum: UInt64 public var value: [UInt8] - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(typeNum: UInt64, value: [UInt8]) { self.typeNum = typeNum self.value = value @@ -5246,8 +5294,8 @@ public struct ElectrumSyncConfig { public var backgroundSyncConfig: BackgroundSyncConfig? public var connectionTimeoutSecs: UInt64 - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(backgroundSyncConfig: BackgroundSyncConfig?, connectionTimeoutSecs: UInt64) { self.backgroundSyncConfig = backgroundSyncConfig self.connectionTimeoutSecs = connectionTimeoutSecs @@ -5306,8 +5354,8 @@ public func FfiConverterTypeElectrumSyncConfig_lower(_ value: ElectrumSyncConfig public struct EsploraSyncConfig { public var backgroundSyncConfig: BackgroundSyncConfig? - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(backgroundSyncConfig: BackgroundSyncConfig?) { self.backgroundSyncConfig = backgroundSyncConfig } @@ -5360,8 +5408,8 @@ public struct LspFeeLimits { public var maxTotalOpeningFeeMsat: UInt64? public var maxProportionalOpeningFeePpmMsat: UInt64? - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(maxTotalOpeningFeeMsat: UInt64?, maxProportionalOpeningFeePpmMsat: UInt64?) { self.maxTotalOpeningFeeMsat = maxTotalOpeningFeeMsat self.maxProportionalOpeningFeePpmMsat = maxProportionalOpeningFeePpmMsat @@ -5424,8 +5472,8 @@ public struct Lsps1Bolt11PaymentInfo { public var orderTotalSat: UInt64 public var invoice: Bolt11Invoice - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(state: Lsps1PaymentState, expiresAt: LspsDateTime, feeTotalSat: UInt64, orderTotalSat: UInt64, invoice: Bolt11Invoice) { self.state = state self.expiresAt = expiresAt @@ -5478,8 +5526,8 @@ public struct Lsps1ChannelInfo { public var fundingOutpoint: OutPoint public var expiresAt: LspsDateTime - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(fundedAt: LspsDateTime, fundingOutpoint: OutPoint, expiresAt: LspsDateTime) { self.fundedAt = fundedAt self.fundingOutpoint = fundingOutpoint @@ -5552,8 +5600,8 @@ public struct Lsps1OnchainPaymentInfo { public var minFeeFor0conf: FeeRate public var refundOnchainAddress: Address? - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(state: Lsps1PaymentState, expiresAt: LspsDateTime, feeTotalSat: UInt64, orderTotalSat: UInt64, address: Address, minOnchainPaymentConfirmations: UInt16?, minFeeFor0conf: FeeRate, refundOnchainAddress: Address?) { self.state = state self.expiresAt = expiresAt @@ -5619,8 +5667,8 @@ public struct Lsps1OrderParams { public var token: String? public var announceChannel: Bool - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(lspBalanceSat: UInt64, clientBalanceSat: UInt64, requiredChannelConfirmations: UInt16, fundingConfirmsWithinBlocks: UInt16, channelExpiryBlocks: UInt32, token: String?, announceChannel: Bool) { self.lspBalanceSat = lspBalanceSat self.clientBalanceSat = clientBalanceSat @@ -5717,8 +5765,8 @@ public struct Lsps1OrderStatus { public var paymentOptions: Lsps1PaymentInfo public var channelState: Lsps1ChannelInfo? - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(orderId: Lsps1OrderId, orderParams: Lsps1OrderParams, paymentOptions: Lsps1PaymentInfo, channelState: Lsps1ChannelInfo?) { self.orderId = orderId self.orderParams = orderParams @@ -5767,8 +5815,8 @@ public struct Lsps1PaymentInfo { public var bolt11: Lsps1Bolt11PaymentInfo? public var onchain: Lsps1OnchainPaymentInfo? - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(bolt11: Lsps1Bolt11PaymentInfo?, onchain: Lsps1OnchainPaymentInfo?) { self.bolt11 = bolt11 self.onchain = onchain @@ -5819,8 +5867,8 @@ public struct Lsps2ServiceConfig { public var maxPaymentSizeMsat: UInt64 public var clientTrustsLsp: Bool - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(requireToken: String?, advertiseService: Bool, channelOpeningFeePpm: UInt32, channelOverProvisioningPpm: UInt32, minChannelOpeningFeeMsat: UInt64, minChannelLifetime: UInt32, maxClientToSelfDelay: UInt32, minPaymentSizeMsat: UInt64, maxPaymentSizeMsat: UInt64, clientTrustsLsp: Bool) { self.requireToken = requireToken self.advertiseService = advertiseService @@ -5938,8 +5986,8 @@ public struct LogRecord { public var modulePath: String public var line: UInt32 - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(level: LogLevel, args: String, modulePath: String, line: UInt32) { self.level = level self.args = args @@ -6014,8 +6062,8 @@ public struct NodeAnnouncementInfo { public var alias: String public var addresses: [SocketAddress] - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(lastUpdate: UInt32, alias: String, addresses: [SocketAddress]) { self.lastUpdate = lastUpdate self.alias = alias @@ -6082,8 +6130,8 @@ public struct NodeInfo { public var channels: [UInt64] public var announcementInfo: NodeAnnouncementInfo? - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(channels: [UInt64], announcementInfo: NodeAnnouncementInfo?) { self.channels = channels self.announcementInfo = announcementInfo @@ -6150,8 +6198,8 @@ public struct NodeStatus { public var latestNodeAnnouncementBroadcastTimestamp: UInt64? public var latestChannelMonitorArchivalHeight: UInt32? - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(isRunning: Bool, currentBestBlock: BestBlock, latestLightningWalletSyncTimestamp: UInt64?, latestOnchainWalletSyncTimestamp: UInt64?, latestFeeRateCacheUpdateTimestamp: UInt64?, latestRgsSnapshotTimestamp: UInt64?, latestPathfindingScoresSyncTimestamp: UInt64?, latestNodeAnnouncementBroadcastTimestamp: UInt64?, latestChannelMonitorArchivalHeight: UInt32?) { self.isRunning = isRunning self.currentBestBlock = currentBestBlock @@ -6260,8 +6308,8 @@ public struct OutPoint { public var txid: Txid public var vout: UInt32 - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(txid: Txid, vout: UInt32) { self.txid = txid self.vout = vout @@ -6326,8 +6374,8 @@ public struct PaymentDetails { public var status: PaymentStatus public var latestUpdateTimestamp: UInt64 - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(id: PaymentId, kind: PaymentKind, amountMsat: UInt64?, feePaidMsat: UInt64?, direction: PaymentDirection, status: PaymentStatus, latestUpdateTimestamp: UInt64) { self.id = id self.kind = kind @@ -6424,8 +6472,8 @@ public struct PeerDetails { public var isPersisted: Bool public var isConnected: Bool - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(nodeId: PublicKey, address: SocketAddress, isPersisted: Bool, isConnected: Bool) { self.nodeId = nodeId self.address = address @@ -6503,8 +6551,8 @@ public struct RouteHintHop { public var htlcMaximumMsat: UInt64? public var fees: RoutingFees - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(srcNodeId: PublicKey, shortChannelId: UInt64, cltvExpiryDelta: UInt16, htlcMinimumMsat: UInt64?, htlcMaximumMsat: UInt64?, fees: RoutingFees) { self.srcNodeId = srcNodeId self.shortChannelId = shortChannelId @@ -6594,8 +6642,8 @@ public struct RouteParametersConfig { public var maxPathCount: UInt8 public var maxChannelSaturationPowerOfHalf: UInt8 - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(maxTotalRoutingFeeMsat: UInt64?, maxTotalCltvExpiryDelta: UInt32, maxPathCount: UInt8, maxChannelSaturationPowerOfHalf: UInt8) { self.maxTotalRoutingFeeMsat = maxTotalRoutingFeeMsat self.maxTotalCltvExpiryDelta = maxTotalCltvExpiryDelta @@ -6669,8 +6717,8 @@ public struct RoutingFees { public var baseMsat: UInt32 public var proportionalMillionths: UInt32 - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(baseMsat: UInt32, proportionalMillionths: UInt32) { self.baseMsat = baseMsat self.proportionalMillionths = proportionalMillionths @@ -6731,8 +6779,8 @@ public struct RuntimeSyncIntervals { public var lightningWalletSyncIntervalSecs: UInt64 public var feeRateCacheUpdateIntervalSecs: UInt64 - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(onchainWalletSyncIntervalSecs: UInt64, lightningWalletSyncIntervalSecs: UInt64, feeRateCacheUpdateIntervalSecs: UInt64) { self.onchainWalletSyncIntervalSecs = onchainWalletSyncIntervalSecs self.lightningWalletSyncIntervalSecs = lightningWalletSyncIntervalSecs @@ -6799,8 +6847,8 @@ public struct SpendableUtxo { public var outpoint: OutPoint public var valueSats: UInt64 - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(outpoint: OutPoint, valueSats: UInt64) { self.outpoint = outpoint self.valueSats = valueSats @@ -6861,8 +6909,8 @@ public struct TransactionDetails { public var inputs: [TxInput] public var outputs: [TxOutput] - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(amountSats: Int64, inputs: [TxInput], outputs: [TxOutput]) { self.amountSats = amountSats self.inputs = inputs @@ -6932,8 +6980,8 @@ public struct TxInput { public var witness: [String] public var sequence: UInt32 - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(txid: Txid, vout: UInt32, scriptsig: String, witness: [String], sequence: UInt32) { self.txid = txid self.vout = vout @@ -7017,8 +7065,8 @@ public struct TxOutput { public var value: Int64 public var n: UInt32 - // Default memberwise initializers are never public by default, so we - // declare one manually. + /// Default memberwise initializers are never public by default, so we + /// declare one manually. public init(scriptpubkey: String, scriptpubkeyType: String?, scriptpubkeyAddress: String?, value: Int64, n: UInt32) { self.scriptpubkey = scriptpubkey self.scriptpubkeyType = scriptpubkeyType @@ -7279,10 +7327,8 @@ extension BalanceSource: Equatable, Hashable {} // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. public enum Bolt11InvoiceDescription { - case hash(hash: String - ) - case direct(description: String - ) + case hash(hash: String) + case direct(description: String) } #if swift(>=5.8) @@ -7294,11 +7340,9 @@ public struct FfiConverterTypeBolt11InvoiceDescription: FfiConverterRustBuffer { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Bolt11InvoiceDescription { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return try .hash(hash: FfiConverterString.read(from: &buf) - ) + case 1: return try .hash(hash: FfiConverterString.read(from: &buf)) - case 2: return try .direct(description: FfiConverterString.read(from: &buf) - ) + case 2: return try .direct(description: FfiConverterString.read(from: &buf)) default: throw UniffiInternalError.unexpectedEnumCase } @@ -7494,23 +7538,20 @@ extension BuildError: Foundation.LocalizedError { // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. public enum ClosureReason { - case counterpartyForceClosed(peerMsg: UntrustedString - ) + case counterpartyForceClosed(peerMsg: UntrustedString) case holderForceClosed(broadcastedLatestTxn: Bool?, message: String) case legacyCooperativeClosure case counterpartyInitiatedCooperativeClosure case locallyInitiatedCooperativeClosure case commitmentTxConfirmed case fundingTimedOut - case processingError(err: String - ) + case processingError(err: String) case disconnectedPeer case outdatedChannelManager case counterpartyCoopClosedUnfundedChannel case locallyCoopClosedUnfundedChannel case fundingBatchClosure - case htlCsTimedOut(paymentHash: PaymentHash? - ) + case htlCsTimedOut(paymentHash: PaymentHash?) case peerFeerateTooLow(peerFeerateSatPerKw: UInt32, requiredFeerateSatPerKw: UInt32) } @@ -7523,8 +7564,7 @@ public struct FfiConverterTypeClosureReason: FfiConverterRustBuffer { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ClosureReason { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return try .counterpartyForceClosed(peerMsg: FfiConverterTypeUntrustedString.read(from: &buf) - ) + case 1: return try .counterpartyForceClosed(peerMsg: FfiConverterTypeUntrustedString.read(from: &buf)) case 2: return try .holderForceClosed(broadcastedLatestTxn: FfiConverterOptionBool.read(from: &buf), message: FfiConverterString.read(from: &buf)) @@ -7538,8 +7578,7 @@ public struct FfiConverterTypeClosureReason: FfiConverterRustBuffer { case 7: return .fundingTimedOut - case 8: return try .processingError(err: FfiConverterString.read(from: &buf) - ) + case 8: return try .processingError(err: FfiConverterString.read(from: &buf)) case 9: return .disconnectedPeer @@ -7551,8 +7590,7 @@ public struct FfiConverterTypeClosureReason: FfiConverterRustBuffer { case 13: return .fundingBatchClosure - case 14: return try .htlCsTimedOut(paymentHash: FfiConverterOptionTypePaymentHash.read(from: &buf) - ) + case 14: return try .htlCsTimedOut(paymentHash: FfiConverterOptionTypePaymentHash.read(from: &buf)) case 15: return try .peerFeerateTooLow(peerFeerateSatPerKw: FfiConverterUInt32.read(from: &buf), requiredFeerateSatPerKw: FfiConverterUInt32.read(from: &buf)) @@ -7839,10 +7877,8 @@ public enum Event { case onchainTransactionConfirmed(txid: Txid, blockHash: BlockHash, blockHeight: UInt32, confirmationTime: UInt64, details: TransactionDetails) case onchainTransactionReceived(txid: Txid, details: TransactionDetails) case onchainTransactionReplaced(txid: Txid, conflicts: [Txid]) - case onchainTransactionReorged(txid: Txid - ) - case onchainTransactionEvicted(txid: Txid - ) + case onchainTransactionReorged(txid: Txid) + case onchainTransactionEvicted(txid: Txid) case syncProgress(syncType: SyncType, progressPercent: UInt8, currentBlockHeight: UInt32, targetBlockHeight: UInt32) case syncCompleted(syncType: SyncType, syncedBlockHeight: UInt32) case balanceChanged(oldSpendableOnchainBalanceSats: UInt64, newSpendableOnchainBalanceSats: UInt64, oldTotalOnchainBalanceSats: UInt64, newTotalOnchainBalanceSats: UInt64, oldTotalLightningBalanceSats: UInt64, newTotalLightningBalanceSats: UInt64) @@ -7883,11 +7919,9 @@ public struct FfiConverterTypeEvent: FfiConverterRustBuffer { case 13: return try .onchainTransactionReplaced(txid: FfiConverterTypeTxid.read(from: &buf), conflicts: FfiConverterSequenceTypeTxid.read(from: &buf)) - case 14: return try .onchainTransactionReorged(txid: FfiConverterTypeTxid.read(from: &buf) - ) + case 14: return try .onchainTransactionReorged(txid: FfiConverterTypeTxid.read(from: &buf)) - case 15: return try .onchainTransactionEvicted(txid: FfiConverterTypeTxid.read(from: &buf) - ) + case 15: return try .onchainTransactionEvicted(txid: FfiConverterTypeTxid.read(from: &buf)) case 16: return try .syncProgress(syncType: FfiConverterTypeSyncType.read(from: &buf), progressPercent: FfiConverterUInt8.read(from: &buf), currentBlockHeight: FfiConverterUInt32.read(from: &buf), targetBlockHeight: FfiConverterUInt32.read(from: &buf)) @@ -8291,10 +8325,8 @@ extension LogLevel: Equatable, Hashable {} // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. public enum MaxDustHtlcExposure { - case fixedLimit(limitMsat: UInt64 - ) - case feeRateMultiplier(multiplier: UInt64 - ) + case fixedLimit(limitMsat: UInt64) + case feeRateMultiplier(multiplier: UInt64) } #if swift(>=5.8) @@ -8306,11 +8338,9 @@ public struct FfiConverterTypeMaxDustHTLCExposure: FfiConverterRustBuffer { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MaxDustHtlcExposure { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return try .fixedLimit(limitMsat: FfiConverterUInt64.read(from: &buf) - ) + case 1: return try .fixedLimit(limitMsat: FfiConverterUInt64.read(from: &buf)) - case 2: return try .feeRateMultiplier(multiplier: FfiConverterUInt64.read(from: &buf) - ) + case 2: return try .feeRateMultiplier(multiplier: FfiConverterUInt64.read(from: &buf)) default: throw UniffiInternalError.unexpectedEnumCase } @@ -8978,8 +9008,7 @@ extension NodeError: Foundation.LocalizedError { // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. public enum OfferAmount { - case bitcoin(amountMsats: UInt64 - ) + case bitcoin(amountMsats: UInt64) case currency(iso4217Code: String, amount: UInt64) } @@ -8992,8 +9021,7 @@ public struct FfiConverterTypeOfferAmount: FfiConverterRustBuffer { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> OfferAmount { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return try .bitcoin(amountMsats: FfiConverterUInt64.read(from: &buf) - ) + case 1: return try .bitcoin(amountMsats: FfiConverterUInt64.read(from: &buf)) case 2: return try .currency(iso4217Code: FfiConverterString.read(from: &buf), amount: FfiConverterUInt64.read(from: &buf)) @@ -9417,12 +9445,9 @@ extension PendingSweepBalance: Equatable, Hashable {} // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. public enum QrPaymentResult { - case onchain(txid: Txid - ) - case bolt11(paymentId: PaymentId - ) - case bolt12(paymentId: PaymentId - ) + case onchain(txid: Txid) + case bolt11(paymentId: PaymentId) + case bolt12(paymentId: PaymentId) } #if swift(>=5.8) @@ -9434,14 +9459,11 @@ public struct FfiConverterTypeQrPaymentResult: FfiConverterRustBuffer { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> QrPaymentResult { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return try .onchain(txid: FfiConverterTypeTxid.read(from: &buf) - ) + case 1: return try .onchain(txid: FfiConverterTypeTxid.read(from: &buf)) - case 2: return try .bolt11(paymentId: FfiConverterTypePaymentId.read(from: &buf) - ) + case 2: return try .bolt11(paymentId: FfiConverterTypePaymentId.read(from: &buf)) - case 3: return try .bolt12(paymentId: FfiConverterTypePaymentId.read(from: &buf) - ) + case 3: return try .bolt12(paymentId: FfiConverterTypePaymentId.read(from: &buf)) default: throw UniffiInternalError.unexpectedEnumCase } @@ -12076,8 +12098,8 @@ private func uniffiRustCallAsync( )) } -// Callback handlers for an async calls. These are invoked by Rust when the future is ready. They -// lift the return value or error and resume the suspended function. +/// Callback handlers for an async calls. These are invoked by Rust when the future is ready. They +/// lift the return value or error and resume the suspended function. private func uniffiFutureContinuationCallback(handle: UInt64, pollResult: Int8) { if let continuation = try? uniffiContinuationHandleMap.remove(handle: handle) { continuation.resume(returning: pollResult) @@ -12088,15 +12110,13 @@ private func uniffiFutureContinuationCallback(handle: UInt64, pollResult: Int8) public func batterySavingSyncIntervals() -> RuntimeSyncIntervals { return try! FfiConverterTypeRuntimeSyncIntervals.lift(try! rustCall { - uniffi_ldk_node_fn_func_battery_saving_sync_intervals($0 - ) + uniffi_ldk_node_fn_func_battery_saving_sync_intervals($0) }) } public func defaultConfig() -> Config { return try! FfiConverterTypeConfig.lift(try! rustCall { - uniffi_ldk_node_fn_func_default_config($0 - ) + uniffi_ldk_node_fn_func_default_config($0) }) } @@ -12123,8 +12143,8 @@ private enum InitializationResult { case apiChecksumMismatch } -// Use a global variable to perform the versioning checks. Swift ensures that -// the code inside is only computed once. +/// Use a global variable to perform the versioning checks. Swift ensures that +/// the code inside is only computed once. private var initializationResult: InitializationResult = { // Get the bindings contract version from our ComponentInterface let bindings_contract_version = 26 diff --git a/src/builder.rs b/src/builder.rs index 93ee47957..307d6b4e4 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -32,7 +32,7 @@ use lightning::routing::scoring::{ ChannelLiquidities, CombinedScorer, ProbabilisticScorer, ProbabilisticScoringDecayParameters, ProbabilisticScoringFeeParameters, }; -use lightning::sign::{EntropySource, InMemorySigner, NodeSigner}; +use lightning::sign::{EntropySource, InMemorySigner, NodeSigner, SignerProvider}; use lightning::util::persist::{ KVStore, KVStoreSync, CHANNEL_MANAGER_PERSISTENCE_KEY, CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE, CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE, @@ -43,7 +43,7 @@ use lightning::util::persist::{ }; use lightning::util::ser::{Readable, ReadableArgs}; use lightning::util::sweep::OutputSweeper; -use lightning::{log_info, log_trace}; +use lightning::{log_info, log_trace, log_warn}; use lightning_persister::fs_store::FilesystemStore; use vss_client::headers::{FixedHeaders, LnurlAuthToJwtProvider, VssHeaderProvider}; @@ -1339,6 +1339,164 @@ fn build_additional_wallets( additional_wallets } +fn apply_channel_data_migration( + migration: &ChannelDataMigration, kv_store: &Arc, keys_manager: &K, + logger: &Arc, runtime: &Arc, +) -> Result<(), BuildError> +where + K: EntropySource + SignerProvider, +{ + if let Some(manager_bytes) = &migration.channel_manager { + // Existence-only check (no deserialization). Unlike monitors, we cannot + // deserialize the channel manager here because ChannelManagerReadArgs + // requires already-deserialized channel_monitor_references, which are + // loaded later in build(). This means corrupt existing data will block + // migration — that is intentional: overwriting a potentially valid + // channel manager with stale FS data risks fund loss, while a node that + // refuses to start can be recovered by clearing the KV store. + let should_write = match runtime.block_on(KVStore::read( + &**kv_store, + CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE, + CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE, + CHANNEL_MANAGER_PERSISTENCE_KEY, + )) { + Ok(_existing_data) => { + log_warn!( + logger, + "Skipping channel manager migration: store already contains a channel manager" + ); + false + }, + Err(e) if e.kind() == lightning::io::ErrorKind::NotFound => true, + Err(e) => { + log_error!( + logger, + "Failed to read existing channel manager, refusing migration write \ + to avoid overwriting potentially newer state: {}", + e + ); + return Err(BuildError::ReadFailed); + }, + }; + + if should_write { + runtime + .block_on(async { + KVStore::write( + &**kv_store, + CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE, + CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE, + CHANNEL_MANAGER_PERSISTENCE_KEY, + manager_bytes.clone(), + ) + .await + }) + .map_err(|e| { + log_error!(logger, "Failed to write migrated channel_manager: {}", e); + BuildError::WriteFailed + })?; + } + } + + for monitor_data in &migration.channel_monitors { + let mut reader = lightning::io::Cursor::new(monitor_data); + let (_, channel_monitor) = match <(BlockHash, ChannelMonitor)>::read( + &mut reader, + (keys_manager, keys_manager), + ) { + Ok(monitor) => monitor, + Err(e) => { + log_error!(logger, "Failed to deserialize channel_monitor: {:?}", e); + return Err(BuildError::ReadFailed); + }, + }; + + let funding_txo = channel_monitor.get_funding_txo(); + let monitor_key = format!("{}_{}", funding_txo.txid, funding_txo.index); + let migrated_update_id = channel_monitor.get_latest_update_id(); + log_info!( + logger, + "Migrating channel monitor: {} (update_id={})", + monitor_key, + migrated_update_id + ); + + // Check if the store already has a newer monitor to avoid overwriting + // current state with stale migration data. + let should_write = match runtime.block_on(KVStore::read( + &**kv_store, + CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, + CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, + &monitor_key, + )) { + Ok(existing_data) => { + let mut existing_reader = Cursor::new(&existing_data); + match <(BlockHash, ChannelMonitor)>::read( + &mut existing_reader, + (keys_manager, keys_manager), + ) { + Ok((_, existing_monitor)) => { + let existing_update_id = existing_monitor.get_latest_update_id(); + if existing_update_id >= migrated_update_id { + log_warn!( + logger, + "Skipping migration for monitor {}: existing update_id {} is newer than or equal to migrated update_id {}", + monitor_key, + existing_update_id, + migrated_update_id + ); + false + } else { + true + } + }, + Err(e) => { + log_error!( + logger, + "Failed to deserialize existing monitor {}, refusing migration write to avoid overwriting potentially newer state: {:?}", + monitor_key, + e + ); + return Err(BuildError::ReadFailed); + }, + } + }, + Err(e) if e.kind() == lightning::io::ErrorKind::NotFound => true, + Err(e) => { + log_error!( + logger, + "Failed to read existing monitor {}, refusing migration write to avoid overwriting potentially newer state: {}", + monitor_key, + e + ); + return Err(BuildError::ReadFailed); + }, + }; + + if should_write { + runtime + .block_on(async { + KVStore::write( + &**kv_store, + CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, + CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, + &monitor_key, + monitor_data.clone(), + ) + .await + }) + .map_err(|e| { + log_error!(logger, "Failed to write channel_monitor {}: {}", monitor_key, e); + BuildError::WriteFailed + })?; + } + } + + log_info!(logger, "Applied channel migration: {} monitors", migration.channel_monitors.len()); + + Ok(()) +} + fn build_with_store_internal( config: Arc, chain_data_source_config: Option<&ChainDataSourceConfig>, gossip_source_config: Option<&GossipSourceConfig>, @@ -1606,63 +1764,7 @@ fn build_with_store_internal( )); if let Some(migration) = channel_data_migration { - if let Some(manager_bytes) = &migration.channel_manager { - runtime - .block_on(async { - KVStore::write( - &*kv_store, - CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE, - CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE, - CHANNEL_MANAGER_PERSISTENCE_KEY, - manager_bytes.clone(), - ) - .await - }) - .map_err(|e| { - log_error!(logger, "Failed to write migrated channel_manager: {}", e); - BuildError::WriteFailed - })?; - } - - for monitor_data in &migration.channel_monitors { - let mut reader = lightning::io::Cursor::new(monitor_data); - let (_, channel_monitor) = match <(BlockHash, ChannelMonitor)>::read( - &mut reader, - (&*keys_manager, &*keys_manager), - ) { - Ok(monitor) => monitor, - Err(e) => { - log_error!(logger, "Failed to deserialize channel_monitor: {:?}", e); - return Err(BuildError::ReadFailed); - }, - }; - - let funding_txo = channel_monitor.get_funding_txo(); - let monitor_key = format!("{}_{}", funding_txo.txid, funding_txo.index); - log_info!(logger, "Migrating channel monitor: {}", monitor_key); - - runtime - .block_on(async { - KVStore::write( - &*kv_store, - CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, - CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, - &monitor_key, - monitor_data.clone(), - ) - .await - }) - .map_err(|e| { - log_error!(logger, "Failed to write channel_monitor {}: {}", monitor_key, e); - BuildError::WriteFailed - })?; - } - - log_info!( - logger, - "Applied channel migration: {} monitors", - migration.channel_monitors.len() - ); + apply_channel_data_migration(migration, &kv_store, &*keys_manager, &logger, &runtime)?; } // Initialize the network graph from local cache, with VSS fallback for migration. @@ -2358,7 +2460,32 @@ pub(crate) fn sanitize_alias(alias_str: &str) -> Result { #[cfg(test)] mod tests { - use super::{sanitize_alias, BuildError, NodeAlias}; + use std::future::Future; + use std::pin::Pin; + use std::sync::Arc; + + use lightning::io; + use lightning::ln::functional_test_utils::{ + create_announced_chan_between_nodes, create_chanmon_cfgs, create_network, create_node_cfgs, + create_node_chanmgrs, + }; + use lightning::sign::KeysManager as LdkKeysManager; + use lightning::util::persist::{ + KVStore, KVStoreSync, CHANNEL_MANAGER_PERSISTENCE_KEY, + CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE, + CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE, + CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, + CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, + }; + use lightning::util::ser::Writeable; + + use super::{ + apply_channel_data_migration, sanitize_alias, BuildError, ChannelDataMigration, NodeAlias, + }; + use crate::io::test_utils::InMemoryStore; + use crate::logger::Logger; + use crate::runtime::Runtime; + use crate::types::DynStore; #[test] fn sanitize_empty_node_alias() { @@ -2395,4 +2522,349 @@ mod tests { let node = sanitize_alias(alias); assert_eq!(node.err().unwrap(), BuildError::InvalidNodeAlias); } + + /// Creates valid serialized (BlockHash, ChannelMonitor) bytes for testing. + /// + /// Uses LDK's functional test utilities to create a real channel between two + /// nodes, then serializes one of the resulting channel monitors. Returns the + /// seed used for node 0's KeysManager so callers can create a matching + /// KeysManager for deserialization. + /// + /// Returns (monitor_bytes, monitor_key, update_id, seed). + fn create_test_monitor_bytes() -> (Vec, String, u64, [u8; 32]) { + let chanmon_cfgs = create_chanmon_cfgs(2); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); + let nodes = create_network(2, &node_cfgs, &node_chanmgrs); + + let _ = create_announced_chan_between_nodes(&nodes, 0, 1); + + let channel_id = nodes[0].node.list_channels()[0].channel_id; + let monitor = nodes[0].chain_monitor.chain_monitor.get_monitor(channel_id).unwrap(); + + let funding_txo = monitor.get_funding_txo(); + let monitor_key = format!("{}_{}", funding_txo.txid, funding_txo.index); + let update_id = monitor.get_latest_update_id(); + + let seed = nodes[0].node_seed; + let mut serialized = Vec::new(); + monitor.write(&mut serialized).unwrap(); + + (serialized, monitor_key, update_id, seed) + } + + fn make_test_deps( + seed: &[u8; 32], + ) -> (Arc, LdkKeysManager, Arc, Arc) { + let store: Arc = Arc::new(InMemoryStore::new()); + let keys_manager = LdkKeysManager::new(seed, 0, 0, false); + let logger = Arc::new(Logger::new_log_facade()); + let runtime = Arc::new(Runtime::new(Arc::clone(&logger)).unwrap()); + (store, keys_manager, logger, runtime) + } + + /// A KVStore wrapper that returns IO errors for reads from the channel + /// monitor namespace, simulating a storage backend failure. + struct FailingReadStore; + + impl KVStore for FailingReadStore { + fn read( + &self, _primary_namespace: &str, _secondary_namespace: &str, _key: &str, + ) -> Pin, io::Error>> + 'static + Send>> { + Box::pin(async { + Err(io::Error::new(io::ErrorKind::ConnectionReset, "simulated IO error")) + }) + } + + fn write( + &self, _primary_namespace: &str, _secondary_namespace: &str, _key: &str, _buf: Vec, + ) -> Pin> + 'static + Send>> { + Box::pin(async { Ok(()) }) + } + + fn remove( + &self, _primary_namespace: &str, _secondary_namespace: &str, _key: &str, _lazy: bool, + ) -> Pin> + 'static + Send>> { + Box::pin(async { Ok(()) }) + } + + fn list( + &self, _primary_namespace: &str, _secondary_namespace: &str, + ) -> Pin, io::Error>> + 'static + Send>> { + Box::pin(async { Ok(Vec::new()) }) + } + } + + impl KVStoreSync for FailingReadStore { + fn read( + &self, _primary_namespace: &str, _secondary_namespace: &str, _key: &str, + ) -> io::Result> { + Err(io::Error::new(io::ErrorKind::ConnectionReset, "simulated IO error")) + } + + fn write( + &self, _primary_namespace: &str, _secondary_namespace: &str, _key: &str, _buf: Vec, + ) -> io::Result<()> { + Ok(()) + } + + fn remove( + &self, _primary_namespace: &str, _secondary_namespace: &str, _key: &str, _lazy: bool, + ) -> io::Result<()> { + Ok(()) + } + + fn list( + &self, _primary_namespace: &str, _secondary_namespace: &str, + ) -> io::Result> { + Ok(Vec::new()) + } + } + + unsafe impl Sync for FailingReadStore {} + unsafe impl Send for FailingReadStore {} + + #[test] + fn test_migration_invalid_monitor_data_returns_read_failed() { + let (store, keys_manager, logger, runtime) = make_test_deps(&[42u8; 32]); + let migration = ChannelDataMigration { + channel_manager: None, + channel_monitors: vec![vec![0xDE, 0xAD, 0xBE, 0xEF]], + }; + + let result = + apply_channel_data_migration(&migration, &store, &keys_manager, &logger, &runtime); + assert_eq!(result, Err(BuildError::ReadFailed)); + } + + #[test] + fn test_migration_empty_monitors_succeeds() { + let (store, keys_manager, logger, runtime) = make_test_deps(&[42u8; 32]); + let migration = + ChannelDataMigration { channel_manager: None, channel_monitors: Vec::new() }; + + let result = + apply_channel_data_migration(&migration, &store, &keys_manager, &logger, &runtime); + assert!(result.is_ok()); + } + + #[test] + fn test_migration_fresh_write_to_empty_store() { + let (monitor_bytes, monitor_key, _, seed) = create_test_monitor_bytes(); + let (store, keys_manager, logger, runtime) = make_test_deps(&seed); + + let migration = ChannelDataMigration { + channel_manager: None, + channel_monitors: vec![monitor_bytes.clone()], + }; + + let result = + apply_channel_data_migration(&migration, &store, &keys_manager, &logger, &runtime); + assert!(result.is_ok()); + + // Verify the monitor was written to the store. + let stored = runtime.block_on(KVStore::read( + &*store, + CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, + CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, + &monitor_key, + )); + assert!(stored.is_ok()); + assert_eq!(stored.unwrap(), monitor_bytes); + } + + #[test] + fn test_migration_skips_when_existing_is_newer_or_equal() { + let (monitor_bytes, monitor_key, _, seed) = create_test_monitor_bytes(); + let (store, keys_manager, logger, runtime) = make_test_deps(&seed); + + // Pre-populate the store with valid monitor data (update_id=0). + // Migration data also has update_id=0. Since existing >= migrated, + // the function should skip the redundant write and succeed. + runtime + .block_on(KVStore::write( + &*store, + CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, + CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, + &monitor_key, + monitor_bytes.clone(), + )) + .unwrap(); + + let migration = ChannelDataMigration { + channel_manager: None, + channel_monitors: vec![monitor_bytes.clone()], + }; + + let result = + apply_channel_data_migration(&migration, &store, &keys_manager, &logger, &runtime); + assert!(result.is_ok()); + } + + #[test] + fn test_migration_fails_on_corrupt_existing_data() { + let (monitor_bytes, monitor_key, _, seed) = create_test_monitor_bytes(); + let (store, keys_manager, logger, runtime) = make_test_deps(&seed); + + // Pre-populate the store with garbage data for this monitor key. + runtime + .block_on(KVStore::write( + &*store, + CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, + CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, + &monitor_key, + vec![0xFF, 0xFE, 0xFD, 0xFC], + )) + .unwrap(); + + // Migration should fail because the existing data can't be deserialized + // (fail-closed to avoid overwriting potentially newer state). + let migration = + ChannelDataMigration { channel_manager: None, channel_monitors: vec![monitor_bytes] }; + + let result = + apply_channel_data_migration(&migration, &store, &keys_manager, &logger, &runtime); + assert_eq!(result, Err(BuildError::ReadFailed)); + } + + #[test] + fn test_migration_fails_on_store_read_error() { + let (monitor_bytes, _, _, seed) = create_test_monitor_bytes(); + + let store: Arc = Arc::new(FailingReadStore); + let keys_manager = LdkKeysManager::new(&seed, 0, 0, false); + let logger = Arc::new(Logger::new_log_facade()); + let runtime = Arc::new(Runtime::new(Arc::clone(&logger)).unwrap()); + + // Migration should fail because the store returns an IO error on read + // (fail-closed: non-NotFound errors refuse migration). + let migration = + ChannelDataMigration { channel_manager: None, channel_monitors: vec![monitor_bytes] }; + + let result = + apply_channel_data_migration(&migration, &store, &keys_manager, &logger, &runtime); + assert_eq!(result, Err(BuildError::ReadFailed)); + } + + #[test] + fn test_channel_manager_migration_fresh_write_to_empty_store() { + let (store, keys_manager, logger, runtime) = make_test_deps(&[42u8; 32]); + let manager_bytes = vec![0x01, 0x02, 0x03, 0x04]; + + let migration = ChannelDataMigration { + channel_manager: Some(manager_bytes.clone()), + channel_monitors: Vec::new(), + }; + + let result = + apply_channel_data_migration(&migration, &store, &keys_manager, &logger, &runtime); + assert!(result.is_ok()); + + // Verify the channel manager was written to the store. + let stored = runtime.block_on(KVStore::read( + &*store, + CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE, + CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE, + CHANNEL_MANAGER_PERSISTENCE_KEY, + )); + assert!(stored.is_ok()); + assert_eq!(stored.unwrap(), manager_bytes); + } + + #[test] + fn test_channel_manager_migration_skips_when_existing_data_present() { + let (store, keys_manager, logger, runtime) = make_test_deps(&[42u8; 32]); + + // Pre-populate the store with existing channel manager data. + let existing_data = vec![0xAA, 0xBB, 0xCC]; + runtime + .block_on(KVStore::write( + &*store, + CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE, + CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE, + CHANNEL_MANAGER_PERSISTENCE_KEY, + existing_data.clone(), + )) + .unwrap(); + + // Attempt migration with different data. + let migration_bytes = vec![0x01, 0x02, 0x03]; + let migration = ChannelDataMigration { + channel_manager: Some(migration_bytes), + channel_monitors: Vec::new(), + }; + + let result = + apply_channel_data_migration(&migration, &store, &keys_manager, &logger, &runtime); + assert!(result.is_ok()); + + // Verify the original data is preserved (migration was skipped). + let stored = runtime + .block_on(KVStore::read( + &*store, + CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE, + CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE, + CHANNEL_MANAGER_PERSISTENCE_KEY, + )) + .unwrap(); + assert_eq!(stored, existing_data); + } + + #[test] + fn test_channel_manager_migration_skips_even_when_existing_data_is_corrupt() { + let (store, keys_manager, logger, runtime) = make_test_deps(&[42u8; 32]); + + // Pre-populate the store with corrupt/garbage data. + let corrupt_data = vec![0xDE, 0xAD, 0xBE, 0xEF]; + runtime + .block_on(KVStore::write( + &*store, + CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE, + CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE, + CHANNEL_MANAGER_PERSISTENCE_KEY, + corrupt_data.clone(), + )) + .unwrap(); + + // Migration should skip (existence-only check, no deserialization). + // This is intentional: overwriting potentially valid data with stale + // FS bytes risks fund loss; a node that fails to start on corrupt + // data can be recovered by clearing the KV store. + let migration = ChannelDataMigration { + channel_manager: Some(vec![0x01, 0x02, 0x03]), + channel_monitors: Vec::new(), + }; + + let result = + apply_channel_data_migration(&migration, &store, &keys_manager, &logger, &runtime); + assert!(result.is_ok()); + + // Verify the corrupt data is preserved (migration was skipped). + let stored = runtime + .block_on(KVStore::read( + &*store, + CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE, + CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE, + CHANNEL_MANAGER_PERSISTENCE_KEY, + )) + .unwrap(); + assert_eq!(stored, corrupt_data); + } + + #[test] + fn test_channel_manager_migration_fails_on_store_read_error() { + let store: Arc = Arc::new(FailingReadStore); + let keys_manager = LdkKeysManager::new(&[42u8; 32], 0, 0, false); + let logger = Arc::new(Logger::new_log_facade()); + let runtime = Arc::new(Runtime::new(Arc::clone(&logger)).unwrap()); + + let migration = ChannelDataMigration { + channel_manager: Some(vec![0x01, 0x02, 0x03]), + channel_monitors: Vec::new(), + }; + + let result = + apply_channel_data_migration(&migration, &store, &keys_manager, &logger, &runtime); + assert_eq!(result, Err(BuildError::ReadFailed)); + } }