diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c4993d7e..ba39edce 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,7 @@ on: jobs: native-build-linux-x86-64: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 env: SKIP_GRADLE: true steps: @@ -111,6 +111,10 @@ jobs: with: name: gnark native build artifacts path: gnark/build/ + - uses: actions/upload-artifact@v3.1.0 + with: + name: constantine native build artifacts + path: constantine/build/ native-build-macos: runs-on: macos-13 @@ -258,7 +262,7 @@ jobs: path: constantine/build/ final-assembly: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: - native-build-macos - native-build-m1 @@ -349,6 +353,10 @@ jobs: with: name: jars path: gnark/build/libs + - uses: actions/upload-artifact@v3.1.0 + with: + name: jars + path: constantine/build/libs - name: gradle publish uses: gradle/gradle-build-action@v2 if: contains('refs/heads/release-', github.ref) || github.ref == 'refs/heads/main' diff --git a/build.sh b/build.sh index 4e5620ff..17413d62 100755 --- a/build.sh +++ b/build.sh @@ -307,6 +307,8 @@ EOF LIBRARY_EXTENSION=so elif [[ "$OSTYPE" == "darwin"* ]]; then LIBRARY_EXTENSION=dylib + export GOROOT=$(brew --prefix go@1.22)/libexec + export PATH=$GOROOT/bin:$PATH fi go build -buildmode=c-shared -o libgnark_jni.$LIBRARY_EXTENSION gnark-jni.go @@ -324,33 +326,45 @@ build_constantine() { echo "####### build constantine ####" echo "#############################" - # Skip if OSARCH is linux-gnu-aarch64 - if [[ "$OSARCH" == "linux-gnu-aarch64" ]]; then - echo "Skipping build for OSARCH: ${OSARCH}" - return - fi - cd "$SCRIPTDIR/constantine/constantine" # delete old build dir, if exists rm -rf "$SCRIPTDIR/constantine/build" || true mkdir -p "$SCRIPTDIR/constantine/build/${OSARCH}/lib" - export PATH=$HOME/.nimble/bin:$PATH + # Check and modify config.nims if on x86_64 + if [[ "$OSARCH" == "linux-gnu-x86_64" ]]; then + # Check if the config.nims already contains the necessary flags + if ! grep -q 'switch("passC", "-fPIC")' config.nims; then + { + echo 'when defined(linux):' + echo ' switch("passC", "-fPIC")' + echo ' switch("passL", "-fPIC")' + } >> config.nims + fi + fi # Build the constantine library export CTT_LTO=false - nimble make_lib + if [[ "$OSARCH" == "linux-gnu-aarch64" ]]; then + # Download and extract Nim + wget https://github.com/nim-lang/nightlies/releases/download/2024-03-28-version-2-0-b47747d31844c6bd9af4322efe55e24fefea544c/nim-2.0.4-linux_arm64.tar.xz + tar -xf nim-2.0.4-linux_arm64.tar.xz + git config --global --add safe.directory /home/ubuntu/constantine/constantine + export PATH=$(pwd)/nim-2.0.4/bin:$PATH + nimble make_lib + else + export PATH=$HOME/.nimble/bin:$PATH + nimble make_lib + fi cd "$SCRIPTDIR/constantine/" # Compile the native library if [[ "$OSTYPE" == "darwin"* ]]; then - cp "$SCRIPTDIR/constantine/constantine/lib/libconstantine.dylib" "$SCRIPTDIR/constantine/build/${OSARCH}/lib/" - clang -I"${JAVA_HOME}/include" -I"${JAVA_HOME}/include/darwin" -shared -o "$SCRIPTDIR/constantine/build/${OSARCH}/lib/libconstantineeip196.jnilib" ethereum_evm_precompiles.c -Iconstantine/include -I. -Lconstantine/lib -lconstantine + clang -I"${JAVA_HOME}/include" -I"${JAVA_HOME}/include/darwin" -shared -o "$SCRIPTDIR/constantine/build/${OSARCH}/lib/libconstantineeip196.dylib" jna_ethereum_evm_precompiles.c -Iconstantine/include -I. constantine/lib/libconstantine.a elif [[ "$OSTYPE" == "linux-gnu"* ]]; then - cp "$SCRIPTDIR/constantine/constantine/lib/libconstantine.so" "$SCRIPTDIR/constantine/build/${OSARCH}/lib/" - clang -I"${JAVA_HOME}/include" -I"${JAVA_HOME}/include/linux" -fPIC -shared -o "$SCRIPTDIR/constantine/build/${OSARCH}/lib/libconstantineeip196.so" ethereum_evm_precompiles.c -Iconstantine/include -I. -Lconstantine/lib -lconstantine + gcc -I"${JAVA_HOME}/include" -I"${JAVA_HOME}/include/linux" -fPIC -shared -o "$SCRIPTDIR/constantine/build/${OSARCH}/lib/libconstantineeip196.so" jna_ethereum_evm_precompiles.c -Iconstantine/include -I. -Lconstantine/lib constantine/lib/libconstantine.a else echo "Unsupported OS/architecture: ${OSARCH}" exit 1 diff --git a/constantine/build.gradle b/constantine/build.gradle index 92285205..c015b501 100644 --- a/constantine/build.gradle +++ b/constantine/build.gradle @@ -25,41 +25,37 @@ tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } -def osName = System.getProperty('os.name').toLowerCase() -def osArch = System.getProperty('os.arch') -def libDir - -if (osName.contains('mac') && osArch.contains('aarch64')) { - libDir = 'darwin-aarch64' -} else if (osName.contains('mac')) { - libDir = 'darwin-x86-64' -} else if (osName.contains('linux') && osArch.contains('aarch64')) { - libDir = 'linux-gnu-aarch64' -} else { - libDir = 'linux-gnu-x86_64' +task macArmLibCopy(type: Copy) { + from "build/darwin-aarch64/lib/libconstantineeip196.dylib" + into 'build/resources/main/darwin-aarch64' } -task libCopy(type: Copy) { - from "build/${libDir}/lib/" - into "build/resources/main/lib/${libDir}" +task macLibCopy(type: Copy) { + from "build/darwin-x86-64/lib/libconstantineeip196.dylib" + into 'build/resources/main/darwin-x86-64' } -processResources.dependsOn libCopy +task linuxLibCopy(type: Copy) { + from "build/linux-gnu-x86_64/lib/libconstantineeip196.so" + into 'build/resources/main/linux-gnu-x86_64' + +} -task compileJavaSource(type: Exec) { - description = 'Compiles the Java source files' - commandLine 'javac', '-d', 'build', 'src/main/java/org/hyperledger/besu/nativelib/constantine/LibConstantineEIP196.java' - dependsOn libCopy +task linuxArm64LibCopy(type: Copy) { + from "build/linux-gnu-aarch64/lib/libconstantineeip196.so" + into 'build/resources/main/linux-gnu-aarch64' } +processResources.dependsOn macArmLibCopy, macLibCopy, linuxLibCopy, linuxArm64LibCopy + tasks.named('test', Test) { description = 'Runs the Java tests' useJUnit { include '**/*Test.class' } - environment 'LD_LIBRARY_PATH', "${System.env.LD_LIBRARY_PATH}:build/resources/main/lib/${libDir}" - jvmArgs = ["-Djava.library.path=build/resources/main/lib/${libDir}"] - dependsOn compileJavaSource + environment 'LD_LIBRARY_PATH', "${System.env.LD_LIBRARY_PATH}:build/resources/main/linux-gnu-x86_64" + + dependsOn macArmLibCopy, macLibCopy, linuxLibCopy, linuxArm64LibCopy } jar { @@ -142,4 +138,4 @@ artifactory { test { useJUnit() -} \ No newline at end of file +} diff --git a/constantine/jna_ethereum_evm_precompiles.c b/constantine/jna_ethereum_evm_precompiles.c new file mode 100644 index 00000000..955446e4 --- /dev/null +++ b/constantine/jna_ethereum_evm_precompiles.c @@ -0,0 +1,30 @@ +#include "jna_ethereum_evm_precompiles.h" +#include +#include + +void printByteArray(const char* label, const byte* array, size_t len) { + printf("%s: [", label); + for (size_t i = 0; i < len; i++) { + printf("%02x", array[i]); + if (i < len - 1) { + printf(", "); + } + } + printf("]\n"); +} + +int bn254_g1add(byte* r, int r_len, const byte* inputs, int inputs_len) { + // Call the original function + return (int) ctt_eth_evm_bn254_g1add(r, (ptrdiff_t)r_len, inputs, (ptrdiff_t)inputs_len); +} + +int bn254_g1mul(byte* r, int r_len, const byte* inputs, int inputs_len) { + // Call the original function + return (int) ctt_eth_evm_bn254_g1mul(r, (ptrdiff_t)r_len, inputs, (ptrdiff_t)inputs_len); +} + +int bn254_pairingCheck(byte* r, int r_len, const byte* inputs, int inputs_len) { + // Call the original function + return (int) ctt_eth_evm_bn254_ecpairingcheck(r, (ptrdiff_t)r_len, inputs, (ptrdiff_t)inputs_len); +} + diff --git a/constantine/jna_ethereum_evm_precompiles.h b/constantine/jna_ethereum_evm_precompiles.h new file mode 100644 index 00000000..7150436a --- /dev/null +++ b/constantine/jna_ethereum_evm_precompiles.h @@ -0,0 +1,34 @@ +#ifndef _Included_LibConstantineEIP196_Wrapper +#define _Included_LibConstantineEIP196_Wrapper + +#ifdef __cplusplus +extern "C" { +#endif + +// Define byte as unsigned char +typedef unsigned char byte; + +/* + * Function: ctt_eth_evm_bn254_g1add_wrapper + * Signature: (byte*, int, const byte*, int) -> int + */ +int bn254_g1add(byte* r, int r_len, const byte* inputs, int inputs_len); + +/* + * Function: ctt_eth_evm_bn254_g1mul_wrapper + * Signature: (byte*, int, const byte*, int) -> int + */ +int bn254_g1mul(byte* r, int r_len, const byte* inputs, int inputs_len); + +/* + * Function: ctt_eth_evm_bn254_pairingCheck_wrapper + * Signature: (byte*, int, const byte*, int) -> int + */ +int bn254_pairingCheck(byte* r, int r_len, const byte* inputs, int inputs_len); + +#ifdef __cplusplus +} +#endif + +#endif /* _Included_LibConstantineEIP196_Wrapper */ + diff --git a/constantine/src/main/java/org/hyperledger/besu/nativelib/constantine/LibConstantineEIP196.java b/constantine/src/main/java/org/hyperledger/besu/nativelib/constantine/LibConstantineEIP196.java index c919d4ad..ff48d2ca 100644 --- a/constantine/src/main/java/org/hyperledger/besu/nativelib/constantine/LibConstantineEIP196.java +++ b/constantine/src/main/java/org/hyperledger/besu/nativelib/constantine/LibConstantineEIP196.java @@ -1,42 +1,50 @@ package org.hyperledger.besu.nativelib.constantine; +import com.sun.jna.Native; + public class LibConstantineEIP196 { + public static final boolean ENABLED; + static { - System.loadLibrary("constantineeip196"); + boolean enabled; + try { + Native.register(LibConstantineEIP196.class, "constantineeip196"); + enabled = true; + } catch (final Throwable t) { + t.printStackTrace(); + enabled = false; + } + ENABLED = enabled; } - public native int ctt_eth_evm_bn254_g1add(byte[] r, int r_len, byte[] inputs, int inputs_len); - public native int ctt_eth_evm_bn254_g1mul(byte[] r, int r_len, byte[] inputs, int inputs_len); - public native int ctt_eth_evm_bn254_pairingCheck(byte[] r, int r_len, byte[] inputs, int inputs_len); - - public static void loadNativeLibrary() { - System.loadLibrary("constantineeip196"); - } + public static native int bn254_g1add(byte[] r, int r_len, byte[] inputs, int inputs_len); + public static native int bn254_g1mul(byte[] r, int r_len, byte[] inputs, int inputs_len); + public static native int bn254_pairingCheck(byte[] r, int r_len, byte[] inputs, int inputs_len); - public byte[] add(byte[] inputs) { + public static byte[] add(byte[] inputs) { byte[] result = new byte[64]; - int status = ctt_eth_evm_bn254_g1add(result, result.length, inputs, inputs.length); + int status = bn254_g1add(result, result.length, inputs, inputs.length); if (status != 0) { throw new RuntimeException("ctt_eth_evm_bn254_g1add failed with status: " + status); } return result; } - public byte[] mul(byte[] inputs) { + public static byte[] mul(byte[] inputs) { byte[] result = new byte[64]; - int status = ctt_eth_evm_bn254_g1mul(result, result.length, inputs, inputs.length); + int status = bn254_g1mul(result, result.length, inputs, inputs.length); if (status != 0) { throw new RuntimeException("ctt_eth_evm_bn254_g1mul failed with status: " + status); } return result; } - public byte[] pairingCheck(byte[] inputs) { + public static byte[] pairingCheck(byte[] inputs) { byte[] result = new byte[32]; - int status = ctt_eth_evm_bn254_pairingCheck(result, result.length, inputs, inputs.length); + int status = bn254_pairingCheck(result, result.length, inputs, inputs.length); if (status != 0) { throw new RuntimeException("ctt_eth_evm_bn254_pairingCheck failed with status: " + status); } return result; } -} \ No newline at end of file +} diff --git a/constantine/src/test/java/org/hyperledger/besu/nativelib/constantine/ConstantineEIP196G1AddTest.java b/constantine/src/test/java/org/hyperledger/besu/nativelib/constantine/ConstantineEIP196G1AddTest.java index 23620010..621194b0 100644 --- a/constantine/src/test/java/org/hyperledger/besu/nativelib/constantine/ConstantineEIP196G1AddTest.java +++ b/constantine/src/test/java/org/hyperledger/besu/nativelib/constantine/ConstantineEIP196G1AddTest.java @@ -45,11 +45,10 @@ public void shouldCalculate() { // skip the header row return; } - LibConstantineEIP196 constInstance = new LibConstantineEIP196(); byte[] inputBytes = Bytes.fromHexString(this.input).toArrayUnsafe(); byte[] result = new byte[64]; - int status = constInstance.ctt_eth_evm_bn254_g1add(result, result.length, inputBytes, inputBytes.length); + int status = LibConstantineEIP196.bn254_g1add(result, result.length, inputBytes, inputBytes.length); Bytes expectedComputation = expectedResult == null ? null : Bytes.fromHexString(expectedResult); if (status != 0) { @@ -63,4 +62,4 @@ public void shouldCalculate() { assertTrue("Notes should be empty", notes.isEmpty()); } } -} \ No newline at end of file +} diff --git a/constantine/src/test/java/org/hyperledger/besu/nativelib/constantine/ConstantineEIP196G1MulTest.java b/constantine/src/test/java/org/hyperledger/besu/nativelib/constantine/ConstantineEIP196G1MulTest.java index e08c05ca..1c1f9096 100644 --- a/constantine/src/test/java/org/hyperledger/besu/nativelib/constantine/ConstantineEIP196G1MulTest.java +++ b/constantine/src/test/java/org/hyperledger/besu/nativelib/constantine/ConstantineEIP196G1MulTest.java @@ -46,11 +46,10 @@ public void shouldCalculate() { // skip the header row return; } - LibConstantineEIP196 constInstance = new LibConstantineEIP196(); byte[] inputBytes = Bytes.fromHexString(this.input).toArrayUnsafe(); byte[] result = new byte[64]; - int status = constInstance.ctt_eth_evm_bn254_g1mul(result, result.length, inputBytes, inputBytes.length); + int status = LibConstantineEIP196.bn254_g1mul(result, result.length, inputBytes, inputBytes.length); Bytes expectedComputation = expectedResult == null ? null : Bytes.fromHexString(expectedResult); @@ -65,4 +64,4 @@ public void shouldCalculate() { assertTrue("Notes should be empty", notes.isEmpty()); } } -} \ No newline at end of file +} diff --git a/constantine/src/test/java/org/hyperledger/besu/nativelib/constantine/ConstantineEIP196PairingCheckTest.java b/constantine/src/test/java/org/hyperledger/besu/nativelib/constantine/ConstantineEIP196PairingCheckTest.java index 6a4ed3ad..7e6142c0 100644 --- a/constantine/src/test/java/org/hyperledger/besu/nativelib/constantine/ConstantineEIP196PairingCheckTest.java +++ b/constantine/src/test/java/org/hyperledger/besu/nativelib/constantine/ConstantineEIP196PairingCheckTest.java @@ -44,11 +44,10 @@ public void shouldCalculate() { if ("input".equals(input)) { return; } - LibConstantineEIP196 constInstance = new LibConstantineEIP196(); byte[] inputBytes = Bytes.fromHexString(this.input).toArrayUnsafe(); byte[] result = new byte[32]; - int status = constInstance.ctt_eth_evm_bn254_pairingCheck(result, result.length, inputBytes, inputBytes.length); + int status = LibConstantineEIP196.bn254_pairingCheck(result, result.length, inputBytes, inputBytes.length); Bytes expectedComputation = expectedResult == null ? null : Bytes.fromHexString(expectedResult); @@ -62,4 +61,4 @@ public void shouldCalculate() { assertTrue("Notes should be empty", notes.isEmpty()); } } -} \ No newline at end of file +} diff --git a/constantine/src/test/java/org/hyperledger/besu/nativelib/constantine/LibConstantineEIP196Test.java b/constantine/src/test/java/org/hyperledger/besu/nativelib/constantine/LibConstantineEIP196Test.java index 0a0471d7..fbb1a73b 100644 --- a/constantine/src/test/java/org/hyperledger/besu/nativelib/constantine/LibConstantineEIP196Test.java +++ b/constantine/src/test/java/org/hyperledger/besu/nativelib/constantine/LibConstantineEIP196Test.java @@ -1,25 +1,17 @@ package org.hyperledger.besu.nativelib.constantine; -import org.junit.Before; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; public class LibConstantineEIP196Test { - private LibConstantineEIP196 constInstance; - - @Before - public void setUp() { - LibConstantineEIP196.loadNativeLibrary(); - constInstance = new LibConstantineEIP196(); - } @Test public void testG1Add() { byte[] inputs = new byte[128]; - byte[] result = constInstance.add(inputs); + byte[] result = LibConstantineEIP196.add(inputs); assertNotNull("Result array should not be null", result); assertEquals("Result array length should be 64", 64, result.length); } @@ -28,7 +20,7 @@ public void testG1Add() { public void testG1Mul() { byte[] inputs = new byte[96]; - byte[] result = constInstance.mul(inputs); + byte[] result = LibConstantineEIP196.mul(inputs); assertNotNull("Result array should not be null", result); assertEquals("Result array length should be 64", 64, result.length); } @@ -37,9 +29,9 @@ public void testG1Mul() { public void testPairingCheck() { byte[] inputs = new byte[0]; // Empty input - byte[] result = constInstance.pairingCheck(inputs); + byte[] result = LibConstantineEIP196.pairingCheck(inputs); assertNotNull("Result array should not be null", result); assertEquals("Result array length should be 32", 32, result.length); assertEquals("The last byte of the result should be 1", 1, result[31]); } -} \ No newline at end of file +} diff --git a/native-build.sh b/native-build.sh index f2c6bc1c..ad52c677 100755 --- a/native-build.sh +++ b/native-build.sh @@ -1,6 +1,8 @@ #!/bin/bash apt-get update -DEBIAN_FRONTEND=non-interactive apt-get install -y autoconf build-essential libtool automake patchelf curl openjdk-11-jre-headless git wget +DEBIAN_FRONTEND=non-interactive apt-get install -y autoconf build-essential libtool automake patchelf curl openjdk-21-jdk git wget +export JAVA_HOME=$(dirname $(dirname $(readlink -f $(which java)))) +export PATH=$JAVA_HOME/bin:$PATH wget https://go.dev/dl/go1.20.2.linux-arm64.tar.gz echo "78d632915bb75e9a6356a47a42625fd1a785c83a64a643fedd8f61e31b1b3bef go1.20.2.linux-arm64.tar.gz" | sha256sum -c || exit 1 tar -xzf go1.20.2.linux-arm64.tar.gz -C $HOME