Skip to content

Commit

Permalink
Linux aarch64 support for EIP196 constantine (#203)
Browse files Browse the repository at this point in the history
* add support for linux aarch64
* convert to JNA, use static libconstantine.a

Signed-off-by: Nischal Sharma <nischal@web3labs.com>
Co-authored-by: garyschulte <garyschulte@gmail.com>
  • Loading branch information
NickSneo and garyschulte committed Aug 21, 2024
1 parent bc48568 commit e996d8e
Show file tree
Hide file tree
Showing 11 changed files with 156 additions and 75 deletions.
12 changes: 10 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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'
Expand Down
38 changes: 26 additions & 12 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
44 changes: 20 additions & 24 deletions constantine/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -142,4 +138,4 @@ artifactory {

test {
useJUnit()
}
}
30 changes: 30 additions & 0 deletions constantine/jna_ethereum_evm_precompiles.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include "jna_ethereum_evm_precompiles.h"
#include <constantine.h>
#include <stdio.h>

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);
}

34 changes: 34 additions & 0 deletions constantine/jna_ethereum_evm_precompiles.h
Original file line number Diff line number Diff line change
@@ -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 */

Original file line number Diff line number Diff line change
@@ -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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -63,4 +62,4 @@ public void shouldCalculate() {
assertTrue("Notes should be empty", notes.isEmpty());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -65,4 +64,4 @@ public void shouldCalculate() {
assertTrue("Notes should be empty", notes.isEmpty());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -62,4 +61,4 @@ public void shouldCalculate() {
assertTrue("Notes should be empty", notes.isEmpty());
}
}
}
}
Loading

0 comments on commit e996d8e

Please sign in to comment.