From 361b610d13fdc947b4ac0a2eb6118be0da263811 Mon Sep 17 00:00:00 2001 From: alexanderwiederin Date: Tue, 7 Nov 2023 17:04:13 +0100 Subject: [PATCH 1/3] add linting configuration and script --- .../kotlin/ldk-node-android/lib/build.gradle.kts | 9 +++++++++ bindings/kotlin/ldk-node-jvm/lib/build.gradle.kts | 9 +++++++++ scripts/format_kotlin.sh | 15 +++++++++++++++ 3 files changed, 33 insertions(+) create mode 100755 scripts/format_kotlin.sh diff --git a/bindings/kotlin/ldk-node-android/lib/build.gradle.kts b/bindings/kotlin/ldk-node-android/lib/build.gradle.kts index 0e624cada..325d03132 100644 --- a/bindings/kotlin/ldk-node-android/lib/build.gradle.kts +++ b/bindings/kotlin/ldk-node-android/lib/build.gradle.kts @@ -10,6 +10,7 @@ plugins { id("maven-publish") id("signing") + id("org.jlleitschuh.gradle.ktlint") version "11.6.1" } repositories { @@ -106,3 +107,11 @@ signing { // useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) sign(publishing.publications) } + +ktlint { + filter { + exclude { entry -> + entry.file.toString().contains("main") + } + } +} diff --git a/bindings/kotlin/ldk-node-jvm/lib/build.gradle.kts b/bindings/kotlin/ldk-node-jvm/lib/build.gradle.kts index 61374cfc4..ee21b3de8 100644 --- a/bindings/kotlin/ldk-node-jvm/lib/build.gradle.kts +++ b/bindings/kotlin/ldk-node-jvm/lib/build.gradle.kts @@ -12,6 +12,7 @@ plugins { id("java-library") id("maven-publish") id("signing") + id("org.jlleitschuh.gradle.ktlint") version "11.6.1" } repositories { @@ -111,3 +112,11 @@ signing { // useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) sign(publishing.publications) } + +ktlint { + filter { + exclude { entry -> + entry.file.toString().contains("main") + } + } +} diff --git a/scripts/format_kotlin.sh b/scripts/format_kotlin.sh new file mode 100755 index 000000000..b093395ea --- /dev/null +++ b/scripts/format_kotlin.sh @@ -0,0 +1,15 @@ +#!/bin/bash +LDK_NODE_ANDROID_DIR="bindings/kotlin/ldk-node-android" +LDK_NODE_JVM_DIR="bindings/kotlin/ldk-node-jvm" + +# Run ktlintFormat in ldk-node-android +( + cd $LDK_NODE_ANDROID_DIR || exit 1 + ./gradlew ktlintFormat +) + +# Run ktlintFormat in ldk-node-jvm +( + cd $LDK_NODE_JVM_DIR || exit 1 + ./gradlew ktlintFormat +) From 476446fd1333ef5c64a67c1df7e19f525e24f1e3 Mon Sep 17 00:00:00 2001 From: alexanderwiederin Date: Tue, 7 Nov 2023 17:10:07 +0100 Subject: [PATCH 2/3] lint format kotlin --- .../ldk-node-android/lib/build.gradle.kts | 3 --- .../kotlin/ldk-node-jvm/lib/build.gradle.kts | 26 ++++++++++-------- .../lightningdevkit/ldknode/LibraryTest.kt | 27 +++++++++---------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/bindings/kotlin/ldk-node-android/lib/build.gradle.kts b/bindings/kotlin/ldk-node-android/lib/build.gradle.kts index 325d03132..8803d5e60 100644 --- a/bindings/kotlin/ldk-node-android/lib/build.gradle.kts +++ b/bindings/kotlin/ldk-node-android/lib/build.gradle.kts @@ -1,6 +1,3 @@ -import org.gradle.api.tasks.testing.logging.TestExceptionFormat.* -import org.gradle.api.tasks.testing.logging.TestLogEvent.* - // library version is defined in gradle.properties val libraryVersion: String by project diff --git a/bindings/kotlin/ldk-node-jvm/lib/build.gradle.kts b/bindings/kotlin/ldk-node-jvm/lib/build.gradle.kts index ee21b3de8..7b2d148cf 100644 --- a/bindings/kotlin/ldk-node-jvm/lib/build.gradle.kts +++ b/bindings/kotlin/ldk-node-jvm/lib/build.gradle.kts @@ -1,5 +1,9 @@ -import org.gradle.api.tasks.testing.logging.TestExceptionFormat.* -import org.gradle.api.tasks.testing.logging.TestLogEvent.* +import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL +import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED +import org.gradle.api.tasks.testing.logging.TestLogEvent.PASSED +import org.gradle.api.tasks.testing.logging.TestLogEvent.SKIPPED +import org.gradle.api.tasks.testing.logging.TestLogEvent.STANDARD_ERROR +import org.gradle.api.tasks.testing.logging.TestLogEvent.STANDARD_OUT // library version is defined in gradle.properties val libraryVersion: String by project @@ -32,12 +36,12 @@ dependencies { // Use the JUnit 5 integration. testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.1") - //// This dependency is exported to consumers, that is to say found on their compile classpath. - //api("org.apache.commons:commons-math3:3.6.1") + // // This dependency is exported to consumers, that is to say found on their compile classpath. + // api("org.apache.commons:commons-math3:3.6.1") - //// This dependency is used internally, and not exposed to consumers on their own compile classpath. - //implementation("com.google.guava:guava:31.1-jre") - // Align versions of all Kotlin components + // // This dependency is used internally, and not exposed to consumers on their own compile classpath. + // implementation("com.google.guava:guava:31.1-jre") + // Align versions of all Kotlin components implementation(platform("org.jetbrains.kotlin:kotlin-bom")) // Use the Kotlin JDK 8 standard library. @@ -50,13 +54,13 @@ tasks.named("test") { // Use JUnit Platform for unit tests. useJUnitPlatform() - testLogging { + testLogging { events(PASSED, SKIPPED, FAILED, STANDARD_OUT, STANDARD_ERROR) exceptionFormat = FULL showExceptions = true showCauses = true showStackTraces = true - showStandardStreams = true + showStandardStreams = true } } @@ -89,8 +93,8 @@ afterEvaluate { developers { developer { id.set("tnull") - name.set("Elias Rohrer") - email.set("dev@tnull.de") + name.set("Elias Rohrer") + email.set("dev@tnull.de") } } } diff --git a/bindings/kotlin/ldk-node-jvm/lib/src/test/kotlin/org/lightningdevkit/ldknode/LibraryTest.kt b/bindings/kotlin/ldk-node-jvm/lib/src/test/kotlin/org/lightningdevkit/ldknode/LibraryTest.kt index 5eaa653d7..2864d157a 100644 --- a/bindings/kotlin/ldk-node-jvm/lib/src/test/kotlin/org/lightningdevkit/ldknode/LibraryTest.kt +++ b/bindings/kotlin/ldk-node-jvm/lib/src/test/kotlin/org/lightningdevkit/ldknode/LibraryTest.kt @@ -1,6 +1,3 @@ -/* - * This Kotlin source file was generated by the Gradle 'init' task. - */ package org.lightningdevkit.ldknode import kotlin.UInt @@ -11,6 +8,8 @@ import java.net.URI import java.net.http.HttpClient import java.net.http.HttpRequest import java.net.http.HttpResponse +import kotlin.io.path.createTempDirectory +import kotlin.test.assertEquals fun runCommandAndWait(vararg cmd: String): String { println("Running command \"${cmd.joinToString(" ")}\"") @@ -29,7 +28,7 @@ fun mine(blocks: UInt): String { val output = runCommandAndWait("bitcoin-cli", "-regtest", "generatetoaddress", blocks.toString(), address) println("Mining output: $output") val re = Regex("\n.+\n\\]$") - val lastBlock = re.find(output)!!.value.replace("]","").replace("\"", "").replace("\n","").trim() + val lastBlock = re.find(output)!!.value.replace("]", "").replace("\"", "").replace("\n", "").trim() println("Last block: $lastBlock") return lastBlock } @@ -54,32 +53,32 @@ fun setup() { fun waitForTx(esploraEndpoint: String, txid: String) { var esploraPickedUpTx = false - val re = Regex("\"txid\":\"$txid\""); + val re = Regex("\"txid\":\"$txid\"") while (!esploraPickedUpTx) { val client = HttpClient.newBuilder().build() val request = HttpRequest.newBuilder() .uri(URI.create(esploraEndpoint + "/tx/" + txid)) - .build(); + .build() - val response = client.send(request, HttpResponse.BodyHandlers.ofString()); + val response = client.send(request, HttpResponse.BodyHandlers.ofString()) - esploraPickedUpTx = re.containsMatchIn(response.body()); + esploraPickedUpTx = re.containsMatchIn(response.body()) Thread.sleep(500) } } fun waitForBlock(esploraEndpoint: String, blockHash: String) { var esploraPickedUpBlock = false - val re = Regex("\"in_best_chain\":true"); + val re = Regex("\"in_best_chain\":true") while (!esploraPickedUpBlock) { val client = HttpClient.newBuilder().build() val request = HttpRequest.newBuilder() .uri(URI.create(esploraEndpoint + "/block/" + blockHash + "/status")) - .build(); + .build() - val response = client.send(request, HttpResponse.BodyHandlers.ofString()); + val response = client.send(request, HttpResponse.BodyHandlers.ofString()) - esploraPickedUpBlock = re.containsMatchIn(response.body()); + esploraPickedUpBlock = re.containsMatchIn(response.body()) Thread.sleep(500) } } @@ -172,7 +171,7 @@ class LibraryTest { val fundingTxid = when (channelPendingEvent1) { is Event.ChannelPending -> channelPendingEvent1.fundingTxo.txid - else -> return + else -> return } waitForTx(esploraEndpoint, fundingTxid) @@ -202,7 +201,7 @@ class LibraryTest { val channelId = when (channelReadyEvent2) { is Event.ChannelReady -> channelReadyEvent2.channelId - else -> return + else -> return } val invoice = node2.receivePayment(2500000u, "asdf", 9217u) From 52c969421fda11218b0a6b26bde4d6ede1b356cf Mon Sep 17 00:00:00 2001 From: alexanderwiederin Date: Wed, 8 Nov 2023 15:35:27 +0100 Subject: [PATCH 3/3] add github workflow to run kotlin tests against docker --- .github/workflows/kotlin.yml | 47 ++++++++++++++++ .../kotlin/ldk-node-jvm/lib/build.gradle.kts | 17 ++++++ .../lightningdevkit/ldknode/LibraryTest.kt | 52 ++++++++++++------ docker-compose.yml | 53 +++++++++++++++++++ 4 files changed, 152 insertions(+), 17 deletions(-) create mode 100644 .github/workflows/kotlin.yml create mode 100644 docker-compose.yml diff --git a/.github/workflows/kotlin.yml b/.github/workflows/kotlin.yml new file mode 100644 index 000000000..420bb721d --- /dev/null +++ b/.github/workflows/kotlin.yml @@ -0,0 +1,47 @@ +name: Continuous Integration Checks - Kotlin + +on: [push, pull_request] + +jobs: + check-kotlin: + runs-on: ubuntu-latest + + env: + LDK_NODE_JVM_DIR: bindings/kotlin/ldk-node-jvm + LDK_NODE_ANDROID_DIR: bindings/kotlin/ldk-node-android + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up JDK + uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: 11 + + - name: Run ktlintCheck on ldk-node-jvm + run: | + cd $LDK_NODE_JVM_DIR + ./gradlew ktlintCheck + + - name: Run ktlintCheck on ldk-node-android + run: | + cd $LDK_NODE_ANDROID_DIR + ./gradlew ktlintCheck + + - name: Generate Kotlin JVM + run: ./scripts/uniffi_bindgen_generate_kotlin.sh + + - name: Start bitcoind and electrs + run: docker compose up -d + + - name: Run ldk-node-jvm tests + run: | + cd $LDK_NODE_JVM_DIR + ./gradlew test -Penv=ci + + - name: Run ldk-node-android tests + run: | + cd $LDK_NODE_ANDROID_DIR + ./gradlew test diff --git a/bindings/kotlin/ldk-node-jvm/lib/build.gradle.kts b/bindings/kotlin/ldk-node-jvm/lib/build.gradle.kts index 7b2d148cf..7bed66a6e 100644 --- a/bindings/kotlin/ldk-node-jvm/lib/build.gradle.kts +++ b/bindings/kotlin/ldk-node-jvm/lib/build.gradle.kts @@ -64,6 +64,23 @@ tasks.named("test") { } } +tasks.test { + doFirst { + if (project.hasProperty("env") && project.property("env") == "ci") { + environment("BITCOIN_CLI_BIN", "docker exec ldk-node-bitcoin-1 bitcoin-cli") + environment("BITCOIND_RPC_USER", "user") + environment("BITCOIND_RPC_PASSWORD", "pass") + environment("ESPLORA_ENDPOINT", "http://127.0.0.1:3002") + } else { + // Adapt these to your local environment + environment("BITCOIN_CLI_BIN", "bitcoin-cli") + environment("BITCOIND_RPC_USER", "") + environment("BITCOIND_RPC_PASSWORD", "") + environment("ESPLORA_ENDPOINT", "http://127.0.0.1:3002") + } + } +} + afterEvaluate { publishing { publications { diff --git a/bindings/kotlin/ldk-node-jvm/lib/src/test/kotlin/org/lightningdevkit/ldknode/LibraryTest.kt b/bindings/kotlin/ldk-node-jvm/lib/src/test/kotlin/org/lightningdevkit/ldknode/LibraryTest.kt index 2864d157a..65f4a3f73 100644 --- a/bindings/kotlin/ldk-node-jvm/lib/src/test/kotlin/org/lightningdevkit/ldknode/LibraryTest.kt +++ b/bindings/kotlin/ldk-node-jvm/lib/src/test/kotlin/org/lightningdevkit/ldknode/LibraryTest.kt @@ -1,9 +1,8 @@ package org.lightningdevkit.ldknode -import kotlin.UInt -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.io.path.createTempDirectory +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance import java.net.URI import java.net.http.HttpClient import java.net.http.HttpRequest @@ -23,9 +22,26 @@ fun runCommandAndWait(vararg cmd: String): String { return stdout + stderr } +fun bitcoinCli(vararg cmd: String): String { + val bitcoinCliBin = System.getenv("BITCOIN_CLI_BIN")?.split(" ") ?: listOf("bitcoin-cli") + val bitcoinDRpcUser = System.getenv("BITCOIND_RPC_USER") ?: "" + val bitcoinDRpcPassword = System.getenv("BITCOIND_RPC_PASSWORD") ?: "" + + val baseCommand = bitcoinCliBin + "-regtest" + + val rpcAuth = if (bitcoinDRpcUser.isNotBlank() && bitcoinDRpcPassword.isNotBlank()) { + listOf("-rpcuser=$bitcoinDRpcUser", "-rpcpassword=$bitcoinDRpcPassword") + } else { + emptyList() + } + + val fullCommand = baseCommand + rpcAuth + cmd.toList() + return runCommandAndWait(*fullCommand.toTypedArray()) +} + fun mine(blocks: UInt): String { - val address = runCommandAndWait("bitcoin-cli", "-regtest", "getnewaddress") - val output = runCommandAndWait("bitcoin-cli", "-regtest", "generatetoaddress", blocks.toString(), address) + val address = bitcoinCli("getnewaddress") + val output = bitcoinCli("generatetoaddress", blocks.toString(), address) println("Mining output: $output") val re = Regex("\n.+\n\\]$") val lastBlock = re.find(output)!!.value.replace("]", "").replace("\"", "").replace("\n", "").trim() @@ -40,17 +56,10 @@ fun mineAndWait(esploraEndpoint: String, blocks: UInt) { fun sendToAddress(address: String, amountSats: UInt): String { val amountBtc = amountSats.toDouble() / 100000000.0 - val output = runCommandAndWait("bitcoin-cli", "-regtest", "sendtoaddress", address, amountBtc.toString()) + val output = bitcoinCli("sendtoaddress", address, amountBtc.toString()) return output } -fun setup() { - runCommandAndWait("bitcoin-cli", "-regtest", "createwallet", "ldk_node_test") - runCommandAndWait("bitcoin-cli", "-regtest", "loadwallet", "ldk_node_test", "true") - mine(101u) - Thread.sleep(5_000) -} - fun waitForTx(esploraEndpoint: String, txid: String) { var esploraPickedUpTx = false val re = Regex("\"txid\":\"$txid\"") @@ -83,11 +92,20 @@ fun waitForBlock(esploraEndpoint: String, blockHash: String) { } } +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class LibraryTest { - @Test fun fullCycle() { - val esploraEndpoint = "http://127.0.0.1:3002" - setup() + val esploraEndpoint = System.getenv("ESPLORA_ENDPOINT") + + @BeforeAll + fun setup() { + bitcoinCli("createwallet", "ldk_node_test") + bitcoinCli("loadwallet", "ldk_node_test", "true") + mine(101u) + Thread.sleep(5_000) + } + + @Test fun fullCycle() { val tmpDir1 = createTempDirectory("ldk_node").toString() println("Random dir 1: $tmpDir1") val tmpDir2 = createTempDirectory("ldk_node").toString() diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..3c3233bff --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,53 @@ +version: '3' + +services: + bitcoin: + image: blockstream/bitcoind:24.1 + platform: linux/amd64 + command: + [ + "bitcoind", + "-printtoconsole", + "-regtest=1", + "-rpcallowip=0.0.0.0/0", + "-rpcbind=0.0.0.0", + "-rpcuser=user", + "-rpcpassword=pass", + "-fallbackfee=0.00001" + ] + ports: + - "18443:18443" # Regtest RPC port + - "18444:18444" # Regtest P2P port + networks: + - bitcoin-electrs + healthcheck: + test: ["CMD", "bitcoin-cli", "-regtest", "-rpcuser=user", "-rpcpassword=pass", "getblockchaininfo"] + interval: 5s + timeout: 10s + retries: 5 + + electrs: + image: blockstream/esplora:electrs-cd9f90c115751eb9d2bca9a4da89d10d048ae931 + platform: linux/amd64 + depends_on: + bitcoin: + condition: service_healthy + command: + [ + "/app/electrs_bitcoin/bin/electrs", + "-vvvv", + "--timestamp", + "--jsonrpc-import", + "--cookie=user:pass", + "--network=regtest", + "--daemon-rpc-addr=bitcoin:18443", + "--http-addr=0.0.0.0:3002" + ] + ports: + - "3002:3002" + networks: + - bitcoin-electrs + +networks: + bitcoin-electrs: + driver: bridge