diff --git a/build.gradle.kts b/build.gradle.kts index 0330431..6c64aca 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -80,13 +80,13 @@ kotlin { fun KotlinNativeTargetWithHostTests.phoenixBinaries() { binaries { executable("phoenixd") { - entryPoint = "fr.acinq.lightning.bin.main" + entryPoint = "fr.acinq.lightning.bin.MainKt" optimized = false // without this, release mode throws 'Index 0 out of bounds for length 0' in StaticInitializersOptimization.kt } - executable("phoenix-cli") { + /*executable("phoenix-cli") { entryPoint = "fr.acinq.lightning.cli.main" optimized = false // without this, release mode throws 'Index 0 out of bounds for length 0' in StaticInitializersOptimization.kt - } + }*/ } } @@ -111,19 +111,13 @@ kotlin { } sourceSets { - val jvmMain by getting { - // Include the native source directory if needed - resources.srcDirs("src/commonMain/kotlin/fr/acinq/lightning/vsock/native") - } commonMain { kotlin.srcDir(buildVersionsTask.map { it.destinationDir }) dependencies { implementation("com.github.raymond98.lightning-kmp:lightning-kmp:v1.6.2-FEECREDIT-8") implementation("org.bytedeco:javacpp:1.5.10") - implementation(kotlin("stdlib-jdk8")) - implementation("org.bytedeco:javacv-platform:1.5.10") - implementation("org.bytedeco:javacpp-presets:1.5.10") + implementation("com.github.maven-nar:nar-maven-plugin:3.10.1") api("fr.acinq.bitcoin:bitcoin-kmp:${Versions.bitcoinKmpVersion}") api("co.touchlab:kermit:${Versions.kermitLoggerVersion}") @@ -190,10 +184,10 @@ kotlin { include("*.kexe") rename("phoenixd.kexe", "phoenixd") } - from("$projectDir/build/bin/$dir/phoenix-cliReleaseExecutable") { + /*from("$projectDir/build/bin/$dir/phoenix-cliReleaseExecutable") { include("*.kexe") rename("phoenix-cli.kexe", "phoenix-cli") - } + }*/ into("${archiveBaseName.get()}-${archiveVersion.get()}-${archiveClassifier.get()}") } @@ -223,7 +217,7 @@ application { applicationDefaultJvmArgs = listOf("-Djava.library.path=${project.buildDir}/libs") } -val cliScripts by tasks.register("cliScripts", CreateStartScripts::class) { +/*val cliScripts by tasks.register("cliScripts", CreateStartScripts::class) { mainClass.set("fr.acinq.lightning.cli.PhoenixCliKt") outputDir = tasks.startScripts.get().outputDir classpath = tasks.startScripts.get().classpath @@ -232,11 +226,11 @@ val cliScripts by tasks.register("cliScripts", CreateStartScripts::class) { tasks.startScripts { dependsOn(cliScripts) -} +}*/ val compileNative by tasks.register("compileNative") { group = "build" - description = "Compile the native C++ code into a shared library" + description = "Compile the native C++ code into a shared library and package it into a .nar file" val outputDir = file("${project.buildDir}/libs") val nativeSourceDir = file("src/commonMain/kotlin/fr/acinq/lightning/vsock/native") @@ -249,9 +243,27 @@ val compileNative by tasks.register("compileNative") { inputs.dir(nativeSourceDir) outputs.dir(outputDir) + // Compile the shared library commandLine("g++", "-I$jniIncludeDir", "-I$jniPlatformIncludeDir", "-shared", "-o", outputDir.resolve("libjniVSockImpl.so"), nativeSourceDir.resolve("VSockImpl.cpp"), "-fPIC") + + // After compilation, package the library into a .nar file + doLast { + val narOutputDir = file("${project.buildDir}/nar") + val narFile = narOutputDir.resolve("vsockj-native-1.0.0.nar") + + // Ensure the output directory exists + narOutputDir.mkdirs() + + // Use the tar command to create a .nar file (essentially a .tar.gz) + exec { + commandLine("tar", "-czvf", narFile.absolutePath, "-C", outputDir.absolutePath, ".") + } + + println(System.getProperty("user.dir")) + } } + // Ensure the native library is compiled before creating the distribution tasks.withType { dependsOn(compileNative) diff --git a/src/commonMain/kotlin/fr/acinq/lightning/bin/Main.kt b/src/commonMain/kotlin/fr/acinq/lightning/bin/Main.kt index 0739f6e..d0b5c5b 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/bin/Main.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/bin/Main.kt @@ -134,7 +134,7 @@ class Phoenixd : CliktCommand() { private val startVsock by option("--start-vsock-server", help = "Start the vsock server for API calls").boolean().default(true) private val vsockCID by option("--vsock-server-cid", help = "CID for the Vsock server").int().default(4) - private val vsockPort by option("--vsock-server-port", help = "Port for the Vsock server").int().default(9001) + private val vsockPort by option("--vsock-server-port", help = "Port for the Vsock server").int().default(9002) class LiquidityOptions : OptionGroup(name = "Liquidity Options") { val autoLiquidity by option("--auto-liquidity", help = "Amount automatically requested when inbound liquidity is needed").choice( @@ -393,9 +393,12 @@ class Phoenixd : CliktCommand() { } var vsockServer: VsockServer? = null - if(startVsock){ + if (startVsock) { vsockServer = VsockServer(vsockCID, vsockPort, httpBindPort, httpBindIp, loggerFactory) - vsockServer.start() + GlobalScope.launch { + vsockServer.start() + consoleLog(yellow("Vsock Server Binding to Cid: ${vsockPort}")) + } } val server = embeddedServer(CIO, port = httpBindPort, host = httpBindIp, diff --git a/src/commonMain/kotlin/fr/acinq/lightning/vsock/BaseVSock.kt b/src/commonMain/kotlin/fr/acinq/lightning/vsock/BaseVSock.kt index da725ad..cd81f68 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/vsock/BaseVSock.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/vsock/BaseVSock.kt @@ -43,7 +43,7 @@ abstract class BaseVSock : Closeable { fun bind(address: VSockAddress?, backlog: Int = DEFAULT_BACKLOG) { var backlog = backlog if (isClosed) { - throw SocketException("Socket closed") + throw SocketException("Socket closed thrown in Base Vsock") } if (bound) { throw SocketException("Socket already bound") diff --git a/src/commonMain/kotlin/fr/acinq/lightning/vsock/ServerVSock.kt b/src/commonMain/kotlin/fr/acinq/lightning/vsock/ServerVSock.kt index b6ec846..05afe63 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/vsock/ServerVSock.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/vsock/ServerVSock.kt @@ -6,7 +6,9 @@ import java.net.SocketException class ServerVSock : BaseVSock() { @Throws(IOException::class) fun accept(): VSock { - if (isClosed) throw SocketException("Socket closed") + if (isClosed) { + throw SocketException("Socket closed") + } if (!bound) throw SocketException("Socket not bound") val socket = VSock() socket.setImplementation() diff --git a/src/commonMain/kotlin/fr/acinq/lightning/vsock/VSock.kt b/src/commonMain/kotlin/fr/acinq/lightning/vsock/VSock.kt index b35e36f..5a5de17 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/vsock/VSock.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/vsock/VSock.kt @@ -12,7 +12,7 @@ class VSock : BaseVSock, Closeable { var outputStream: VSockOutputStream? = null get() { if (isClosed) { - throw SocketException("VSock is closed") + throw SocketException("VSock is closed thrown in Vsock") } if (field == null) { field = getImplementation()?.let { VSockOutputStream(it) } @@ -26,7 +26,9 @@ class VSock : BaseVSock, Closeable { var inputStream: VSockInputStream? = null get() { if (isClosed) { - throw SocketException("VSock is closed") + getImplementation()!!.create() + isClosed = false; + //throw SocketException("VSock is closed thrown in Vsock") } if (field == null) { field = getImplementation()?.let { VSockInputStream(it) } diff --git a/src/commonMain/kotlin/fr/acinq/lightning/vsock/VsockMain.kt b/src/commonMain/kotlin/fr/acinq/lightning/vsock/VsockMain.kt index 893ecd9..331a334 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/vsock/VsockMain.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/vsock/VsockMain.kt @@ -2,7 +2,7 @@ package fr.acinq.lightning.vsock import fr.acinq.lightning.bin.json.ApiType.* import fr.acinq.lightning.logging.LoggerFactory -import fr.acinq.lightning.logging.debug +import fr.acinq.lightning.logging.info import fr.acinq.lightning.logging.error import io.ktor.client.* import io.ktor.client.request.* @@ -17,7 +17,7 @@ import java.nio.charset.StandardCharsets import java.util.Base64 class VsockServer(private val CID: Int, private val port: Int, httpBindPort: Int, host: String, loggerFactory: LoggerFactory) { - private var server: ServerVSock? = null + public var server: ServerVSock? = null private val logger = loggerFactory.newLogger(this::class) private val client = HttpClient() private val apiBaseUrl: String = "${host}:${httpBindPort}" @@ -25,10 +25,11 @@ class VsockServer(private val CID: Int, private val port: Int, httpBindPort: Int @OptIn(DelicateCoroutinesApi::class) fun start() { + logger.info { "Called the Vsock" } server = ServerVSock() try { - server?.bind(VSockAddress(CID, port)) //For any CID use VSockAddress.VMADDR_CID_ANY - logger.debug { "Vsock Bound on Cid: ${server?.localCid}" } + server?.bind(VSockAddress(VSockAddress.VMADDR_CID_ANY, port)) //For any CID use VSockAddress.VMADDR_CID_ANY + logger.info { "Vsock Bound on Cid: ${server?.localCid}" } server?.accept()?.use { peerVSock -> val buffer = ByteArray(bufferSize) @@ -36,7 +37,7 @@ class VsockServer(private val CID: Int, private val port: Int, httpBindPort: Int if (bytesRead != null) { if (bytesRead > 0) { val receivedData = String(buffer, 0, bytesRead, StandardCharsets.UTF_8).trim() - logger.debug { "Received Data: $receivedData" } + logger.info { "Received Data: $receivedData" } // Parse the received data into a http request val apiRequest = try { @@ -90,7 +91,7 @@ class VsockServer(private val CID: Int, private val port: Int, httpBindPort: Int } fun stop() { - logger.debug { "Stopping Vsock Server" } + logger.info { "Stopping Vsock Server" } server?.close() } } diff --git a/src/commonMain/kotlin/fr/acinq/lightning/vsock/native/VSockImpl.cpp b/src/commonMain/kotlin/fr/acinq/lightning/vsock/native/VSockImpl.cpp index 600fc87..f4512c6 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/vsock/native/VSockImpl.cpp +++ b/src/commonMain/kotlin/fr/acinq/lightning/vsock/native/VSockImpl.cpp @@ -12,20 +12,28 @@ #define BUFFER_LEN 65536 #define min(a, b) ((a) < (b) ? (a) : (b)) -// Make sure to wrap all your JNI functions with extern "C" to avoid name mangling extern "C" { -// Native method implementations matching the JNI header - JNIEXPORT void JNICALL Java_fr_acinq_lightning_vsock_native_VSockImpl_socketCreate(JNIEnv *env, jobject thisObj) { int fd = socket(AF_VSOCK, SOCK_STREAM, 0); -// Optionally store the socket descriptor in the Java object's field +if (fd < 0) { +env->ThrowNew(env->FindClass("java/net/SocketException"), "Failed to create socket"); +return; +} + +// Store the socket descriptor in the Java object's field +jclass thisClass = env->GetObjectClass(thisObj); +jfieldID fdField = env->GetFieldID(thisClass, "fd", "I"); +env->SetIntField(thisObj, fdField, fd); } JNIEXPORT void JNICALL Java_fr_acinq_lightning_vsock_native_VSockImpl_connect(JNIEnv *env, jobject thisObj, jobject addr) { -int fd = -1; // Assuming you have stored the fd somewhere accessible +jclass thisClass = env->GetObjectClass(thisObj); +jfieldID fdField = env->GetFieldID(thisClass, "fd", "I"); +int fd = env->GetIntField(thisObj, fdField); + if (fd == -1) { env->ThrowNew(env->FindClass("java/net/SocketException"), "Socket is closed"); return; @@ -41,6 +49,7 @@ std::memset(&sock_addr, 0, sizeof(struct sockaddr_vm)); sock_addr.svm_family = AF_VSOCK; sock_addr.svm_port = env->GetIntField(addr, portField); sock_addr.svm_cid = env->GetIntField(addr, cidField); + int status = ::connect(fd, (struct sockaddr *)&sock_addr, sizeof(struct sockaddr_vm)); if (status != 0) { @@ -51,23 +60,30 @@ env->ThrowNew(env->FindClass("java/net/ConnectException"), JNIEXPORT void JNICALL Java_fr_acinq_lightning_vsock_native_VSockImpl_close(JNIEnv *env, jobject thisObj) { -int fd = -1; // Assuming you have stored the fd somewhere accessible +jclass thisClass = env->GetObjectClass(thisObj); +jfieldID fdField = env->GetFieldID(thisClass, "fd", "I"); +int fd = env->GetIntField(thisObj, fdField); + if (fd == -1) { -return; +return; // Socket is already closed } int status = ::close(fd); -fd = -1; - if (status != 0) { env->ThrowNew(env->FindClass("java/net/SocketException"), ("Close failed with error no: " + std::to_string(errno)).c_str()); } + +// Mark the socket as closed +env->SetIntField(thisObj, fdField, -1); } JNIEXPORT void JNICALL Java_fr_acinq_lightning_vsock_native_VSockImpl_write(JNIEnv *env, jobject thisObj, jbyteArray b, jint offset, jint len) { -int fd = -1; // Assuming you have stored the fd somewhere accessible +jclass thisClass = env->GetObjectClass(thisObj); +jfieldID fdField = env->GetFieldID(thisClass, "fd", "I"); +int fd = env->GetIntField(thisObj, fdField); + if (fd == -1) { env->ThrowNew(env->FindClass("java/net/SocketException"), "Socket is closed"); return; @@ -91,7 +107,10 @@ offset += chunkLen; JNIEXPORT jint JNICALL Java_fr_acinq_lightning_vsock_native_VSockImpl_read(JNIEnv *env, jobject thisObj, jbyteArray b, jint offset, jint len) { -int fd = -1; // Assuming you have stored the fd somewhere accessible +jclass thisClass = env->GetObjectClass(thisObj); +jfieldID fdField = env->GetFieldID(thisClass, "fd", "I"); +int fd = env->GetIntField(thisObj, fdField); + if (fd == -1) { env->ThrowNew(env->FindClass("java/net/SocketException"), "Socket is closed"); return -1; @@ -114,7 +133,10 @@ return nread; JNIEXPORT void JNICALL Java_fr_acinq_lightning_vsock_native_VSockImpl_bind(JNIEnv *env, jobject thisObj, jobject addr) { -int fd = -1; // Assuming you have stored the fd somewhere accessible +jclass thisClass = env->GetObjectClass(thisObj); +jfieldID fdField = env->GetFieldID(thisClass, "fd", "I"); +int fd = env->GetIntField(thisObj, fdField); + if (fd == -1) { env->ThrowNew(env->FindClass("java/net/SocketException"), "Socket is closed"); return; @@ -141,7 +163,10 @@ env->ThrowNew(env->FindClass("java/net/BindException"), JNIEXPORT void JNICALL Java_fr_acinq_lightning_vsock_native_VSockImpl_listen(JNIEnv *env, jobject thisObj, jint backlog) { -int fd = -1; // Assuming you have stored the fd somewhere accessible +jclass thisClass = env->GetObjectClass(thisObj); +jfieldID fdField = env->GetFieldID(thisClass, "fd", "I"); +int fd = env->GetIntField(thisObj, fdField); + if (fd == -1) { env->ThrowNew(env->FindClass("java/net/SocketException"), "Socket is closed"); return; @@ -157,7 +182,10 @@ env->ThrowNew(env->FindClass("java/net/SocketException"), JNIEXPORT void JNICALL Java_fr_acinq_lightning_vsock_native_VSockImpl_accept(JNIEnv *env, jobject thisObj, jobject connectionVSock) { -int fd = -1; // Assuming you have stored the fd somewhere accessible +jclass thisClass = env->GetObjectClass(thisObj); +jfieldID fdField = env->GetFieldID(thisClass, "fd", "I"); +int fd = env->GetIntField(thisObj, fdField); + if (fd == -1) { env->ThrowNew(env->FindClass("java/net/SocketException"), "Socket is closed"); return; @@ -175,13 +203,16 @@ return; // Set the peer_fd in the Java connectionVSock object jclass VSockImplClass = env->GetObjectClass(connectionVSock); -jfieldID fdField = env->GetFieldID(VSockImplClass, "fd", "I"); -env->SetIntField(connectionVSock, fdField, peer_fd); +jfieldID peerFdField = env->GetFieldID(VSockImplClass, "fd", "I"); +env->SetIntField(connectionVSock, peerFdField, peer_fd); } JNIEXPORT jint JNICALL Java_fr_acinq_lightning_vsock_native_VSockImpl_getLocalCid(JNIEnv *env, jobject thisObj) { -int fd = -1; // Assuming you have stored the fd somewhere accessible +jclass thisClass = env->GetObjectClass(thisObj); +jfieldID fdField = env->GetFieldID(thisClass, "fd", "I"); +int fd = env->GetIntField(thisObj, fdField); + if (fd == -1) { env->ThrowNew(env->FindClass("java/net/SocketException"), "Socket is closed"); return -1; diff --git a/src/commonMain/kotlin/fr/acinq/lightning/vsock/native/VSockImpl.kt b/src/commonMain/kotlin/fr/acinq/lightning/vsock/native/VSockImpl.kt index fa60369..5212a84 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/vsock/native/VSockImpl.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/vsock/native/VSockImpl.kt @@ -1,13 +1,12 @@ package fr.acinq.lightning.vsock.native import fr.acinq.lightning.vsock.VSockAddress -import org.bytedeco.javacpp.Loader +//import com.github.maven_nar.NarSystem import org.bytedeco.javacpp.Pointer import org.bytedeco.javacpp.annotation.Namespace import org.bytedeco.javacpp.annotation.Platform import java.net.SocketException - @Platform(include = [ "", "", @@ -19,7 +18,8 @@ import java.net.SocketException class VSockImpl() : Pointer() { init { - Loader.load() //load the native library + System.load("${System.getProperty("user.dir")}/build/libs/libjniVSockImpl.so") + //NarSystem.loadLibrary() // Load the native library from the .nar file } var fd: Int = -1