diff --git a/README.adoc b/README.adoc index 789b5b1..89f69a3 100644 --- a/README.adoc +++ b/README.adoc @@ -99,6 +99,20 @@ Run the script: * `./secp256k1-examples-java/build/install/secp256k1-examples-java/bin/secp256k1-examples-java` +== Build and run a native image (AMD64-only with Graal JDK 22) + +To build using GraalVM `native-image`: + +. Make sure you have GraalVM 22 or later installed +. Make sure `GRAALVM_HOME` points to the Graal JDK 22 installation +. `./gradlew secp256k1-examples-java:nativeCompile` + +To run the compiled, native executable: + +. `export LD_LIBRARY_PATH="$HOME/.nix-profile/lib:$LD_LIBRARY_PATH"` +. `./secp256k1-examples-java/build/schnorr` +. Don't blink! + == Building with Nix NOTE:: We currently only support setting up a development environment with Nix. In the future we hope to support a full Nix build. diff --git a/secp256k1-examples-java/build.gradle b/secp256k1-examples-java/build.gradle index eb1eab0..b4ff794 100644 --- a/secp256k1-examples-java/build.gradle +++ b/secp256k1-examples-java/build.gradle @@ -13,6 +13,9 @@ dependencies { implementation project(':secp256k1-api') runtimeOnly project(':secp256k1-bouncy') runtimeOnly project(':secp256k1-foreign') + + // This is only needed for ForeignRegistrationFeature and the native-image build + implementation group: 'org.graalvm.sdk', name: 'nativeimage', version: '24.0.0' } jar { @@ -34,3 +37,38 @@ run { systemProperty "java.library.path", findProperty("javaPath") ?: "${userHome}/.nix-profile/lib" jvmArgs += '--enable-native-access=org.bitcoinj.secp256k1.foreign' } + +configurations { + nativeToolImplementation.extendsFrom implementation +} + +def mainClassName = "org.bitcoinj.secp256k1.examples.Schnorr" + +jar { + manifest { + attributes 'Implementation-Title': 'Schnorr Signature Example', + 'Main-Class': mainClassName, + 'Implementation-Version': archiveVersion.get() + } +} + +// Compile a native image using GraalVM's native-image tool +// Graal must be installed at $GRAALVM_HOME +tasks.register('nativeCompile', Exec) { + dependsOn jar + workingDir = projectDir + executable = "${System.env.GRAALVM_HOME}/bin/native-image" + args = ['--verbose', + '--no-fallback', + '-cp', "${-> configurations.nativeToolImplementation.asPath}", // Lazy configuration resolution + '-jar', jar.archiveFile.get(), + '-H:Path=build', + '-H:Name=schnorr', + '-H:+ForeignAPISupport', + '--features=org.bitcoinj.secp256k1.examples.ForeignRegistrationFeature', + '--enable-native-access=ALL-UNNAMED', + '-H:+ReportUnsupportedElementsAtRuntime', + '-H:+ReportExceptionStackTraces' + ] +} + diff --git a/secp256k1-examples-java/src/main/java/module-info.java b/secp256k1-examples-java/src/main/java/module-info.java index be25465..11e65da 100644 --- a/secp256k1-examples-java/src/main/java/module-info.java +++ b/secp256k1-examples-java/src/main/java/module-info.java @@ -15,4 +15,5 @@ */ module org.bitcoinj.secp256k1.examples { requires org.bitcoinj.secp256k1.api; + requires org.graalvm.nativeimage; } diff --git a/secp256k1-examples-java/src/main/java/org/bitcoinj/secp256k1/examples/ForeignRegistrationFeature.java b/secp256k1-examples-java/src/main/java/org/bitcoinj/secp256k1/examples/ForeignRegistrationFeature.java new file mode 100644 index 0000000..d26f1bf --- /dev/null +++ b/secp256k1-examples-java/src/main/java/org/bitcoinj/secp256k1/examples/ForeignRegistrationFeature.java @@ -0,0 +1,46 @@ +/* + * Copyright 2023-2024 secp256k1-jdk Developers. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.bitcoinj.secp256k1.examples; + +import org.graalvm.nativeimage.hosted.Feature; +import org.graalvm.nativeimage.hosted.RuntimeForeignAccess; + +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; + +import static java.lang.foreign.ValueLayout.*; + +/** + * + */ +public class ForeignRegistrationFeature implements Feature { + public void duringSetup(Feature.DuringSetupAccess access) { + RuntimeForeignAccess.registerForDowncall(FunctionDescriptor.ofVoid()); + RuntimeForeignAccess.registerForDowncall(FunctionDescriptor.of(JAVA_INT, JAVA_INT, JAVA_INT)); + RuntimeForeignAccess.registerForDowncall(FunctionDescriptor.of(JAVA_LONG, JAVA_INT)); + RuntimeForeignAccess.registerForDowncall(FunctionDescriptor.of(JAVA_INT, JAVA_INT, JAVA_INT, JAVA_INT)); + RuntimeForeignAccess.registerForDowncall(FunctionDescriptor.of(JAVA_INT, JAVA_LONG, JAVA_LONG, JAVA_LONG)); + RuntimeForeignAccess.registerForDowncall(FunctionDescriptor.of(JAVA_INT, JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG)); + RuntimeForeignAccess.registerForDowncall(FunctionDescriptor.of(JAVA_INT, JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG)); + RuntimeForeignAccess.registerForDowncall(FunctionDescriptor.of(JAVA_INT, JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_INT)); + RuntimeForeignAccess.registerForDowncall(FunctionDescriptor.of(JAVA_INT, JAVA_LONG, JAVA_LONG)); + RuntimeForeignAccess.registerForDowncall(FunctionDescriptor.ofVoid(JAVA_LONG, JAVA_LONG)); + RuntimeForeignAccess.registerForDowncall(FunctionDescriptor.ofVoid(JAVA_LONG)); + RuntimeForeignAccess.registerForDowncall(FunctionDescriptor.ofVoid(JAVA_INT, JAVA_INT)); + RuntimeForeignAccess.registerForDowncall(FunctionDescriptor.of(ADDRESS, JAVA_INT, JAVA_INT), Linker.Option.firstVariadicArg(1)); + RuntimeForeignAccess.registerForDowncall(FunctionDescriptor.ofVoid(JAVA_INT), Linker.Option.captureCallState("errno")); + } +} diff --git a/secp256k1-foreign/src/main/java/org/bitcoinj/secp256k1/foreign/Secp256k1Foreign.java b/secp256k1-foreign/src/main/java/org/bitcoinj/secp256k1/foreign/Secp256k1Foreign.java index 4b47635..437df30 100644 --- a/secp256k1-foreign/src/main/java/org/bitcoinj/secp256k1/foreign/Secp256k1Foreign.java +++ b/secp256k1-foreign/src/main/java/org/bitcoinj/secp256k1/foreign/Secp256k1Foreign.java @@ -73,7 +73,7 @@ public Secp256k1Foreign(int flags) { } public Secp256k1Foreign(int flags, boolean randomize) { - arena = Arena.ofShared(); + arena = Arena.ofConfined(); // Changed from `ofShared` for use in Graal native-image tools /* Before we can call actual API functions, we need to create a "context". */ ctx = secp256k1_h.secp256k1_context_create(flags);