diff --git a/build.sh b/build.sh index 0d74cb61..b7be136a 100755 --- a/build.sh +++ b/build.sh @@ -302,18 +302,27 @@ EOF if [[ "$OSTYPE" == "msys" ]]; then LIBRARY_EXTENSION=dll + STATIC_EXTENSION=lib elif [[ "$OSTYPE" == "linux-gnu"* ]]; then LIBRARY_EXTENSION=so + STATIC_EXTENSION=a elif [[ "$OSTYPE" == "darwin"* ]]; then LIBRARY_EXTENSION=dylib + STATIC_EXTENSION=a export GOROOT=$(brew --prefix go@1.24)/libexec export PATH=$GOROOT/bin:$PATH fi + # Build shared libraries go build -buildmode=c-shared -o libgnark_jni.$LIBRARY_EXTENSION gnark-jni.go go build -buildmode=c-shared -o libgnark_eip_2537.$LIBRARY_EXTENSION gnark-eip-2537.go go build -buildmode=c-shared -o libgnark_eip_196.$LIBRARY_EXTENSION gnark-eip-196.go + # Build static libraries (c-archive creates .a files) + go build -buildmode=c-archive -o libgnark_jni.$STATIC_EXTENSION gnark-jni.go + go build -buildmode=c-archive -o libgnark_eip_2537.$STATIC_EXTENSION gnark-eip-2537.go + go build -buildmode=c-archive -o libgnark_eip_196.$STATIC_EXTENSION gnark-eip-196.go + mkdir -p "$SCRIPTDIR/gnark/build/${OSARCH}/lib" cp libgnark_jni.* "$SCRIPTDIR/gnark/build/${OSARCH}/lib" cp libgnark_eip_2537.* "$SCRIPTDIR/gnark/build/${OSARCH}/lib" diff --git a/gnark/build.gradle b/gnark/build.gradle index a8315018..ecedf674 100644 --- a/gnark/build.gradle +++ b/gnark/build.gradle @@ -19,9 +19,31 @@ plugins { id 'com.jfrog.artifactory' version '5.2.3' } +// Configure source sets - separate GraalVM classes from main JAR +sourceSets { + graal { + java { + srcDir 'src/main/java' + include '**/*Graal*.java' + } + // Graal source set needs access to main source set classes + compileClasspath += sourceSets.main.output + } + main { + java { + exclude '**/*Graal*.java' + } + } +} + dependencies { implementation 'net.java.dev.jna:jna:5.12.1' implementation project(':common') + + // GraalVM SDK only for graal source set + graalImplementation 'org.graalvm.sdk:graal-sdk:24.0.2' + graalImplementation project(':common') + graalImplementation 'net.java.dev.jna:jna:5.12.1' testImplementation 'com.google.guava:guava:31.1-jre' testImplementation 'net.java.dev.jna:jna:5.12.1' testImplementation 'io.consensys.tuweni:tuweni-bytes:2.7.1' @@ -30,6 +52,7 @@ dependencies { testImplementation 'org.mockito:mockito-core:4.4.0' } +// Dynamic library copy tasks (for JNA) task macArmLibCopy(type: Copy) { from 'build/darwin-aarch64/lib/libgnark_jni.dylib' from 'build/darwin-aarch64/lib/libgnark_eip_2537.dylib' @@ -70,6 +93,63 @@ task linuxRiscv64LibCopy(type: Copy) { } processResources.dependsOn linuxRiscv64LibCopy +// Static library copy tasks (for GraalVM native-image) +task macArmStaticLibCopy(type: Copy) { + from 'build/darwin-aarch64/lib/libgnark_jni.a' + from 'build/darwin-aarch64/lib/libgnark_eip_2537.a' + from 'build/darwin-aarch64/lib/libgnark_eip_196.a' + from 'build/darwin-aarch64/lib/libgnark_jni.h' + from 'build/darwin-aarch64/lib/libgnark_eip_2537.h' + from 'build/darwin-aarch64/lib/libgnark_eip_196.h' + into 'build/resources-static/lib/aarch64' +} + +task macStaticLibCopy(type: Copy) { + from 'build/darwin-x86-64/lib/libgnark_jni.a' + from 'build/darwin-x86-64/lib/libgnark_eip_2537.a' + from 'build/darwin-x86-64/lib/libgnark_eip_196.a' + from 'build/darwin-x86-64/lib/libgnark_jni.h' + from 'build/darwin-x86-64/lib/libgnark_eip_2537.h' + from 'build/darwin-x86-64/lib/libgnark_eip_196.h' + into 'build/resources-static/lib/x86-64' +} + +task linuxStaticLibCopy(type: Copy) { + from 'build/linux-gnu-x86_64/lib/libgnark_jni.a' + from 'build/linux-gnu-x86_64/lib/libgnark_eip_2537.a' + from 'build/linux-gnu-x86_64/lib/libgnark_eip_196.a' + from 'build/linux-gnu-x86_64/lib/libgnark_jni.h' + from 'build/linux-gnu-x86_64/lib/libgnark_eip_2537.h' + from 'build/linux-gnu-x86_64/lib/libgnark_eip_196.h' + into 'build/resources-static/lib/x86-64' +} + +task linuxArm64StaticLibCopy(type: Copy) { + from 'build/linux-gnu-aarch64/lib/libgnark_jni.a' + from 'build/linux-gnu-aarch64/lib/libgnark_eip_2537.a' + from 'build/linux-gnu-aarch64/lib/libgnark_eip_196.a' + from 'build/linux-gnu-aarch64/lib/libgnark_jni.h' + from 'build/linux-gnu-aarch64/lib/libgnark_eip_2537.h' + from 'build/linux-gnu-aarch64/lib/libgnark_eip_196.h' + into 'build/resources-static/lib/aarch64' +} + +task linuxRiscv64StaticLibCopy(type: Copy) { + from 'build/linux-gnu-riscv64/lib/libgnark_jni.a' + from 'build/linux-gnu-riscv64/lib/libgnark_eip_2537.a' + from 'build/linux-gnu-riscv64/lib/libgnark_eip_196.a' + from 'build/linux-gnu-riscv64/lib/libgnark_jni.h' + from 'build/linux-gnu-riscv64/lib/libgnark_eip_2537.h' + from 'build/linux-gnu-riscv64/lib/libgnark_eip_196.h' + into 'build/resources-static/lib/riscv64' +} + +// Task to prepare static resources +task prepareStaticResources { + dependsOn macArmStaticLibCopy, macStaticLibCopy, linuxStaticLibCopy, + linuxArm64StaticLibCopy, linuxRiscv64StaticLibCopy +} + jar { archiveBaseName = 'besu-native-gnark' includeEmptyDirs = false @@ -96,6 +176,14 @@ task javadocJar(type: Jar, dependsOn: javadoc) { from javadoc.destinationDir } +task staticJar(type: Jar, dependsOn: [prepareStaticResources, graalClasses]) { + archiveBaseName = 'besu-native-gnark' + archiveClassifier = 'static' + from 'build/resources-static' + from sourceSets.graal.output + includeEmptyDirs = false +} + publishing { publications { @@ -107,6 +195,7 @@ publishing { from components.java artifact sourcesJar artifact javadocJar + artifact staticJar pom { name = "Besu Native - ${project.name}" diff --git a/gnark/src/main/java/org/hyperledger/besu/nativelib/gnark/LibGnarkEIP196Graal.java b/gnark/src/main/java/org/hyperledger/besu/nativelib/gnark/LibGnarkEIP196Graal.java new file mode 100644 index 00000000..ae41c832 --- /dev/null +++ b/gnark/src/main/java/org/hyperledger/besu/nativelib/gnark/LibGnarkEIP196Graal.java @@ -0,0 +1,239 @@ +/* + * Copyright contributors to Besu. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.hyperledger.besu.nativelib.gnark; + +import org.graalvm.nativeimage.PinnedObject; +import org.graalvm.nativeimage.c.CContext; +import org.graalvm.nativeimage.c.function.CFunction; +import org.graalvm.nativeimage.c.type.CCharPointer; +import org.graalvm.nativeimage.c.type.CIntPointer; + +import java.util.Collections; +import java.util.List; + +/** + * GraalVM native-image compatible interface to gnark EIP-196 static library. + * Provides operations for Alt-BN128 elliptic curve operations including G1 addition, + * scalar multiplication, and pairing checks. + */ +public class LibGnarkEIP196Graal { + + /** Recommended buffer size for operation results. */ + public static final int EIP196_PREALLOCATE_FOR_RESULT_BYTES = 128; + + /** Recommended buffer size for error messages. */ + public static final int EIP196_PREALLOCATE_FOR_ERROR_BYTES = 256; + + /** Operation code for G1 point addition. */ + public static final byte EIP196_ADD_OPERATION_RAW_VALUE = 1; + + /** Operation code for G1 scalar multiplication. */ + public static final byte EIP196_MUL_OPERATION_RAW_VALUE = 2; + + /** Operation code for pairing check. */ + public static final byte EIP196_PAIR_OPERATION_RAW_VALUE = 3; + + /** Private constructor to prevent instantiation of utility class. */ + private LibGnarkEIP196Graal() {} + + @CContext(LibGnarkEIP196Graal.Directives.class) + public static class Directives implements CContext.Directives { + @Override + public List getHeaderFiles() { + return Collections.singletonList(""); + } + + @Override + public List getLibraries() { + return Collections.singletonList("gnark_eip_196"); + } + + @Override + public List getLibraryPaths() { + // Library paths should be configured via native-image build arguments + return Collections.emptyList(); + } + } + + @CFunction(value = "eip196altbn128G1Add") + public static native int eip196altbn128G1AddNative( + CCharPointer input, + CCharPointer output, + CCharPointer error, + int inputSize, + CIntPointer outputSize, + CIntPointer errorSize); + + @CFunction(value = "eip196altbn128G1Mul") + public static native int eip196altbn128G1MulNative( + CCharPointer input, + CCharPointer output, + CCharPointer error, + int inputSize, + CIntPointer outputSize, + CIntPointer errorSize); + + @CFunction(value = "eip196altbn128Pairing") + public static native int eip196altbn128PairingNative( + CCharPointer input, + CCharPointer output, + CCharPointer error, + int inputSize, + CIntPointer outputSize, + CIntPointer errorSize); + + /** + * Compatibility shim for the pre-existing matter-labs implementation. + * Routes operation codes to the appropriate native function. + * + * @param op operation code (ADD, MUL, or PAIR) + * @param input input data buffer + * @param inputLength length of input data + * @param output output data buffer + * @param outputSize output size reference (updated by native function) + * @param error error message buffer + * @param errorSize error size reference (updated by native function) + * @return result code from native function + */ + public static int eip196_perform_operation( + byte op, + byte[] input, + int inputLength, + byte[] output, + int[] outputSize, + byte[] error, + int[] errorSize) { + + int ret = -1; + switch(op) { + case EIP196_ADD_OPERATION_RAW_VALUE: + ret = eip196altbn128G1Add(input, output, error, inputLength, outputSize, errorSize); + break; + case EIP196_MUL_OPERATION_RAW_VALUE: + ret = eip196altbn128G1Mul(input, output, error, inputLength, outputSize, errorSize); + break; + case EIP196_PAIR_OPERATION_RAW_VALUE: + ret = eip196altbn128Pairing(input, output, error, inputLength, outputSize, errorSize); + break; + default: + throw new RuntimeException("Not Implemented EIP-196 operation " + op); + } + + return ret; + } + + /** + * Java-friendly wrapper for Alt-BN128 G1 point addition. + * + * @param input input data containing two G1 points to add + * @param output output buffer for result + * @param error error message buffer + * @param inputSize size of input data + * @param outputSize output size reference (updated by native function) + * @param errorSize error size reference (updated by native function) + * @return result code from native function + */ + public static int eip196altbn128G1Add( + byte[] input, + byte[] output, + byte[] error, + int inputSize, + int[] outputSize, + int[] errorSize) { + try (PinnedObject pinnedInput = PinnedObject.create(input); + PinnedObject pinnedOutput = PinnedObject.create(output); + PinnedObject pinnedError = PinnedObject.create(error); + PinnedObject pinnedOutputSize = PinnedObject.create(outputSize); + PinnedObject pinnedErrorSize = PinnedObject.create(errorSize)) { + return eip196altbn128G1AddNative( + pinnedInput.addressOfArrayElement(0), + pinnedOutput.addressOfArrayElement(0), + pinnedError.addressOfArrayElement(0), + inputSize, + pinnedOutputSize.addressOfArrayElement(0), + pinnedErrorSize.addressOfArrayElement(0) + ); + } + } + + /** + * Java-friendly wrapper for Alt-BN128 G1 scalar multiplication. + * + * @param input input data containing G1 point and scalar + * @param output output buffer for result + * @param error error message buffer + * @param inputSize size of input data + * @param outputSize output size reference (updated by native function) + * @param errorSize error size reference (updated by native function) + * @return result code from native function + */ + public static int eip196altbn128G1Mul( + byte[] input, + byte[] output, + byte[] error, + int inputSize, + int[] outputSize, + int[] errorSize) { + try (PinnedObject pinnedInput = PinnedObject.create(input); + PinnedObject pinnedOutput = PinnedObject.create(output); + PinnedObject pinnedError = PinnedObject.create(error); + PinnedObject pinnedOutputSize = PinnedObject.create(outputSize); + PinnedObject pinnedErrorSize = PinnedObject.create(errorSize)) { + return eip196altbn128G1MulNative( + pinnedInput.addressOfArrayElement(0), + pinnedOutput.addressOfArrayElement(0), + pinnedError.addressOfArrayElement(0), + inputSize, + pinnedOutputSize.addressOfArrayElement(0), + pinnedErrorSize.addressOfArrayElement(0) + ); + } + } + + /** + * Java-friendly wrapper for Alt-BN128 pairing check operation. + * + * @param input input data containing pairs of G1 and G2 points + * @param output output buffer for result (boolean encoded as bytes) + * @param error error message buffer + * @param inputSize size of input data + * @param outputSize output size reference (updated by native function) + * @param errorSize error size reference (updated by native function) + * @return result code from native function + */ + public static int eip196altbn128Pairing( + byte[] input, + byte[] output, + byte[] error, + int inputSize, + int[] outputSize, + int[] errorSize) { + try (PinnedObject pinnedInput = PinnedObject.create(input); + PinnedObject pinnedOutput = PinnedObject.create(output); + PinnedObject pinnedError = PinnedObject.create(error); + PinnedObject pinnedOutputSize = PinnedObject.create(outputSize); + PinnedObject pinnedErrorSize = PinnedObject.create(errorSize)) { + return eip196altbn128PairingNative( + pinnedInput.addressOfArrayElement(0), + pinnedOutput.addressOfArrayElement(0), + pinnedError.addressOfArrayElement(0), + inputSize, + pinnedOutputSize.addressOfArrayElement(0), + pinnedErrorSize.addressOfArrayElement(0) + ); + } + } +} diff --git a/gnark/src/main/java/org/hyperledger/besu/nativelib/gnark/LibGnarkEIP2537Graal.java b/gnark/src/main/java/org/hyperledger/besu/nativelib/gnark/LibGnarkEIP2537Graal.java new file mode 100644 index 00000000..c5569cd4 --- /dev/null +++ b/gnark/src/main/java/org/hyperledger/besu/nativelib/gnark/LibGnarkEIP2537Graal.java @@ -0,0 +1,570 @@ +/* + * Copyright contributors to Besu. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.hyperledger.besu.nativelib.gnark; + +import org.graalvm.nativeimage.PinnedObject; +import org.graalvm.nativeimage.c.CContext; +import org.graalvm.nativeimage.c.function.CFunction; +import org.graalvm.nativeimage.c.type.CCharPointer; + +import java.util.Collections; +import java.util.List; + +/** + * GraalVM native-image compatible interface to gnark EIP-2537 static library. + * Provides operations for BLS12-381 elliptic curve operations including G1/G2 addition, + * multi-scalar multiplication, pairing checks, and curve/subgroup validation. + */ +public class LibGnarkEIP2537Graal { + + /** Recommended buffer size for operation results. */ + public static final int EIP2537_PREALLOCATE_FOR_RESULT_BYTES = 256; + + /** Recommended buffer size for error messages. */ + public static final int EIP2537_PREALLOCATE_FOR_ERROR_BYTES = 256; + + /** Operation code for BLS12-381 G1 point addition. */ + public static final byte BLS12_G1ADD_OPERATION_SHIM_VALUE = 1; + + /** Operation code for BLS12-381 G1 multi-scalar multiplication. */ + public static final byte BLS12_G1MULTIEXP_OPERATION_SHIM_VALUE = 2; + + /** Operation code for BLS12-381 G2 point addition. */ + public static final byte BLS12_G2ADD_OPERATION_SHIM_VALUE = 3; + + /** Operation code for BLS12-381 G2 multi-scalar multiplication. */ + public static final byte BLS12_G2MULTIEXP_OPERATION_SHIM_VALUE = 4; + + /** Operation code for BLS12-381 pairing check. */ + public static final byte BLS12_PAIR_OPERATION_SHIM_VALUE = 5; + + /** Operation code for mapping field element to G1. */ + public static final byte BLS12_MAP_FP_TO_G1_OPERATION_SHIM_VALUE = 6; + + /** Operation code for mapping field element to G2. */ + public static final byte BLS12_MAP_FP2_TO_G2_OPERATION_SHIM_VALUE = 7; + + /** Degree of parallelism for multi-scalar multiplication (0 = auto-detect CPU cores). */ + private static int degreeOfMSMParallelism = 0; + + /** Private constructor to prevent instantiation of utility class. */ + private LibGnarkEIP2537Graal() {} + + @CContext(LibGnarkEIP2537Graal.Directives.class) + public static class Directives implements CContext.Directives { + @Override + public List getHeaderFiles() { + return Collections.singletonList(""); + } + + @Override + public List getLibraries() { + return Collections.singletonList("gnark_eip_2537"); + } + + @Override + public List getLibraryPaths() { + // Library paths should be configured via native-image build arguments + return Collections.emptyList(); + } + } + + @CFunction(value = "eip2537blsG1Add") + public static native int eip2537blsG1AddNative( + CCharPointer input, + CCharPointer output, + CCharPointer error, + int inputSize, + int outputLength, + int errorLength); + + @CFunction(value = "eip2537blsG1MultiExp") + public static native int eip2537blsG1MultiExpNative( + CCharPointer input, + CCharPointer output, + CCharPointer error, + int inputSize, + int outputLength, + int errorLength, + int nbTasks); + + @CFunction(value = "eip2537blsG2Add") + public static native int eip2537blsG2AddNative( + CCharPointer input, + CCharPointer output, + CCharPointer error, + int inputSize, + int outputLength, + int errorLength); + + @CFunction(value = "eip2537blsG2MultiExp") + public static native int eip2537blsG2MultiExpNative( + CCharPointer input, + CCharPointer output, + CCharPointer error, + int inputSize, + int outputLength, + int errorLength, + int nbTasks); + + @CFunction(value = "eip2537blsPairing") + public static native int eip2537blsPairingNative( + CCharPointer input, + CCharPointer output, + CCharPointer error, + int inputSize, + int outputLength, + int errorLength); + + @CFunction(value = "eip2537blsMapFpToG1") + public static native int eip2537blsMapFpToG1Native( + CCharPointer input, + CCharPointer output, + CCharPointer error, + int inputSize, + int outputLength, + int errorLength); + + @CFunction(value = "eip2537blsMapFp2ToG2") + public static native int eip2537blsMapFp2ToG2Native( + CCharPointer input, + CCharPointer output, + CCharPointer error, + int inputSize, + int outputLength, + int errorLength); + + @CFunction(value = "eip2537G1IsOnCurve") + public static native boolean eip2537G1IsOnCurveNative( + CCharPointer input, + CCharPointer error, + int inputSize, + int errorLength); + + @CFunction(value = "eip2537G2IsOnCurve") + public static native boolean eip2537G2IsOnCurveNative( + CCharPointer input, + CCharPointer error, + int inputSize, + int errorLength); + + @CFunction(value = "eip2537G1IsInSubGroup") + public static native boolean eip2537G1IsInSubGroupNative( + CCharPointer input, + CCharPointer error, + int inputSize, + int errorLength); + + @CFunction(value = "eip2537G2IsInSubGroup") + public static native boolean eip2537G2IsInSubGroupNative( + CCharPointer input, + CCharPointer error, + int inputSize, + int errorLength); + + /** + * Here as a compatibility shim for the pre-existing matter-labs implementation. + * + * IMPORTANT: The output buffer MUST be zero-initialized before calling this method. + * The native implementation relies on this pre-initialization for proper functioning. + */ + public static int eip2537_perform_operation( + byte op, + byte[] input, + int inputLength, + byte[] output, + int[] outputLength, + byte[] error, + int[] errorLength) { + + int ret = -1; + switch(op) { + case BLS12_G1ADD_OPERATION_SHIM_VALUE: + ret = eip2537blsG1Add(input, output, error, inputLength, + EIP2537_PREALLOCATE_FOR_RESULT_BYTES, + EIP2537_PREALLOCATE_FOR_ERROR_BYTES); + outputLength[0] = 128; + break; + case BLS12_G1MULTIEXP_OPERATION_SHIM_VALUE: + ret = eip2537blsG1MultiExp(input, output, error, inputLength, + EIP2537_PREALLOCATE_FOR_RESULT_BYTES, + EIP2537_PREALLOCATE_FOR_ERROR_BYTES, + degreeOfMSMParallelism); + outputLength[0] = 128; + break; + case BLS12_G2ADD_OPERATION_SHIM_VALUE: + ret = eip2537blsG2Add(input, output, error, inputLength, + EIP2537_PREALLOCATE_FOR_RESULT_BYTES, + EIP2537_PREALLOCATE_FOR_ERROR_BYTES); + outputLength[0] = 256; + break; + case BLS12_G2MULTIEXP_OPERATION_SHIM_VALUE: + ret = eip2537blsG2MultiExp(input, output, error, inputLength, + EIP2537_PREALLOCATE_FOR_RESULT_BYTES, + EIP2537_PREALLOCATE_FOR_ERROR_BYTES, + degreeOfMSMParallelism); + outputLength[0] = 256; + break; + case BLS12_PAIR_OPERATION_SHIM_VALUE: + ret = eip2537blsPairing(input, output, error, inputLength, + EIP2537_PREALLOCATE_FOR_RESULT_BYTES, + EIP2537_PREALLOCATE_FOR_ERROR_BYTES); + outputLength[0] = 32; + break; + case BLS12_MAP_FP_TO_G1_OPERATION_SHIM_VALUE: + ret = eip2537blsMapFpToG1(input, output, error, inputLength, + EIP2537_PREALLOCATE_FOR_RESULT_BYTES, + EIP2537_PREALLOCATE_FOR_ERROR_BYTES); + outputLength[0] = 128; + break; + case BLS12_MAP_FP2_TO_G2_OPERATION_SHIM_VALUE: + ret = eip2537blsMapFp2ToG2(input, output, error, inputLength, + EIP2537_PREALLOCATE_FOR_RESULT_BYTES, + EIP2537_PREALLOCATE_FOR_ERROR_BYTES); + outputLength[0] = 256; + break; + default: + throw new RuntimeException("Not Implemented EIP-2537 operation " + op); + } + + if (ret != 0) { + errorLength[0] = LibGnarkUtils.findFirstTrailingZeroIndex(error); + outputLength[0] = 0; + } else { + errorLength[0] = 0; + } + return ret; + } + + /** + * Java-friendly wrapper for BLS12-381 G1 point addition. + * + * @param input input data containing two G1 points to add + * @param output output buffer for result + * @param error error message buffer + * @param inputSize size of input data + * @param outputLength size of output buffer + * @param errorLength size of error buffer + * @return result code from native function (0 = success) + */ + public static int eip2537blsG1Add( + byte[] input, + byte[] output, + byte[] error, + int inputSize, + int outputLength, + int errorLength) { + try (PinnedObject pinnedInput = PinnedObject.create(input); + PinnedObject pinnedOutput = PinnedObject.create(output); + PinnedObject pinnedError = PinnedObject.create(error)) { + return eip2537blsG1AddNative( + pinnedInput.addressOfArrayElement(0), + pinnedOutput.addressOfArrayElement(0), + pinnedError.addressOfArrayElement(0), + inputSize, outputLength, errorLength + ); + } + } + + /** + * Java-friendly wrapper for BLS12-381 G1 multi-scalar multiplication. + * + * @param input input data containing G1 points and scalars + * @param output output buffer for result + * @param error error message buffer + * @param inputSize size of input data + * @param outputLength size of output buffer + * @param errorLength size of error buffer + * @param nbTasks number of parallel tasks (0 = auto-detect) + * @return result code from native function (0 = success) + */ + public static int eip2537blsG1MultiExp( + byte[] input, + byte[] output, + byte[] error, + int inputSize, + int outputLength, + int errorLength, + int nbTasks) { + try (PinnedObject pinnedInput = PinnedObject.create(input); + PinnedObject pinnedOutput = PinnedObject.create(output); + PinnedObject pinnedError = PinnedObject.create(error)) { + return eip2537blsG1MultiExpNative( + pinnedInput.addressOfArrayElement(0), + pinnedOutput.addressOfArrayElement(0), + pinnedError.addressOfArrayElement(0), + inputSize, outputLength, errorLength, nbTasks + ); + } + } + + /** + * Java-friendly wrapper for BLS12-381 G2 point addition. + * + * @param input input data containing two G2 points to add + * @param output output buffer for result + * @param error error message buffer + * @param inputSize size of input data + * @param outputLength size of output buffer + * @param errorLength size of error buffer + * @return result code from native function (0 = success) + */ + public static int eip2537blsG2Add( + byte[] input, + byte[] output, + byte[] error, + int inputSize, + int outputLength, + int errorLength) { + try (PinnedObject pinnedInput = PinnedObject.create(input); + PinnedObject pinnedOutput = PinnedObject.create(output); + PinnedObject pinnedError = PinnedObject.create(error)) { + return eip2537blsG2AddNative( + pinnedInput.addressOfArrayElement(0), + pinnedOutput.addressOfArrayElement(0), + pinnedError.addressOfArrayElement(0), + inputSize, outputLength, errorLength + ); + } + } + + /** + * Java-friendly wrapper for BLS12-381 G2 multi-scalar multiplication. + * + * @param input input data containing G2 points and scalars + * @param output output buffer for result + * @param error error message buffer + * @param inputSize size of input data + * @param outputLength size of output buffer + * @param errorLength size of error buffer + * @param nbTasks number of parallel tasks (0 = auto-detect) + * @return result code from native function (0 = success) + */ + public static int eip2537blsG2MultiExp( + byte[] input, + byte[] output, + byte[] error, + int inputSize, + int outputLength, + int errorLength, + int nbTasks) { + try (PinnedObject pinnedInput = PinnedObject.create(input); + PinnedObject pinnedOutput = PinnedObject.create(output); + PinnedObject pinnedError = PinnedObject.create(error)) { + return eip2537blsG2MultiExpNative( + pinnedInput.addressOfArrayElement(0), + pinnedOutput.addressOfArrayElement(0), + pinnedError.addressOfArrayElement(0), + inputSize, outputLength, errorLength, nbTasks + ); + } + } + + /** + * Java-friendly wrapper for BLS12-381 pairing check operation. + * + * @param input input data containing pairs of G1 and G2 points + * @param output output buffer for result (boolean encoded as bytes) + * @param error error message buffer + * @param inputSize size of input data + * @param outputLength size of output buffer + * @param errorLength size of error buffer + * @return result code from native function (0 = success) + */ + public static int eip2537blsPairing( + byte[] input, + byte[] output, + byte[] error, + int inputSize, + int outputLength, + int errorLength) { + try (PinnedObject pinnedInput = PinnedObject.create(input); + PinnedObject pinnedOutput = PinnedObject.create(output); + PinnedObject pinnedError = PinnedObject.create(error)) { + return eip2537blsPairingNative( + pinnedInput.addressOfArrayElement(0), + pinnedOutput.addressOfArrayElement(0), + pinnedError.addressOfArrayElement(0), + inputSize, outputLength, errorLength + ); + } + } + + /** + * Java-friendly wrapper for mapping field element to BLS12-381 G1 point. + * + * @param input input data containing field element + * @param output output buffer for resulting G1 point + * @param error error message buffer + * @param inputSize size of input data + * @param outputLength size of output buffer + * @param errorLength size of error buffer + * @return result code from native function (0 = success) + */ + public static int eip2537blsMapFpToG1( + byte[] input, + byte[] output, + byte[] error, + int inputSize, + int outputLength, + int errorLength) { + try (PinnedObject pinnedInput = PinnedObject.create(input); + PinnedObject pinnedOutput = PinnedObject.create(output); + PinnedObject pinnedError = PinnedObject.create(error)) { + return eip2537blsMapFpToG1Native( + pinnedInput.addressOfArrayElement(0), + pinnedOutput.addressOfArrayElement(0), + pinnedError.addressOfArrayElement(0), + inputSize, outputLength, errorLength + ); + } + } + + /** + * Java-friendly wrapper for mapping field element to BLS12-381 G2 point. + * + * @param input input data containing field element + * @param output output buffer for resulting G2 point + * @param error error message buffer + * @param inputSize size of input data + * @param outputLength size of output buffer + * @param errorLength size of error buffer + * @return result code from native function (0 = success) + */ + public static int eip2537blsMapFp2ToG2( + byte[] input, + byte[] output, + byte[] error, + int inputSize, + int outputLength, + int errorLength) { + try (PinnedObject pinnedInput = PinnedObject.create(input); + PinnedObject pinnedOutput = PinnedObject.create(output); + PinnedObject pinnedError = PinnedObject.create(error)) { + return eip2537blsMapFp2ToG2Native( + pinnedInput.addressOfArrayElement(0), + pinnedOutput.addressOfArrayElement(0), + pinnedError.addressOfArrayElement(0), + inputSize, outputLength, errorLength + ); + } + } + + /** + * Java-friendly wrapper for checking if a point is on the BLS12-381 G1 curve. + * + * @param input input data containing G1 point to validate + * @param error error message buffer + * @param inputSize size of input data + * @param errorLength size of error buffer + * @return true if point is on curve, false otherwise + */ + public static boolean eip2537G1IsOnCurve( + byte[] input, + byte[] error, + int inputSize, + int errorLength) { + try (PinnedObject pinnedInput = PinnedObject.create(input); + PinnedObject pinnedError = PinnedObject.create(error)) { + return eip2537G1IsOnCurveNative( + pinnedInput.addressOfArrayElement(0), + pinnedError.addressOfArrayElement(0), + inputSize, errorLength + ); + } + } + + /** + * Java-friendly wrapper for checking if a point is on the BLS12-381 G2 curve. + * + * @param input input data containing G2 point to validate + * @param error error message buffer + * @param inputSize size of input data + * @param errorLength size of error buffer + * @return true if point is on curve, false otherwise + */ + public static boolean eip2537G2IsOnCurve( + byte[] input, + byte[] error, + int inputSize, + int errorLength) { + try (PinnedObject pinnedInput = PinnedObject.create(input); + PinnedObject pinnedError = PinnedObject.create(error)) { + return eip2537G2IsOnCurveNative( + pinnedInput.addressOfArrayElement(0), + pinnedError.addressOfArrayElement(0), + inputSize, errorLength + ); + } + } + + /** + * Java-friendly wrapper for checking if a G1 point is in the correct subgroup. + * + * @param input input data containing G1 point to validate + * @param error error message buffer + * @param inputSize size of input data + * @param errorLength size of error buffer + * @return true if point is in subgroup, false otherwise + */ + public static boolean eip2537G1IsInSubGroup( + byte[] input, + byte[] error, + int inputSize, + int errorLength) { + try (PinnedObject pinnedInput = PinnedObject.create(input); + PinnedObject pinnedError = PinnedObject.create(error)) { + return eip2537G1IsInSubGroupNative( + pinnedInput.addressOfArrayElement(0), + pinnedError.addressOfArrayElement(0), + inputSize, errorLength + ); + } + } + + /** + * Java-friendly wrapper for checking if a G2 point is in the correct subgroup. + * + * @param input input data containing G2 point to validate + * @param error error message buffer + * @param inputSize size of input data + * @param errorLength size of error buffer + * @return true if point is in subgroup, false otherwise + */ + public static boolean eip2537G2IsInSubGroup( + byte[] input, + byte[] error, + int inputSize, + int errorLength) { + try (PinnedObject pinnedInput = PinnedObject.create(input); + PinnedObject pinnedError = PinnedObject.create(error)) { + return eip2537G2IsInSubGroupNative( + pinnedInput.addressOfArrayElement(0), + pinnedError.addressOfArrayElement(0), + inputSize, errorLength + ); + } + } + + /** + * Sets the degree of parallelism for multi-scalar multiplication operations. + * + * @param nbTasks number of parallel tasks (0 = auto-detect CPU cores) + */ + public static void setDegreeOfMSMParallelism(int nbTasks) { + degreeOfMSMParallelism = nbTasks; + } +} diff --git a/gnark/src/main/java/org/hyperledger/besu/nativelib/gnark/LibGnarkGraal.java b/gnark/src/main/java/org/hyperledger/besu/nativelib/gnark/LibGnarkGraal.java new file mode 100644 index 00000000..4d0c824e --- /dev/null +++ b/gnark/src/main/java/org/hyperledger/besu/nativelib/gnark/LibGnarkGraal.java @@ -0,0 +1,90 @@ +/* + * Copyright contributors to Besu. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.hyperledger.besu.nativelib.gnark; + +import org.graalvm.nativeimage.PinnedObject; +import org.graalvm.nativeimage.c.CContext; +import org.graalvm.nativeimage.c.function.CFunction; +import org.graalvm.nativeimage.c.type.CCharPointer; +import org.graalvm.word.WordFactory; + +import java.util.Collections; +import java.util.List; + +/** + * GraalVM native-image compatible interface to gnark static library + */ +public class LibGnarkGraal { + + @CContext(LibGnarkGraal.Directives.class) + public static class Directives implements CContext.Directives { + @Override + public List getHeaderFiles() { + return Collections.singletonList(""); + } + + @Override + public List getLibraries() { + return Collections.singletonList("gnark_jni"); + } + + @Override + public List getLibraryPaths() { + // Library paths should be configured via native-image build arguments + return Collections.emptyList(); + } + } + + @CFunction(value = "computeMimcBn254") + public static native int computeMimcBn254Native( + CCharPointer input, + int inputLength, + CCharPointer output); + + @CFunction(value = "computeMimcBls12377") + public static native int computeMimcBls12377Native( + CCharPointer input, + int inputLength, + CCharPointer output); + + /** + * Java-friendly wrapper for computeMimcBn254 + */ + public static int computeMimcBn254(byte[] input, int inputLength, byte[] output) { + try (PinnedObject pinnedInput = PinnedObject.create(input); + PinnedObject pinnedOutput = PinnedObject.create(output)) { + return computeMimcBn254Native( + pinnedInput.addressOfArrayElement(0), + inputLength, + pinnedOutput.addressOfArrayElement(0) + ); + } + } + + /** + * Java-friendly wrapper for computeMimcBls12377 + */ + public static int computeMimcBls12377(byte[] input, int inputLength, byte[] output) { + try (PinnedObject pinnedInput = PinnedObject.create(input); + PinnedObject pinnedOutput = PinnedObject.create(output)) { + return computeMimcBls12377Native( + pinnedInput.addressOfArrayElement(0), + inputLength, + pinnedOutput.addressOfArrayElement(0) + ); + } + } +} diff --git a/gradle.properties b/gradle.properties index 3768476e..5a1c848c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=1.4.1-SNAPSHOT +version=1.4.1-SNASPSHOT