diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index 76cba4cf8..e4cb4e62b 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -50,6 +50,6 @@ jobs: claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} plugin_marketplaces: 'https://github.com/anthropics/claude-code.git' plugins: 'code-review@claude-code-plugins' - prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}' - # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md - # or https://code.claude.com/docs/en/cli-reference for available options + prompt: '/code-review:code-review --comment ${{ github.repository }}/pull/${{ github.event.pull_request.number }}' + claude_args: | + --allowedTools "Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh api:*),Bash(git log:*),Bash(git diff:*),Bash(git blame:*),Read,Glob,Grep" diff --git a/.gitignore b/.gitignore index eafe12832..d66282f25 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,11 @@ swift.swiftdoc # Ignore LDKNodeFFI.xcframework files /bindings/swift/LDKNodeFFI.xcframework +# Build artifacts +*.o +*.a +*.d + # IDE and local files .idea .build diff --git a/AGENTS.md b/AGENTS.md index 92c91141f..8af49d29a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -54,18 +54,11 @@ cargo clippy --fix ### Language Bindings ```bash -# Generate Kotlin bindings -./scripts/uniffi_bindgen_generate_kotlin.sh - -# Generate Android bindings -./scripts/uniffi_bindgen_generate_kotlin_android.sh - -# Generate Python bindings -./scripts/uniffi_bindgen_generate_python.sh - -# Generate Swift bindings -./scripts/uniffi_bindgen_generate_swift.sh +# Generate ALL bindings (Swift, Kotlin, Python) + xcframework archive +./bindgen.sh ``` +Individual scripts live under `scripts/` but should NOT be run directly — +`bindgen.sh` installs shared tooling once and sets the correct build profile. ## Architecture @@ -122,7 +115,17 @@ You are an extremely strict senior Rust systems engineer with 15+ years shipping Your job is not just to write or review code — it is to deliver code that would pass a full Trail of Bits + Rust unsafe + Jepsen-level audit on the first try. -Follow this exact multi-stage process and never skip or summarize any stage: +Follow this exact multi-stage process and never skip or summarize any stage, with +one exception for automated CI reviews (see below): + +**Automated CI code reviews (GitHub Actions):** When running as the `code-review` +plugin in GitHub Actions, build and test tools (Bash, cargo, etc.) are unavailable. +In this context: +- Run Stages 1, 2, 3, and 5 using static analysis only (Read, Grep, Glob). +- Skip Stage 4 (Testing) and Stage 6 (Build & CI Verification) entirely. + Do NOT attempt to run cargo, build, or test commands. +- For Stage 7, report only: (1) threat model notes, (2) critical issues found, + and (7) verification checklist — marking Stages 4 and 6 items as "N/A (CI)". Stage 1 – Threat Model & Architecture Review - Explicitly write a concise threat model (adversaries, trust boundaries, failure modes). @@ -187,7 +190,8 @@ Only after completing all stages above, output in this exact order: - Architecture is minimal and correct Never say "trust me" or "in practice this would pass". You must demonstrate everything above explicitly. -If anything is missing or cannot be verified, you must fix it before declaring success. +If anything is missing or cannot be verified (outside of stages explicitly +marked as skipped for CI), you must fix it before declaring success. --- ## RULES @@ -196,7 +200,7 @@ If anything is missing or cannot be verified, you must fix it before declaring s - 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) -- NEVER run binding generation scripts yourself - always ask the user to run them (they are long-running and resource-intensive) +- Run `./bindgen.sh` in the background when bindings need regeneration (it is long-running) ## Bindings Generation Command To regenerate ALL bindings (Swift, Kotlin, Python), run from the repo root: @@ -215,21 +219,33 @@ When bumping the version, ALWAYS update ALL of these files: ## CHANGELOG - The Synonym fork maintains a SINGLE section at the top: `# X.X.X (Synonym Fork)` +- This section is **cumulative** — it contains ALL changes across ALL rc versions for this fork - When bumping version, update the version in the existing heading (don't create new sections) - All Synonym fork additions go under ONE `## Synonym Fork Additions` subsection - New additions should be added at the TOP of the Synonym Fork Additions list - Do NOT create separate sections for each rc version -- Use the CHANGELOG content as the GitHub release notes body +- **GitHub release notes are NOT the full CHANGELOG** — see PR Release Workflow below ## PR Release Workflow - For PRs that bump version, ALWAYS create a release on the PR branch BEFORE merge +- **CRITICAL: Commit and push ALL changes to the branch BEFORE tagging.** The tag must + point to a commit that contains every change included in the release (CHANGELOG, version + bumps, bindings, etc.). Never tag uncommitted or unpushed work. - Tag the last commit on the PR branch with the version from Cargo.toml (e.g., `v0.7.0-rc.6`) - **CRITICAL: Before uploading `LDKNodeFFI.xcframework.zip`, ALWAYS verify the checksum matches `Package.swift`:** ```bash shasum -a 256 bindings/swift/LDKNodeFFI.xcframework.zip # Compare output with the checksum value in Package.swift - they MUST match ``` -- Create GitHub release with same name as the tag, upload `LDKNodeFFI.xcframework.zip` +- Create GitHub release as **published** (not draft) and **set as latest release**, with + same name as the tag, upload `LDKNodeFFI.xcframework.zip` +- **Release notes = DELTA only.** Describe only what changed since the previous release + (e.g., rc.31 notes list only changes since rc.30). Do NOT paste the full cumulative + CHANGELOG section — that covers all rc versions combined. Write concise notes covering + the PR's changes: bug fixes, features, optimizations, and binding/version updates. +- Release notes are for **consumers of the bindings** — cover only Rust code changes, + FFI changes, and binding updates. Do NOT include internal CI, documentation, or + workflow changes. - **ALWAYS add release link at the end of PR description** (use `gh pr edit` to update the body): ``` ### Release diff --git a/CHANGELOG.md b/CHANGELOG.md index fbb406d22..d92039872 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,12 @@ -# 0.7.0-rc.29 (Synonym Fork) +# 0.7.0-rc.32 (Synonym Fork) ## Bug Fixes +- Fixed cumulative change-address derivation index leak during fee estimation and dry-run + transaction builds. BDK's `TxBuilder::finish()` advances the internal (change) keychain index + each time it's called; repeated fee estimations would burn through change addresses without + broadcasting any transaction. All dry-run and error-after-`finish()` paths now cancel the PSBT + to unmark the change address for reuse. - Bumped `FEE_RATE_CACHE_UPDATE_TIMEOUT_SECS` and `TX_BROADCAST_TIMEOUT_SECS` from 5s to 15s. The 5s node-level timeout fires before Electrum can complete a request (10s timeout), causing `FeerateEstimationUpdateTimeout` on node start. diff --git a/Cargo.toml b/Cargo.toml index 1be9ce098..515bcbcd2 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ exclude = ["bindings/uniffi-bindgen"] [package] name = "ldk-node" -version = "0.7.0-rc.29" +version = "0.7.0-rc.32" authors = ["Elias Rohrer "] homepage = "https://lightningdevkit.org/" license = "MIT OR Apache-2.0" diff --git a/Package.swift b/Package.swift index 56b3ac464..0d5a264ab 100644 --- a/Package.swift +++ b/Package.swift @@ -3,8 +3,8 @@ import PackageDescription -let tag = "v0.7.0-rc.30" -let checksum = "61f686901875529cb54850846283ce709e17b2500a1728b51d629ad03298a641" +let tag = "v0.7.0-rc.32" +let checksum = "89d987f390e0f8fae370c6d549aacd8b8e1c3c95fb0d6e9ccfcf216cbd263b38" let url = "https://github.com/synonymdev/ldk-node/releases/download/\(tag)/LDKNodeFFI.xcframework.zip" let package = Package( diff --git a/bindgen.sh b/bindgen.sh index a6fb8345b..4af5bde6e 100644 --- a/bindgen.sh +++ b/bindgen.sh @@ -1,8 +1,14 @@ #!/usr/bin/env bash set -euo pipefail -RUSTFLAGS="--cfg no_download" cargo build \ - && ./scripts/uniffi_bindgen_generate.sh \ - && ./scripts/swift_create_xcframework_archive.sh \ - && sh scripts/uniffi_bindgen_generate_kotlin.sh \ - && sh scripts/uniffi_bindgen_generate_kotlin_android.sh +# Install gobley-uniffi-bindgen once for all Kotlin scripts +echo "Installing gobley-uniffi-bindgen fork..." +cargo install --git https://github.com/ovitrif/gobley.git \ + --branch fix-v0.2.0 gobley-uniffi-bindgen --force +export BINDGEN_GOBLEY_INSTALLED=1 + +# Standardize on release-smaller for all targets +export BINDGEN_PROFILE="release-smaller" + +./scripts/uniffi_bindgen_generate.sh \ + && ./scripts/swift_create_xcframework_archive.sh diff --git a/bindings/kotlin/ldk-node-android/gradle.properties b/bindings/kotlin/ldk-node-android/gradle.properties index ac7f34deb..63a32f40e 100644 --- a/bindings/kotlin/ldk-node-android/gradle.properties +++ b/bindings/kotlin/ldk-node-android/gradle.properties @@ -3,4 +3,4 @@ android.useAndroidX=true android.enableJetifier=true kotlin.code.style=official group=com.synonym -version=0.7.0-rc.30 +version=0.7.0-rc.32 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 4a76e096c..8fa9afdaf 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 f260c4802..0b00d1b89 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 f1db658a0..ec2a5a8e6 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/gradle.properties b/bindings/kotlin/ldk-node-jvm/gradle.properties index eed593aff..d4c9d344c 100644 --- a/bindings/kotlin/ldk-node-jvm/gradle.properties +++ b/bindings/kotlin/ldk-node-jvm/gradle.properties @@ -1,4 +1,4 @@ org.gradle.jvmargs=-Xmx1536m kotlin.code.style=official group=com.synonym -version=0.7.0-rc.30 +version=0.7.0-rc.32 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 5f8c21952..c74c3c151 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 3ce9de905..6bce06c68 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/python/pyproject.toml b/bindings/python/pyproject.toml index f64c3c65e..b71180742 100644 --- a/bindings/python/pyproject.toml +++ b/bindings/python/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ldk_node" -version = "0.7.0-rc.30" +version = "0.7.0-rc.32" authors = [ { name="Elias Rohrer", email="dev@tnull.de" }, ] diff --git a/bindings/swift/Sources/LDKNode/LDKNode.swift b/bindings/swift/Sources/LDKNode/LDKNode.swift index ef5cbbca6..f679967d9 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 @@ -5245,8 +5293,8 @@ public func FfiConverterTypeCustomTlvRecord_lower(_ value: CustomTlvRecord) -> R public struct ElectrumSyncConfig { 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 } @@ -5298,8 +5346,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 } @@ -5352,8 +5400,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 @@ -5416,8 +5464,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 @@ -5470,8 +5518,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 @@ -5544,8 +5592,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 @@ -5611,8 +5659,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 @@ -5709,8 +5757,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 @@ -5759,8 +5807,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 @@ -5811,8 +5859,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 @@ -5930,8 +5978,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 @@ -6006,8 +6054,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 @@ -6074,8 +6122,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 @@ -6142,8 +6190,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 @@ -6252,8 +6300,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 @@ -6318,8 +6366,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 @@ -6416,8 +6464,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 @@ -6495,8 +6543,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 @@ -6586,8 +6634,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 @@ -6661,8 +6709,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 @@ -6723,8 +6771,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 @@ -6791,8 +6839,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 @@ -6853,8 +6901,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 @@ -6924,8 +6972,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 @@ -7009,8 +7057,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 @@ -7271,10 +7319,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) @@ -7286,11 +7332,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 } @@ -7486,23 +7530,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) } @@ -7515,8 +7556,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)) @@ -7530,8 +7570,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 @@ -7543,8 +7582,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)) @@ -7831,10 +7869,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) @@ -7875,11 +7911,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)) @@ -8283,10 +8317,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) @@ -8298,11 +8330,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 } @@ -8970,8 +9000,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) } @@ -8984,8 +9013,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)) @@ -9409,12 +9437,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) @@ -9426,14 +9451,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 } @@ -12068,8 +12090,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) @@ -12080,15 +12102,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) }) } @@ -12115,8 +12135,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/crates/bdk-wallet-aggregate/src/lib.rs b/crates/bdk-wallet-aggregate/src/lib.rs index 5d6e2d110..fb10611c4 100644 --- a/crates/bdk-wallet-aggregate/src/lib.rs +++ b/crates/bdk-wallet-aggregate/src/lib.rs @@ -414,6 +414,19 @@ where Ok(()) } + /// Cancel a dry-run transaction on the primary wallet without persisting. + /// + /// Unmarks change addresses that were marked "used" by `finish()`, so + /// the next `finish()` reuses them instead of revealing new ones. The + /// reveal itself is left in the staged changeset and persists harmlessly. + /// + /// Only targets the primary wallet. All current build paths use the primary. + pub fn cancel_dry_run_tx(&mut self, tx: &Transaction) { + let primary = + self.wallets.get_mut(&self.primary).expect("Primary wallet must always exist"); + primary.cancel_tx(tx); + } + // ─── Fee Calculation ──────────────────────────────────────────────── /// Calculate the fee of a PSBT by summing input values and subtracting @@ -1346,3 +1359,277 @@ where self.wallets.values().any(|w| w.is_mine(script_pubkey.clone())) } } + +#[cfg(test)] +mod tests { + use bdk_wallet::template::Bip84; + use bdk_wallet::{ChangeSet, KeychainKind, PersistedWallet, Wallet, WalletPersister}; + use bitcoin::bip32::Xpriv; + use bitcoin::{Amount, FeeRate, Network, OutPoint, ScriptBuf, Transaction, TxIn, TxOut, Txid}; + + use super::*; + + struct NoopPersister; + + impl WalletPersister for NoopPersister { + type Error = std::convert::Infallible; + + fn initialize(_: &mut Self) -> Result { + Ok(ChangeSet::default()) + } + + fn persist(_: &mut Self, _: &ChangeSet) -> Result<(), Self::Error> { + Ok(()) + } + } + + fn test_xprv() -> Xpriv { + Xpriv::new_master(Network::Regtest, &[0x42; 32]).unwrap() + } + + fn create_funded_wallet( + persister: &mut NoopPersister, amount: Amount, + ) -> PersistedWallet { + let xprv = test_xprv(); + let mut wallet = PersistedWallet::create( + persister, + Wallet::create( + Bip84(xprv, KeychainKind::External), + Bip84(xprv, KeychainKind::Internal), + ) + .network(Network::Regtest), + ) + .unwrap(); + + let addr = wallet.reveal_next_address(KeychainKind::External); + let funding_tx = Transaction { + version: bitcoin::transaction::Version::TWO, + lock_time: bitcoin::blockdata::locktime::absolute::LockTime::ZERO, + input: vec![TxIn { + previous_output: OutPoint { txid: Txid::from_byte_array([0x01; 32]), vout: 0 }, + ..Default::default() + }], + output: vec![TxOut { value: amount, script_pubkey: addr.address.script_pubkey() }], + }; + wallet.apply_unconfirmed_txs([(funding_tx, 0)]); + wallet + } + + fn recipient_script() -> ScriptBuf { + ScriptBuf::new_p2wpkh(&bitcoin::WPubkeyHash::from_byte_array([0xab; 20])) + } + + /// Demonstrates the bug: without cancel_tx, each finish() call + /// cumulatively advances the internal (change) derivation index. + #[test] + fn test_finish_without_cancel_leaks_change_index() { + let mut persister = NoopPersister; + let mut wallet = create_funded_wallet(&mut persister, Amount::from_sat(100_000)); + + let recipient = recipient_script(); + let fee_rate = FeeRate::from_sat_per_vb(2).unwrap(); + + let initial_idx = wallet.derivation_index(KeychainKind::Internal); + + // First dry-run finish(), no cancel + let mut b1 = wallet.build_tx(); + b1.add_recipient(recipient.clone(), Amount::from_sat(30_000)).fee_rate(fee_rate); + let _psbt1 = b1.finish().unwrap(); + let after_first = wallet.derivation_index(KeychainKind::Internal); + + // Second dry-run finish(), no cancel + let mut b2 = wallet.build_tx(); + b2.add_recipient(recipient.clone(), Amount::from_sat(30_000)).fee_rate(fee_rate); + let _psbt2 = b2.finish().unwrap(); + let after_second = wallet.derivation_index(KeychainKind::Internal); + + // Third dry-run finish(), no cancel + let mut b3 = wallet.build_tx(); + b3.add_recipient(recipient.clone(), Amount::from_sat(30_000)).fee_rate(fee_rate); + let _psbt3 = b3.finish().unwrap(); + let after_third = wallet.derivation_index(KeychainKind::Internal); + + assert!( + after_first > initial_idx || (initial_idx.is_none() && after_first.is_some()), + "First finish() should advance the internal index" + ); + assert!( + after_second > after_first, + "Second finish() without cancel should advance further: {:?} > {:?}", + after_second, + after_first, + ); + assert!( + after_third > after_second, + "Third finish() without cancel should advance further: {:?} > {:?}", + after_third, + after_second, + ); + } + + /// Demonstrates the fix: cancel_dry_run_tx unmarks the change address, + /// so subsequent finish() calls reuse the same index. + #[test] + fn test_cancel_dry_run_prevents_cumulative_index_leak() { + let mut persister = NoopPersister; + let wallet = create_funded_wallet(&mut persister, Amount::from_sat(100_000)); + let mut agg = AggregateWallet::::new(wallet, persister, 0, vec![]); + + let recipient = recipient_script(); + let fee_rate = FeeRate::from_sat_per_vb(2).unwrap(); + + let initial_idx = agg.derivation_index(KeychainKind::Internal); + + // Dry-run 1: finish + cancel + let psbt1 = { + let w = agg.primary_wallet_mut(); + let mut b = w.build_tx(); + b.add_recipient(recipient.clone(), Amount::from_sat(30_000)).fee_rate(fee_rate); + b.finish().unwrap() + }; + agg.cancel_dry_run_tx(&psbt1.unsigned_tx); + let after_first = agg.derivation_index(KeychainKind::Internal); + + assert!( + after_first > initial_idx || (initial_idx.is_none() && after_first.is_some()), + "First finish() should reveal an internal address" + ); + + // Dry-run 2: finish + cancel + let psbt2 = { + let w = agg.primary_wallet_mut(); + let mut b = w.build_tx(); + b.add_recipient(recipient.clone(), Amount::from_sat(30_000)).fee_rate(fee_rate); + b.finish().unwrap() + }; + agg.cancel_dry_run_tx(&psbt2.unsigned_tx); + let after_second = agg.derivation_index(KeychainKind::Internal); + + assert_eq!( + after_first, after_second, + "cancel_dry_run_tx should prevent cumulative index advance" + ); + + // Dry-runs 3-5: confirm stability + for i in 3..=5 { + let psbt = { + let w = agg.primary_wallet_mut(); + let mut b = w.build_tx(); + b.add_recipient(recipient.clone(), Amount::from_sat(30_000)).fee_rate(fee_rate); + b.finish().unwrap() + }; + agg.cancel_dry_run_tx(&psbt.unsigned_tx); + let idx = agg.derivation_index(KeychainKind::Internal); + assert_eq!(after_first, idx, "Dry-run {} should still reuse the same index", i); + } + } + + /// Verifies that cancel_dry_run_tx prevents index leak even when an + /// intermediate operation fails between finish() and the cancel. + #[test] + fn test_cancel_after_failed_intermediate_prevents_leak() { + let mut persister = NoopPersister; + let wallet = create_funded_wallet(&mut persister, Amount::from_sat(100_000)); + let mut agg = AggregateWallet::::new(wallet, persister, 0, vec![]); + + let recipient = recipient_script(); + let fee_rate = FeeRate::from_sat_per_vb(2).unwrap(); + + // Baseline + let initial_idx = agg.derivation_index(KeychainKind::Internal); + + // Simulate finish() + failed intermediate + unconditional cancel. + for _ in 0..5 { + let psbt = { + let w = agg.primary_wallet_mut(); + let mut b = w.build_tx(); + b.add_recipient(recipient.clone(), Amount::from_sat(30_000)).fee_rate(fee_rate); + b.finish().unwrap() + }; + + // Simulate an intermediate operation that fails. + let _simulated_failure: Result = Err("simulated fee calculation failure"); + + // Cancel regardless of failure. + agg.cancel_dry_run_tx(&psbt.unsigned_tx); + } + + let final_idx = agg.derivation_index(KeychainKind::Internal); + + assert!( + final_idx > initial_idx || (initial_idx.is_none() && final_idx.is_some()), + "Index should be revealed at least once" + ); + + // One more dry-run + cancel should not change it. + let psbt = { + let w = agg.primary_wallet_mut(); + let mut b = w.build_tx(); + b.add_recipient(recipient.clone(), Amount::from_sat(30_000)).fee_rate(fee_rate); + b.finish().unwrap() + }; + agg.cancel_dry_run_tx(&psbt.unsigned_tx); + assert_eq!( + final_idx, + agg.derivation_index(KeychainKind::Internal), + "Sixth dry-run should still reuse the same index" + ); + } + + /// After a cancelled dry-run, the next real finish() reuses the same + /// change address. Verifies the fix doesn't break real transactions. + #[test] + fn test_dry_run_cancel_then_real_tx_reuses_change_address() { + let mut persister = NoopPersister; + let wallet = create_funded_wallet(&mut persister, Amount::from_sat(200_000)); + let mut agg = AggregateWallet::::new(wallet, persister, 0, vec![]); + + let recipient = recipient_script(); + let fee_rate = FeeRate::from_sat_per_vb(2).unwrap(); + + // Dry-run + cancel + let dry_psbt = { + let w = agg.primary_wallet_mut(); + let mut b = w.build_tx(); + b.add_recipient(recipient.clone(), Amount::from_sat(50_000)).fee_rate(fee_rate); + b.finish().unwrap() + }; + let dry_run_change_script: Option = dry_psbt + .unsigned_tx + .output + .iter() + .find(|o| agg.is_mine(o.script_pubkey.clone())) + .map(|o| o.script_pubkey.clone()); + agg.cancel_dry_run_tx(&dry_psbt.unsigned_tx); + + let idx_after_dry = agg.derivation_index(KeychainKind::Internal); + + // Real tx (no cancel — this would be signed and broadcast) + let real_psbt = { + let w = agg.primary_wallet_mut(); + let mut b = w.build_tx(); + b.add_recipient(recipient.clone(), Amount::from_sat(50_000)).fee_rate(fee_rate); + b.finish().unwrap() + }; + let real_change_script: Option = real_psbt + .unsigned_tx + .output + .iter() + .find(|o| agg.is_mine(o.script_pubkey.clone())) + .map(|o| o.script_pubkey.clone()); + + let idx_after_real = agg.derivation_index(KeychainKind::Internal); + + assert_eq!( + idx_after_dry, idx_after_real, + "Real tx should not advance index beyond what dry-run already revealed" + ); + if let (Some(dry_change), Some(real_change)) = (&dry_run_change_script, &real_change_script) + { + assert_eq!( + dry_change, real_change, + "Real tx should reuse the same change address as the cancelled dry-run" + ); + } + } +} diff --git a/scripts/uniffi_bindgen_generate.sh b/scripts/uniffi_bindgen_generate.sh index 3b9717a77..745a971d5 100755 --- a/scripts/uniffi_bindgen_generate.sh +++ b/scripts/uniffi_bindgen_generate.sh @@ -16,4 +16,4 @@ unset BINDGEN_EXTRA_CLANG_ARGS source ./scripts/uniffi_bindgen_generate_kotlin.sh || exit 1 source ./scripts/uniffi_bindgen_generate_python.sh || exit 1 source ./scripts/uniffi_bindgen_generate_swift.sh || exit 1 - +source ./scripts/uniffi_bindgen_generate_kotlin_android.sh || exit 1 diff --git a/scripts/uniffi_bindgen_generate_kotlin.sh b/scripts/uniffi_bindgen_generate_kotlin.sh index 6ac4f1b04..910b02afe 100755 --- a/scripts/uniffi_bindgen_generate_kotlin.sh +++ b/scripts/uniffi_bindgen_generate_kotlin.sh @@ -5,32 +5,34 @@ TARGET_DIR="target" PROJECT_DIR="ldk-node-jvm" JVM_LIB_DIR="$BINDINGS_DIR/$PROJECT_DIR" -# Install gobley-uniffi-bindgen from fork with patched version -echo "Installing gobley-uniffi-bindgen fork..." -cargo install --git https://github.com/ovitrif/gobley.git --branch fix-v0.2.0 gobley-uniffi-bindgen --force +# Install gobley-uniffi-bindgen from fork (skip if orchestrator already installed it) +if [ -z "${BINDGEN_GOBLEY_INSTALLED:-}" ]; then + echo "Installing gobley-uniffi-bindgen fork..." + cargo install --git https://github.com/ovitrif/gobley.git --branch fix-v0.2.0 gobley-uniffi-bindgen --force +fi UNIFFI_BINDGEN_BIN="gobley-uniffi-bindgen" if [[ "$OSTYPE" == "linux-gnu"* ]]; then echo "Building for Linux x86_64..." rustup target add x86_64-unknown-linux-gnu || exit 1 - cargo build --release --target x86_64-unknown-linux-gnu --features uniffi || exit 1 - DYNAMIC_LIB_PATH="$TARGET_DIR/x86_64-unknown-linux-gnu/release/libldk_node.so" + cargo build --profile release-smaller --target x86_64-unknown-linux-gnu --features uniffi || exit 1 + DYNAMIC_LIB_PATH="$TARGET_DIR/x86_64-unknown-linux-gnu/release-smaller/libldk_node.so" RES_DIR="$JVM_LIB_DIR/lib/src/main/resources/linux-x86-64/" mkdir -p $RES_DIR || exit 1 cp $DYNAMIC_LIB_PATH $RES_DIR || exit 1 else echo "Building for macOS x86_64..." rustup target add x86_64-apple-darwin || exit 1 - cargo build --release --target x86_64-apple-darwin --features uniffi || exit 1 - DYNAMIC_LIB_PATH="$TARGET_DIR/x86_64-apple-darwin/release/libldk_node.dylib" + cargo build --profile release-smaller --target x86_64-apple-darwin --features uniffi || exit 1 + DYNAMIC_LIB_PATH="$TARGET_DIR/x86_64-apple-darwin/release-smaller/libldk_node.dylib" RES_DIR="$JVM_LIB_DIR/lib/src/main/resources/darwin-x86-64/" mkdir -p $RES_DIR || exit 1 cp $DYNAMIC_LIB_PATH $RES_DIR || exit 1 echo "Building for macOS aarch64..." rustup target add aarch64-apple-darwin || exit 1 - cargo build --release --target aarch64-apple-darwin --features uniffi || exit 1 - DYNAMIC_LIB_PATH_ARM="$TARGET_DIR/aarch64-apple-darwin/release/libldk_node.dylib" + cargo build --profile release-smaller --target aarch64-apple-darwin --features uniffi || exit 1 + DYNAMIC_LIB_PATH_ARM="$TARGET_DIR/aarch64-apple-darwin/release-smaller/libldk_node.dylib" RES_DIR="$JVM_LIB_DIR/lib/src/main/resources/darwin-aarch64/" mkdir -p $RES_DIR || exit 1 cp $DYNAMIC_LIB_PATH_ARM $RES_DIR || exit 1 diff --git a/scripts/uniffi_bindgen_generate_kotlin_android.sh b/scripts/uniffi_bindgen_generate_kotlin_android.sh index c6a68e9bb..785c28455 100755 --- a/scripts/uniffi_bindgen_generate_kotlin_android.sh +++ b/scripts/uniffi_bindgen_generate_kotlin_android.sh @@ -5,9 +5,11 @@ TARGET_DIR="target" PROJECT_DIR="ldk-node-android" ANDROID_LIB_DIR="$BINDINGS_DIR/$PROJECT_DIR" -# Install gobley-uniffi-bindgen from fork with patched version -echo "Installing gobley-uniffi-bindgen fork..." -cargo install --git https://github.com/ovitrif/gobley.git --branch fix-v0.2.0 gobley-uniffi-bindgen --force +# Install gobley-uniffi-bindgen from fork (skip if orchestrator already installed it) +if [ -z "${BINDGEN_GOBLEY_INSTALLED:-}" ]; then + echo "Installing gobley-uniffi-bindgen fork..." + cargo install --git https://github.com/ovitrif/gobley.git --branch fix-v0.2.0 gobley-uniffi-bindgen --force +fi UNIFFI_BINDGEN_BIN="gobley-uniffi-bindgen" export_variable_if_not_present() { @@ -61,6 +63,11 @@ cargo ndk \ -t x86_64 \ build --profile release-smaller --features uniffi || exit 1 +# Clean up exported flags so they don't leak into subsequent scripts +# (e.g. the -z linker flags are Linux-only and break macOS builds) +unset RUSTFLAGS +unset CFLAGS + # Generate Kotlin bindings echo "Generating Kotlin bindings..." $UNIFFI_BINDGEN_BIN bindings/ldk_node.udl --lib-file $TARGET_DIR/aarch64-linux-android/release-smaller/libldk_node.so --config uniffi-android.toml -o "$ANDROID_LIB_DIR/lib/src" || exit 1 diff --git a/scripts/uniffi_bindgen_generate_swift.sh b/scripts/uniffi_bindgen_generate_swift.sh index 478890e2c..3cb582c07 100755 --- a/scripts/uniffi_bindgen_generate_swift.sh +++ b/scripts/uniffi_bindgen_generate_swift.sh @@ -4,9 +4,6 @@ set -eox pipefail BINDINGS_DIR="./bindings/swift" UNIFFI_BINDGEN_BIN="cargo run --manifest-path bindings/uniffi-bindgen/Cargo.toml" -cargo build --release || exit 1 -$UNIFFI_BINDGEN_BIN generate bindings/ldk_node.udl --language swift -o "$BINDINGS_DIR" || exit 1 - mkdir -p $BINDINGS_DIR # Install rust target toolchains diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index 0ca364f90..49928f0be 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -1075,7 +1075,12 @@ impl Wallet { } }; - let base_fee = primary.calculate_fee(&tmp_tx).map_err(|e| { + let fee_result = primary.calculate_fee(&tmp_tx); + + // Cancel the temp tx to free up the change address. + primary.cancel_tx(&tmp_tx); + + let base_fee = fee_result.map_err(|e| { log_error!( self.logger, "Failed to calculate fee of temporary transaction: {}", @@ -1084,9 +1089,6 @@ impl Wallet { e })?; - // 'cancel' the transaction to free up any used change addresses - primary.cancel_tx(&tmp_tx); - // Adjust the fee estimate for non-primary inputs that will be // added to the actual tx (the temp tx only used primary UTXOs). let extra_input_weight: u64 = non_primary_utxo_infos @@ -1184,21 +1186,26 @@ impl Wallet { }; // Check the reserve requirements (again) and return an error if they aren't met. + // Cancel the PSBT before each early return to free up the change address. match send_amount { OnchainSendAmount::ExactRetainingReserve { amount_sats, cur_anchor_reserve_sats } => { let spendable_amount_sats = self .get_balances_inner(&aggregate_balance, cur_anchor_reserve_sats) .map(|(_, s)| s) .unwrap_or(0); - let tx_fee_sats = - locked_wallet.calculate_fee_with_fallback(&psbt).map_err(|e| { + let fee_result = locked_wallet.calculate_fee_with_fallback(&psbt); + let tx_fee_sats = match fee_result { + Ok(fee) => fee, + Err(e) => { log_error!( self.logger, "Failed to calculate fee of candidate transaction: {}", e ); - Error::WalletOperationFailed - })?; + locked_wallet.cancel_dry_run_tx(&psbt.unsigned_tx); + return Err(Error::WalletOperationFailed); + }, + }; if spendable_amount_sats < amount_sats.saturating_add(tx_fee_sats) { log_error!(self.logger, "Unable to send payment due to insufficient funds. Available: {}sats, Required: {}sats + {}sats fee", @@ -1206,6 +1213,7 @@ impl Wallet { amount_sats, tx_fee_sats, ); + locked_wallet.cancel_dry_run_tx(&psbt.unsigned_tx); return Err(Error::InsufficientFunds); } }, @@ -1221,6 +1229,7 @@ impl Wallet { spendable_amount_sats, drain_amount, ); + locked_wallet.cancel_dry_run_tx(&psbt.unsigned_tx); return Err(Error::InsufficientFunds); } }, @@ -1241,7 +1250,7 @@ impl Wallet { let fee_rate = fee_rate.unwrap_or_else(|| self.fee_estimator.estimate_fee_rate(confirmation_target)); - let (psbt, locked_wallet) = self.build_transaction_psbt( + let (psbt, mut locked_wallet) = self.build_transaction_psbt( address, send_amount, fee_rate, @@ -1249,7 +1258,12 @@ impl Wallet { channel_manager, )?; - let calculated_fee = locked_wallet.calculate_fee_with_fallback(&psbt).map_err(|e| { + let fee_result = locked_wallet.calculate_fee_with_fallback(&psbt); + + // Cancel the dry-run PSBT to free up the change address. + locked_wallet.cancel_dry_run_tx(&psbt.unsigned_tx); + + let calculated_fee = fee_result.map_err(|e| { log_error!(self.logger, "Failed to calculate transaction fee: {}", e); Error::WalletOperationFailed })?; @@ -1388,11 +1402,11 @@ impl Wallet { tx_builder.fee_rate(fee_rate); tx_builder.exclude_unconfirmed(); - tx_builder - .finish() - .map_err(|e| { - log_error!(self.logger, "Failed to select confirmed UTXOs: {}", e); - })? + let psbt = tx_builder.finish().map_err(|e| { + log_error!(self.logger, "Failed to select confirmed UTXOs: {}", e); + })?; + + let result = psbt .unsigned_tx .input .iter() @@ -1403,7 +1417,12 @@ impl Wallet { .map(|tx_details| tx_details.tx.deref().clone()) .map(|prevtx| FundingTxInput::new_p2wpkh(prevtx, txin.previous_output.vout)) }) - .collect::, ()>>() + .collect::, ()>>(); + + // Cancel the dry-run PSBT to free up the change address. + locked_wallet.cancel_dry_run_tx(&psbt.unsigned_tx); + + result } fn list_confirmed_utxos_inner(&self) -> Result, ()> {