diff --git a/.github/workflows/publish-android.yml b/.github/workflows/publish-android.yml index c315efc554..81412f8946 100644 --- a/.github/workflows/publish-android.yml +++ b/.github/workflows/publish-android.yml @@ -18,6 +18,8 @@ jobs: steps: - uses: actions/checkout@v4 + with: + ref: ${{ github.event_name == 'workflow_dispatch' && inputs.version || github.event.release.tag_name }} - name: Set up JDK 17 uses: actions/setup-java@v4 with: @@ -29,6 +31,22 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 + - name: Install native build dependencies + run: sudo apt-get update && sudo apt-get install -y pkg-config protobuf-compiler + + - name: Set up Android SDK + uses: android-actions/setup-android@v3 + + - name: Set up Android NDK + id: setup-ndk + uses: nttld/setup-ndk@v1 + with: + ndk-version: r28c + add-to-path: true + + - name: Export Android NDK root + run: echo "ANDROID_NDK_ROOT=${{ steps.setup-ndk.outputs.ndk-path }}" >> "$GITHUB_ENV" + - name: Extract version from input or tag id: version shell: bash @@ -39,6 +57,9 @@ jobs: fi echo "version=${VERSION#v}" >> $GITHUB_OUTPUT + - name: Generate Android bindings + run: scripts/uniffi_bindgen_generate_kotlin_android.sh + - name: Build with Gradle working-directory: bindings/kotlin/ldk-node-android run: ./gradlew build -Pversion=${{ steps.version.outputs.version }} @@ -47,6 +68,6 @@ jobs: working-directory: bindings/kotlin/ldk-node-android env: GITHUB_ACTOR: ${{ github.actor }} - GITHUB_TOKEN: ${{ secrets.ORG_PACKAGES_TOKEN }} + GITHUB_TOKEN: ${{ github.token }} GITHUB_REPO: ${{ github.repository }} run: ./gradlew publish -Pversion=${{ steps.version.outputs.version }} diff --git a/CHANGELOG.md b/CHANGELOG.md index f3cb198a58..f725b6a799 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ -# 0.7.0-rc.46 (Synonym Fork) +# 0.7.0-rc.48 (Synonym Fork) ## Bug Fixes +- Kept Android native debug metadata in published `ldk-node-android` artifacts and + failed Android binding generation when generated JNI libraries lack usable symbols. - Persist missing announced channel peers from the network graph during build-time restore and after Rapid Gossip Sync graph updates. Automatic recovery respects explicit disconnects and last-channel closes during the diff --git a/Cargo.toml b/Cargo.toml index 41ac44474b..e8eddf05c9 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ exclude = ["bindings/uniffi-bindgen"] [package] name = "ldk-node" -version = "0.7.0-rc.46" +version = "0.7.0-rc.48" authors = ["Elias Rohrer "] homepage = "https://lightningdevkit.org/" license = "MIT OR Apache-2.0" diff --git a/Package.swift b/Package.swift index 2f1b598db9..03583f22ae 100644 --- a/Package.swift +++ b/Package.swift @@ -3,8 +3,8 @@ import PackageDescription -let tag = "v0.7.0-rc.46" -let checksum = "06558c2834c186d3da188389ef197942e948ac4f6f3261746e3594420ab0ee23" +let tag = "v0.7.0-rc.48" +let checksum = "afadaec20a4d580b877282da609d7882233a8a1137f8cb621c894892aa3dbaae" let url = "https://github.com/synonymdev/ldk-node/releases/download/\(tag)/LDKNodeFFI.xcframework.zip" let package = Package( diff --git a/bindings/kotlin/ldk-node-android/gradle.properties b/bindings/kotlin/ldk-node-android/gradle.properties index ca66391555..2efdd46c2f 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.46 +version=0.7.0-rc.48 diff --git a/bindings/kotlin/ldk-node-android/lib/build.gradle.kts b/bindings/kotlin/ldk-node-android/lib/build.gradle.kts index 68b084ec0c..afaa42aece 100644 --- a/bindings/kotlin/ldk-node-android/lib/build.gradle.kts +++ b/bindings/kotlin/ldk-node-android/lib/build.gradle.kts @@ -1,3 +1,6 @@ +import java.io.ByteArrayOutputStream +import java.io.File + plugins { id("com.android.library") kotlin("android") @@ -39,6 +42,12 @@ android { jvmTarget = "1.8" } + packaging { + jniLibs { + keepDebugSymbols += listOf("**/libldk_node.so") + } + } + publishing { singleVariant("release") { withSourcesJar() @@ -58,6 +67,104 @@ dependencies { api("org.slf4j:slf4j-api:1.7.30") } +val androidNativeAbis = listOf("armeabi-v7a", "arm64-v8a", "x86_64") + +fun executableFromPath(name: String): String? { + return System.getenv("PATH") + ?.split(File.pathSeparator) + ?.asSequence() + ?.map { File(it, name) } + ?.firstOrNull { it.canExecute() } + ?.absolutePath +} + +fun findReadelf(): String { + executableFromPath("llvm-readelf")?.let { return it } + executableFromPath("readelf")?.let { return it } + + return listOf("ANDROID_NDK_ROOT", "ANDROID_NDK_HOME", "NDK_HOME") + .mapNotNull { System.getenv(it) } + .map { File(it, "toolchains/llvm/prebuilt") } + .firstNotNullOfOrNull { prebuiltDir -> + if (!prebuiltDir.isDirectory) return@firstNotNullOfOrNull null + + prebuiltDir + .walkTopDown() + .firstOrNull { it.name == "llvm-readelf" && it.canExecute() } + ?.absolutePath + } + ?: throw GradleException( + "llvm-readelf or readelf is required to validate Android native debug symbols" + ) +} + +fun Project.runReadelf(readelf: String, vararg args: String): Pair { + val stdout = ByteArrayOutputStream() + val stderr = ByteArrayOutputStream() + val result = exec { + commandLine(readelf, *args) + standardOutput = stdout + errorOutput = stderr + isIgnoreExitValue = true + } + + return result.exitValue to stdout.toString().ifBlank { stderr.toString() } +} + +fun String.parseElfAlignment(): Long { + return if (startsWith("0x")) { + removePrefix("0x").toLong(16) + } else { + toLong() + } +} + +val validateReleaseNativeLibraries by tasks.registering { + group = "verification" + description = "Validates release JNI libraries keep full DWARF metadata and 16 KB LOAD alignment." + + doLast { + val readelf = findReadelf() + val loadAlignmentRegex = Regex("""^\s*LOAD\s+.*\s+(0x[0-9a-fA-F]+|\d+)\s*$""") + + androidNativeAbis.forEach { abi -> + val lib = layout.projectDirectory.file("src/main/jniLibs/$abi/libldk_node.so").asFile + if (!lib.isFile) { + throw GradleException("Android native library missing at '${lib.path}'") + } + + val (sectionsExit, sections) = runReadelf(readelf, "-S", lib.absolutePath) + if (sectionsExit != 0 || !Regex("""\.debug_""").containsMatchIn(sections)) { + throw GradleException("Android native library has no full DWARF debug metadata: '${lib.path}'") + } + + val wideHeaders = runReadelf(readelf, "-W", "-l", lib.absolutePath) + val headers = if (wideHeaders.first == 0) { + wideHeaders.second + } else { + val fallbackHeaders = runReadelf(readelf, "-l", lib.absolutePath) + if (fallbackHeaders.first != 0) { + throw GradleException("Unable to inspect Android native library headers: '${lib.path}'") + } + fallbackHeaders.second + } + + val alignments = headers + .lineSequence() + .mapNotNull { loadAlignmentRegex.matchEntire(it)?.groupValues?.get(1)?.parseElfAlignment() } + .toList() + + if (alignments.isEmpty() || alignments.any { it < 16_384 }) { + throw GradleException("Android native library is not 16 KB page-size aligned: '${lib.path}'") + } + } + } +} + +tasks.matching { it.name == "bundleReleaseAar" || it.name.startsWith("publish") }.configureEach { + dependsOn(validateReleaseNativeLibraries) +} + afterEvaluate { publishing { publications { 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 e4280dd39c..39ef97d488 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 e6bccf1056..857eb9196d 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 07feb9dc6f..39323d330c 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-android/lib/src/main/kotlin/org/lightningdevkit/ldknode/ldk_node.android.kt b/bindings/kotlin/ldk-node-android/lib/src/main/kotlin/org/lightningdevkit/ldknode/ldk_node.android.kt index 29e01ae29e..857bc2e4aa 100644 --- a/bindings/kotlin/ldk-node-android/lib/src/main/kotlin/org/lightningdevkit/ldknode/ldk_node.android.kt +++ b/bindings/kotlin/ldk-node-android/lib/src/main/kotlin/org/lightningdevkit/ldknode/ldk_node.android.kt @@ -472,11 +472,11 @@ internal open class UniffiForeignFutureStruct( @JvmField internal var `free`: UniffiForeignFutureFree?, ) : com.sun.jna.Structure() { constructor(): this( - + `handle` = 0.toLong(), - + `free` = null, - + ) internal class UniffiByValue( @@ -503,11 +503,11 @@ internal open class UniffiForeignFutureStructU8Struct( @JvmField internal var `callStatus`: UniffiRustCallStatusByValue, ) : com.sun.jna.Structure() { constructor(): this( - + `returnValue` = 0.toByte(), - + `callStatus` = UniffiRustCallStatusHelper.allocValue(), - + ) internal class UniffiByValue( @@ -537,11 +537,11 @@ internal open class UniffiForeignFutureStructI8Struct( @JvmField internal var `callStatus`: UniffiRustCallStatusByValue, ) : com.sun.jna.Structure() { constructor(): this( - + `returnValue` = 0.toByte(), - + `callStatus` = UniffiRustCallStatusHelper.allocValue(), - + ) internal class UniffiByValue( @@ -571,11 +571,11 @@ internal open class UniffiForeignFutureStructU16Struct( @JvmField internal var `callStatus`: UniffiRustCallStatusByValue, ) : com.sun.jna.Structure() { constructor(): this( - + `returnValue` = 0.toShort(), - + `callStatus` = UniffiRustCallStatusHelper.allocValue(), - + ) internal class UniffiByValue( @@ -605,11 +605,11 @@ internal open class UniffiForeignFutureStructI16Struct( @JvmField internal var `callStatus`: UniffiRustCallStatusByValue, ) : com.sun.jna.Structure() { constructor(): this( - + `returnValue` = 0.toShort(), - + `callStatus` = UniffiRustCallStatusHelper.allocValue(), - + ) internal class UniffiByValue( @@ -639,11 +639,11 @@ internal open class UniffiForeignFutureStructU32Struct( @JvmField internal var `callStatus`: UniffiRustCallStatusByValue, ) : com.sun.jna.Structure() { constructor(): this( - + `returnValue` = 0, - + `callStatus` = UniffiRustCallStatusHelper.allocValue(), - + ) internal class UniffiByValue( @@ -673,11 +673,11 @@ internal open class UniffiForeignFutureStructI32Struct( @JvmField internal var `callStatus`: UniffiRustCallStatusByValue, ) : com.sun.jna.Structure() { constructor(): this( - + `returnValue` = 0, - + `callStatus` = UniffiRustCallStatusHelper.allocValue(), - + ) internal class UniffiByValue( @@ -707,11 +707,11 @@ internal open class UniffiForeignFutureStructU64Struct( @JvmField internal var `callStatus`: UniffiRustCallStatusByValue, ) : com.sun.jna.Structure() { constructor(): this( - + `returnValue` = 0.toLong(), - + `callStatus` = UniffiRustCallStatusHelper.allocValue(), - + ) internal class UniffiByValue( @@ -741,11 +741,11 @@ internal open class UniffiForeignFutureStructI64Struct( @JvmField internal var `callStatus`: UniffiRustCallStatusByValue, ) : com.sun.jna.Structure() { constructor(): this( - + `returnValue` = 0.toLong(), - + `callStatus` = UniffiRustCallStatusHelper.allocValue(), - + ) internal class UniffiByValue( @@ -775,11 +775,11 @@ internal open class UniffiForeignFutureStructF32Struct( @JvmField internal var `callStatus`: UniffiRustCallStatusByValue, ) : com.sun.jna.Structure() { constructor(): this( - + `returnValue` = 0.0f, - + `callStatus` = UniffiRustCallStatusHelper.allocValue(), - + ) internal class UniffiByValue( @@ -809,11 +809,11 @@ internal open class UniffiForeignFutureStructF64Struct( @JvmField internal var `callStatus`: UniffiRustCallStatusByValue, ) : com.sun.jna.Structure() { constructor(): this( - + `returnValue` = 0.0, - + `callStatus` = UniffiRustCallStatusHelper.allocValue(), - + ) internal class UniffiByValue( @@ -843,11 +843,11 @@ internal open class UniffiForeignFutureStructPointerStruct( @JvmField internal var `callStatus`: UniffiRustCallStatusByValue, ) : com.sun.jna.Structure() { constructor(): this( - + `returnValue` = NullPointer, - + `callStatus` = UniffiRustCallStatusHelper.allocValue(), - + ) internal class UniffiByValue( @@ -877,11 +877,11 @@ internal open class UniffiForeignFutureStructRustBufferStruct( @JvmField internal var `callStatus`: UniffiRustCallStatusByValue, ) : com.sun.jna.Structure() { constructor(): this( - + `returnValue` = RustBufferHelper.allocValue(), - + `callStatus` = UniffiRustCallStatusHelper.allocValue(), - + ) internal class UniffiByValue( @@ -910,9 +910,9 @@ internal open class UniffiForeignFutureStructVoidStruct( @JvmField internal var `callStatus`: UniffiRustCallStatusByValue, ) : com.sun.jna.Structure() { constructor(): this( - + `callStatus` = UniffiRustCallStatusHelper.allocValue(), - + ) internal class UniffiByValue( @@ -945,11 +945,11 @@ internal open class UniffiVTableCallbackInterfaceLogWriterStruct( @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree?, ) : com.sun.jna.Structure() { constructor(): this( - + `log` = null, - + `uniffiFree` = null, - + ) internal class UniffiByValue( @@ -976,11 +976,11 @@ internal open class UniffiVTableCallbackInterfaceVssHeaderProviderStruct( @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree?, ) : com.sun.jna.Structure() { constructor(): this( - + `getHeaders` = null, - + `uniffiFree` = null, - + ) internal class UniffiByValue( @@ -1560,7 +1560,7 @@ internal interface UniffiLib : Library { uniffiCallbackInterfaceLogWriter.register(lib) } } - + // The Cleaner for the whole library internal val CLEANER: UniffiCleaner by lazy { UniffiCleaner.create() @@ -3448,7 +3448,7 @@ internal interface UniffiLib : Library { ): Short fun ffi_ldk_node_uniffi_contract_version( ): Int - + } private fun uniffiCheckContractApiVersion(lib: UniffiLib) { @@ -4373,7 +4373,7 @@ open class Bolt11Invoice: Disposable, Bolt11InvoiceInterface { }!! } - + override fun `amountMilliSatoshis`(): kotlin.ULong? { return FfiConverterOptionalULong.lift(callWithPointer { uniffiRustCall { uniffiRustCallStatus -> @@ -4552,8 +4552,8 @@ open class Bolt11Invoice: Disposable, Bolt11InvoiceInterface { } - - + + override fun toString(): String { return FfiConverterString.lift(callWithPointer { uniffiRustCall { uniffiRustCallStatus -> @@ -4564,7 +4564,7 @@ open class Bolt11Invoice: Disposable, Bolt11InvoiceInterface { } }) } - + override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is Bolt11Invoice) return false @@ -4578,11 +4578,11 @@ open class Bolt11Invoice: Disposable, Bolt11InvoiceInterface { } }) } - - + + companion object { - + @Throws(NodeException::class) fun `fromStr`(`invoiceStr`: kotlin.String): Bolt11Invoice { return FfiConverterTypeBolt11Invoice.lift(uniffiRustCallWithError(NodeExceptionErrorHandler) { uniffiRustCallStatus -> @@ -4593,9 +4593,9 @@ open class Bolt11Invoice: Disposable, Bolt11InvoiceInterface { }!!) } - + } - + } @@ -4782,7 +4782,7 @@ open class Bolt11Payment: Disposable, Bolt11PaymentInterface { }!! } - + @Throws(NodeException::class) override fun `claimForHash`(`paymentHash`: PaymentHash, `claimableAmountMsat`: kotlin.ULong, `preimage`: PaymentPreimage) { callWithPointer { @@ -5021,13 +5021,13 @@ open class Bolt11Payment: Disposable, Bolt11PaymentInterface { } - - - - + + + + companion object - + } @@ -5151,7 +5151,7 @@ open class Bolt12Invoice: Disposable, Bolt12InvoiceInterface { }!! } - + override fun `absoluteExpirySeconds`(): kotlin.ULong? { return FfiConverterOptionalULong.lift(callWithPointer { uniffiRustCall { uniffiRustCallStatus -> @@ -5373,12 +5373,12 @@ open class Bolt12Invoice: Disposable, Bolt12InvoiceInterface { } - - - + + + companion object { - + @Throws(NodeException::class) fun `fromStr`(`invoiceStr`: kotlin.String): Bolt12Invoice { return FfiConverterTypeBolt12Invoice.lift(uniffiRustCallWithError(NodeExceptionErrorHandler) { uniffiRustCallStatus -> @@ -5389,9 +5389,9 @@ open class Bolt12Invoice: Disposable, Bolt12InvoiceInterface { }!!) } - + } - + } @@ -5515,7 +5515,7 @@ open class Bolt12Payment: Disposable, Bolt12PaymentInterface { }!! } - + @Throws(NodeException::class) override fun `blindedPathsForAsyncRecipient`(`recipientId`: kotlin.ByteArray): kotlin.ByteArray { return FfiConverterByteArray.lift(callWithPointer { @@ -5648,13 +5648,13 @@ open class Bolt12Payment: Disposable, Bolt12PaymentInterface { } - - - - + + + + companion object - + } @@ -5786,7 +5786,7 @@ open class Builder: Disposable, BuilderInterface { }!! } - + @Throws(BuildException::class) override fun `build`(): Node { return FfiConverterTypeNode.lift(callWithPointer { @@ -6177,12 +6177,12 @@ open class Builder: Disposable, BuilderInterface { } - - - + + + companion object { - + fun `fromConfig`(`config`: Config): Builder { return FfiConverterTypeBuilder.lift(uniffiRustCall { uniffiRustCallStatus -> UniffiLib.INSTANCE.uniffi_ldk_node_fn_constructor_builder_from_config( @@ -6192,9 +6192,9 @@ open class Builder: Disposable, BuilderInterface { }!!) } - + } - + } @@ -6318,7 +6318,7 @@ open class FeeRate: Disposable, FeeRateInterface { }!! } - + override fun `toSatPerKwu`(): kotlin.ULong { return FfiConverterULong.lift(callWithPointer { uniffiRustCall { uniffiRustCallStatus -> @@ -6353,12 +6353,12 @@ open class FeeRate: Disposable, FeeRateInterface { } - - - + + + companion object { - + fun `fromSatPerKwu`(`satKwu`: kotlin.ULong): FeeRate { return FfiConverterTypeFeeRate.lift(uniffiRustCall { uniffiRustCallStatus -> UniffiLib.INSTANCE.uniffi_ldk_node_fn_constructor_feerate_from_sat_per_kwu( @@ -6368,7 +6368,7 @@ open class FeeRate: Disposable, FeeRateInterface { }!!) } - + fun `fromSatPerVbUnchecked`(`satVb`: kotlin.ULong): FeeRate { return FfiConverterTypeFeeRate.lift(uniffiRustCall { uniffiRustCallStatus -> UniffiLib.INSTANCE.uniffi_ldk_node_fn_constructor_feerate_from_sat_per_vb_unchecked( @@ -6378,9 +6378,9 @@ open class FeeRate: Disposable, FeeRateInterface { }!!) } - + } - + } @@ -6504,7 +6504,7 @@ open class Lsps1Liquidity: Disposable, Lsps1LiquidityInterface { }!! } - + @Throws(NodeException::class) override fun `checkOrderStatus`(`orderId`: Lsps1OrderId): Lsps1OrderStatus { return FfiConverterTypeLSPS1OrderStatus.lift(callWithPointer { @@ -6535,13 +6535,13 @@ open class Lsps1Liquidity: Disposable, Lsps1LiquidityInterface { } - - - - + + + + companion object - + } @@ -6665,7 +6665,7 @@ open class LogWriterImpl: Disposable, LogWriter { }!! } - + override fun `log`(`record`: LogRecord) { callWithPointer { uniffiRustCall { uniffiRustCallStatus -> @@ -6679,13 +6679,13 @@ open class LogWriterImpl: Disposable, LogWriter { } - - - - + + + + companion object - + } @@ -6877,7 +6877,7 @@ open class NetworkGraph: Disposable, NetworkGraphInterface { }!! } - + override fun `channel`(`shortChannelId`: kotlin.ULong): ChannelInfo? { return FfiConverterOptionalTypeChannelInfo.lift(callWithPointer { uniffiRustCall { uniffiRustCallStatus -> @@ -6925,13 +6925,13 @@ open class NetworkGraph: Disposable, NetworkGraphInterface { } - - - - + + + + companion object - + } @@ -7055,7 +7055,7 @@ open class Node: Disposable, NodeInterface { }!! } - + @Throws(NodeException::class) override fun `addAddressTypeToMonitor`(`addressType`: AddressType, `seedBytes`: List) { callWithPointer { @@ -7676,13 +7676,13 @@ open class Node: Disposable, NodeInterface { } - - - - + + + + companion object - + } @@ -7806,7 +7806,7 @@ open class Offer: Disposable, OfferInterface { }!! } - + override fun `absoluteExpirySeconds`(): kotlin.ULong? { return FfiConverterOptionalULong.lift(callWithPointer { uniffiRustCall { uniffiRustCallStatus -> @@ -7942,8 +7942,8 @@ open class Offer: Disposable, OfferInterface { } - - + + override fun toString(): String { return FfiConverterString.lift(callWithPointer { uniffiRustCall { uniffiRustCallStatus -> @@ -7954,7 +7954,7 @@ open class Offer: Disposable, OfferInterface { } }) } - + override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is Offer) return false @@ -7968,11 +7968,11 @@ open class Offer: Disposable, OfferInterface { } }) } - - + + companion object { - + @Throws(NodeException::class) fun `fromStr`(`offerStr`: kotlin.String): Offer { return FfiConverterTypeOffer.lift(uniffiRustCallWithError(NodeExceptionErrorHandler) { uniffiRustCallStatus -> @@ -7983,9 +7983,9 @@ open class Offer: Disposable, OfferInterface { }!!) } - + } - + } @@ -8109,7 +8109,7 @@ open class OnchainPayment: Disposable, OnchainPaymentInterface { }!! } - + @Throws(NodeException::class) override fun `accelerateByCpfp`(`txid`: Txid, `feeRate`: FeeRate?, `destinationAddress`: Address?): Txid { return FfiConverterTypeTxid.lift(callWithPointer { @@ -8339,13 +8339,13 @@ open class OnchainPayment: Disposable, OnchainPaymentInterface { } - - - - + + + + companion object - + } @@ -8469,7 +8469,7 @@ open class Refund: Disposable, RefundInterface { }!! } - + override fun `absoluteExpirySeconds`(): kotlin.ULong? { return FfiConverterOptionalULong.lift(callWithPointer { uniffiRustCall { uniffiRustCallStatus -> @@ -8581,8 +8581,8 @@ open class Refund: Disposable, RefundInterface { } - - + + override fun toString(): String { return FfiConverterString.lift(callWithPointer { uniffiRustCall { uniffiRustCallStatus -> @@ -8593,7 +8593,7 @@ open class Refund: Disposable, RefundInterface { } }) } - + override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is Refund) return false @@ -8607,11 +8607,11 @@ open class Refund: Disposable, RefundInterface { } }) } - - + + companion object { - + @Throws(NodeException::class) fun `fromStr`(`refundStr`: kotlin.String): Refund { return FfiConverterTypeRefund.lift(uniffiRustCallWithError(NodeExceptionErrorHandler) { uniffiRustCallStatus -> @@ -8622,9 +8622,9 @@ open class Refund: Disposable, RefundInterface { }!!) } - + } - + } @@ -8748,7 +8748,7 @@ open class SpontaneousPayment: Disposable, SpontaneousPaymentInterface { }!! } - + @Throws(NodeException::class) override fun `send`(`amountMsat`: kotlin.ULong, `nodeId`: PublicKey, `routeParameters`: RouteParametersConfig?): PaymentId { return FfiConverterTypePaymentId.lift(callWithPointer { @@ -8828,13 +8828,13 @@ open class SpontaneousPayment: Disposable, SpontaneousPaymentInterface { } - - - - + + + + companion object - + } @@ -8958,7 +8958,7 @@ open class UnifiedQrPayment: Disposable, UnifiedQrPaymentInterface { }!! } - + @Throws(NodeException::class) override fun `receive`(`amountSats`: kotlin.ULong, `message`: kotlin.String, `expirySec`: kotlin.UInt): kotlin.String { return FfiConverterString.lift(callWithPointer { @@ -8989,13 +8989,13 @@ open class UnifiedQrPayment: Disposable, UnifiedQrPaymentInterface { } - - - - + + + + companion object - + } @@ -9119,7 +9119,7 @@ open class VssHeaderProvider: Disposable, VssHeaderProviderInterface { }!! } - + @Throws(VssHeaderProviderException::class, kotlin.coroutines.cancellation.CancellationException::class) override suspend fun `getHeaders`(`request`: List): Map { return uniffiRustCallAsync( @@ -9141,13 +9141,13 @@ open class VssHeaderProvider: Disposable, VssHeaderProviderInterface { } - - - - + + + + companion object - + } diff --git a/bindings/kotlin/ldk-node-jvm/gradle.properties b/bindings/kotlin/ldk-node-jvm/gradle.properties index 99c5c63ef8..14a7941728 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.46 +version=0.7.0-rc.48 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 b6a407013b..0b1d971b02 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 14d3d6861e..96243ce4bd 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 37af932ecf..23396034b1 100644 --- a/bindings/python/pyproject.toml +++ b/bindings/python/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ldk_node" -version = "0.7.0-rc.46" +version = "0.7.0-rc.48" authors = [ { name="Elias Rohrer", email="dev@tnull.de" }, ] diff --git a/scripts/uniffi_bindgen_generate_kotlin_android.sh b/scripts/uniffi_bindgen_generate_kotlin_android.sh index 785c28455d..82abf39f3b 100755 --- a/scripts/uniffi_bindgen_generate_kotlin_android.sh +++ b/scripts/uniffi_bindgen_generate_kotlin_android.sh @@ -41,10 +41,11 @@ case "$OSTYPE" in PATH="$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/$LLVM_ARCH_PATH/bin:$PATH" -# Install cargo-ndk if not already installed -if ! command -v cargo-ndk &> /dev/null; then - echo "Installing cargo-ndk..." - cargo install cargo-ndk +# Install the cargo-ndk version used by the mobile release scripts. +CARGO_NDK_VERSION="3.5.4" +if ! command -v cargo-ndk &> /dev/null || ! cargo ndk --version | grep -q "cargo-ndk $CARGO_NDK_VERSION"; then + echo "Installing cargo-ndk $CARGO_NDK_VERSION..." + cargo install cargo-ndk --version "$CARGO_NDK_VERSION" --locked --force fi # Add Android targets @@ -54,17 +55,144 @@ rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-android # Build for all Android architectures with page size optimizations echo "Building for Android architectures..." JNI_LIB_DIR="$ANDROID_LIB_DIR/lib/src/main/jniLibs" +export CARGO_PROFILE_RELEASE_SMALLER_STRIP=false export RUSTFLAGS="-C link-args=-Wl,-z,max-page-size=16384,-z,common-page-size=16384" export CFLAGS="-D__ANDROID_MIN_SDK_VERSION__=21" + +find_readelf() { + if command -v llvm-readelf >/dev/null 2>&1; then + command -v llvm-readelf + return + fi + + if command -v readelf >/dev/null 2>&1; then + command -v readelf + return + fi + + for ndk_dir in "${ANDROID_NDK_ROOT:-}" "${ANDROID_NDK_HOME:-}" "${NDK_HOME:-}"; do + if [ -z "$ndk_dir" ] || [ ! -d "$ndk_dir/toolchains/llvm/prebuilt" ]; then + continue + fi + + ndk_readelf=$(find "$ndk_dir/toolchains/llvm/prebuilt" -path '*/bin/llvm-readelf' | head -n 1) + if [ -n "$ndk_readelf" ]; then + echo "$ndk_readelf" + return + fi + done + + echo "Error: llvm-readelf or readelf is required to validate Android native debug symbols" + exit 1 +} + +has_dwarf_debug_metadata() { + for attempt in 1 2 3; do + if "$READELF_BIN" -S "$1" | grep -Eq '\.debug_'; then + return 0 + fi + + sleep "$attempt" + done + + "$READELF_BIN" -S "$1" | grep -E '\.debug_' || true + return 1 +} + +readelf_program_headers() { + if "$READELF_BIN" -W -l "$1" >/dev/null 2>&1; then + "$READELF_BIN" -W -l "$1" + return + fi + + "$READELF_BIN" -l "$1" +} + +has_16kb_load_alignment() { + alignments=$(readelf_program_headers "$1" | awk '$1 == "LOAD" { print $NF }') + if [ -z "$alignments" ]; then + return 1 + fi + + while read -r alignment; do + if [ -z "$alignment" ]; then + continue + fi + + if [ "$((alignment))" -lt 16384 ]; then + return 1 + fi + done <