From ec7eae481553d5aac920a15f08b17bdf8e5e598d Mon Sep 17 00:00:00 2001 From: Stefan Bratanov Date: Fri, 25 Nov 2022 13:28:03 +0000 Subject: [PATCH] Java bindings improvements --- bindings/java/Makefile | 3 +- bindings/java/README.md | 6 +- bindings/java/c_kzg_4844_jni.c | 12 +-- bindings/java/c_kzg_4844_jni.h | 87 +++++++++---------- .../{ => ethereum/ckzg4844}/CKzg4844JNI.java | 50 ++++++++++- .../ckzg4844}/CKZg4844JNITest.java | 62 +++++++++++-- 6 files changed, 155 insertions(+), 65 deletions(-) rename bindings/java/src/main/java/{ => ethereum/ckzg4844}/CKzg4844JNI.java (51%) rename bindings/java/src/test/java/{ => ethereum/ckzg4844}/CKZg4844JNITest.java (63%) diff --git a/bindings/java/Makefile b/bindings/java/Makefile index 1272d50..030b565 100644 --- a/bindings/java/Makefile +++ b/bindings/java/Makefile @@ -1,7 +1,7 @@ INCLUDE_DIRS = ../../src ../../blst/bindings ifeq ($(OS),Windows_NT) - CLANG_EXECUTABLE=gcc + CLANG_EXECUTABLE=clang GRADLE_COMMAND=gradlew CLANG_FLAGS=-shared JNI_INCLUDE_FOLDER=win32 @@ -37,4 +37,3 @@ build: test: ${GRADLE_COMMAND} clean test - diff --git a/bindings/java/README.md b/bindings/java/README.md index db519e4..9070467 100644 --- a/bindings/java/README.md +++ b/bindings/java/README.md @@ -2,15 +2,15 @@ ## Prerequisites -* Follow the instructions in the [README.md](../../README.md) to install blst and build the C-KZG code. -* `JAVA_HOME` environment variable is set to a JDK with an `include` folder containing a jni.h file. +* Follow the instructions in the [README.md](../../README.md) to install blst and build the C-KZG library. +* `JAVA_HOME` environment variable is set to a JDK with an `include` folder containing a `jni.h` file. ## Build ```bash make build ``` -This will install the library in the `src/main/resources/lib` folder according to your OS +This will install the library in the `src/main/resources/lib` folder with a name according to your OS ## Test ```bash diff --git a/bindings/java/c_kzg_4844_jni.c b/bindings/java/c_kzg_4844_jni.c index f3c9a5b..82367f3 100644 --- a/bindings/java/c_kzg_4844_jni.c +++ b/bindings/java/c_kzg_4844_jni.c @@ -22,7 +22,7 @@ void throw_exception(JNIEnv *env, const char *message) (*env)->ThrowNew(env, Exception, message); } -JNIEXPORT void JNICALL Java_CKzg4844JNI_loadTrustedSetup(JNIEnv *env, jclass thisCls, jstring file) +JNIEXPORT void JNICALL Java_ethereum_ckzg4844_CKzg4844JNI_loadTrustedSetup(JNIEnv *env, jclass thisCls, jstring file) { if (settings != NULL) { @@ -61,7 +61,7 @@ JNIEXPORT void JNICALL Java_CKzg4844JNI_loadTrustedSetup(JNIEnv *env, jclass thi (*env)->ReleaseStringUTFChars(env, file, file_native); } -JNIEXPORT void JNICALL Java_CKzg4844JNI_freeTrustedSetup(JNIEnv *env, jclass thisCls) +JNIEXPORT void JNICALL Java_ethereum_ckzg4844_CKzg4844JNI_freeTrustedSetup(JNIEnv *env, jclass thisCls) { if (settings == NULL) { @@ -72,7 +72,7 @@ JNIEXPORT void JNICALL Java_CKzg4844JNI_freeTrustedSetup(JNIEnv *env, jclass thi reset_trusted_setup(); } -JNIEXPORT jbyteArray JNICALL Java_CKzg4844JNI_computeAggregateKzgProof(JNIEnv *env, jclass thisCls, jbyteArray blobs, jlong count) +JNIEXPORT jbyteArray JNICALL Java_ethereum_ckzg4844_CKzg4844JNI_computeAggregateKzgProof(JNIEnv *env, jclass thisCls, jbyteArray blobs, jlong count) { if (settings == NULL) { @@ -104,7 +104,7 @@ JNIEXPORT jbyteArray JNICALL Java_CKzg4844JNI_computeAggregateKzgProof(JNIEnv *e return proof; } -JNIEXPORT jboolean JNICALL Java_CKzg4844JNI_verifyAggregateKzgProof(JNIEnv *env, jclass thisCls, jbyteArray blobs, jbyteArray commitments, jlong count, jbyteArray proof) +JNIEXPORT jboolean JNICALL Java_ethereum_ckzg4844_CKzg4844JNI_verifyAggregateKzgProof(JNIEnv *env, jclass thisCls, jbyteArray blobs, jbyteArray commitments, jlong count, jbyteArray proof) { if (settings == NULL) { @@ -163,7 +163,7 @@ JNIEXPORT jboolean JNICALL Java_CKzg4844JNI_verifyAggregateKzgProof(JNIEnv *env, return (jboolean)out; } -JNIEXPORT jbyteArray JNICALL Java_CKzg4844JNI_blobToKzgCommitment(JNIEnv *env, jclass thisCls, jbyteArray blob) +JNIEXPORT jbyteArray JNICALL Java_ethereum_ckzg4844_CKzg4844JNI_blobToKzgCommitment(JNIEnv *env, jclass thisCls, jbyteArray blob) { if (settings == NULL) { @@ -186,7 +186,7 @@ JNIEXPORT jbyteArray JNICALL Java_CKzg4844JNI_blobToKzgCommitment(JNIEnv *env, j return commitment; } -JNIEXPORT jboolean JNICALL Java_CKzg4844JNI_verifyKzgProof(JNIEnv *env, jclass thisCls, jbyteArray commitment, jbyteArray z, jbyteArray y, jbyteArray proof) +JNIEXPORT jboolean JNICALL Java_ethereum_ckzg4844_CKzg4844JNI_verifyKzgProof(JNIEnv *env, jclass thisCls, jbyteArray commitment, jbyteArray z, jbyteArray y, jbyteArray proof) { if (settings == NULL) { diff --git a/bindings/java/c_kzg_4844_jni.h b/bindings/java/c_kzg_4844_jni.h index 1bd8be3..d2eef06 100644 --- a/bindings/java/c_kzg_4844_jni.h +++ b/bindings/java/c_kzg_4844_jni.h @@ -1,59 +1,54 @@ /* DO NOT EDIT THIS FILE - it is machine generated */ #include -/* Header for class CKzg4844JNI */ +/* Header for class ethereum_ckzg4844_CKzg4844JNI */ -#ifndef _Included_CKzg4844JNI -#define _Included_CKzg4844JNI +#ifndef _Included_ethereum_ckzg4844_CKzg4844JNI +#define _Included_ethereum_ckzg4844_CKzg4844JNI #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif -/* - * Class: CKzg4844JNI - * Method: loadTrustedSetup - * Signature: (Ljava/lang/String;)V - */ -JNIEXPORT void JNICALL Java_CKzg4844JNI_loadTrustedSetup - (JNIEnv *, jclass, jstring); + /* + * Class: ethereum_ckzg4844_CKzg4844JNI + * Method: loadTrustedSetup + * Signature: (Ljava/lang/String;)V + */ + JNIEXPORT void JNICALL Java_ethereum_ckzg4844_CKzg4844JNI_loadTrustedSetup(JNIEnv *, jclass, jstring); -/* - * Class: CKzg4844JNI - * Method: freeTrustedSetup - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_CKzg4844JNI_freeTrustedSetup - (JNIEnv *, jclass); + /* + * Class: ethereum_ckzg4844_CKzg4844JNI + * Method: freeTrustedSetup + * Signature: ()V + */ + JNIEXPORT void JNICALL Java_ethereum_ckzg4844_CKzg4844JNI_freeTrustedSetup(JNIEnv *, jclass); -/* - * Class: CKzg4844JNI - * Method: computeAggregateKzgProof - * Signature: ([BJ)[B - */ -JNIEXPORT jbyteArray JNICALL Java_CKzg4844JNI_computeAggregateKzgProof - (JNIEnv *, jclass, jbyteArray, jlong); + /* + * Class: ethereum_ckzg4844_CKzg4844JNI + * Method: computeAggregateKzgProof + * Signature: ([BJ)[B + */ + JNIEXPORT jbyteArray JNICALL Java_ethereum_ckzg4844_CKzg4844JNI_computeAggregateKzgProof(JNIEnv *, jclass, jbyteArray, jlong); -/* - * Class: CKzg4844JNI - * Method: verifyAggregateKzgProof - * Signature: ([B[BJ[B)Z - */ -JNIEXPORT jboolean JNICALL Java_CKzg4844JNI_verifyAggregateKzgProof - (JNIEnv *, jclass, jbyteArray, jbyteArray, jlong, jbyteArray); + /* + * Class: ethereum_ckzg4844_CKzg4844JNI + * Method: verifyAggregateKzgProof + * Signature: ([B[BJ[B)Z + */ + JNIEXPORT jboolean JNICALL Java_ethereum_ckzg4844_CKzg4844JNI_verifyAggregateKzgProof(JNIEnv *, jclass, jbyteArray, jbyteArray, jlong, jbyteArray); -/* - * Class: CKzg4844JNI - * Method: blobToKzgCommitment - * Signature: ([B)[B - */ -JNIEXPORT jbyteArray JNICALL Java_CKzg4844JNI_blobToKzgCommitment - (JNIEnv *, jclass, jbyteArray); + /* + * Class: ethereum_ckzg4844_CKzg4844JNI + * Method: blobToKzgCommitment + * Signature: ([B)[B + */ + JNIEXPORT jbyteArray JNICALL Java_ethereum_ckzg4844_CKzg4844JNI_blobToKzgCommitment(JNIEnv *, jclass, jbyteArray); -/* - * Class: CKzg4844JNI - * Method: verifyKzgProof - * Signature: ([B[B[B[B)Z - */ -JNIEXPORT jboolean JNICALL Java_CKzg4844JNI_verifyKzgProof - (JNIEnv *, jclass, jbyteArray, jbyteArray, jbyteArray, jbyteArray); + /* + * Class: ethereum_ckzg4844_CKzg4844JNI + * Method: verifyKzgProof + * Signature: ([B[B[B[B)Z + */ + JNIEXPORT jboolean JNICALL Java_ethereum_ckzg4844_CKzg4844JNI_verifyKzgProof(JNIEnv *, jclass, jbyteArray, jbyteArray, jbyteArray, jbyteArray); #ifdef __cplusplus } diff --git a/bindings/java/src/main/java/CKzg4844JNI.java b/bindings/java/src/main/java/ethereum/ckzg4844/CKzg4844JNI.java similarity index 51% rename from bindings/java/src/main/java/CKzg4844JNI.java rename to bindings/java/src/main/java/ethereum/ckzg4844/CKzg4844JNI.java index e6ea637..754820d 100644 --- a/bindings/java/src/main/java/CKzg4844JNI.java +++ b/bindings/java/src/main/java/ethereum/ckzg4844/CKzg4844JNI.java @@ -1,3 +1,5 @@ +package ethereum.ckzg4844; + import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; @@ -10,8 +12,9 @@ public class CKzg4844JNI { private static final String PLATFORM_NATIVE_LIBRARY_NAME = System.mapLibraryName(LIBRARY_NAME); static { - InputStream libraryResource = CKzg4844JNI.class.getResourceAsStream( - "lib/" + PLATFORM_NATIVE_LIBRARY_NAME); + InputStream libraryResource = Thread.currentThread().getContextClassLoader() + .getResourceAsStream( + "lib/" + PLATFORM_NATIVE_LIBRARY_NAME); if (libraryResource == null) { try { System.loadLibrary(LIBRARY_NAME); @@ -39,17 +42,60 @@ public class CKzg4844JNI { public static int BYTES_PER_FIELD_ELEMENT = 32; public static int BYTES_PER_BLOB = FIELD_ELEMENTS_PER_BLOB * BYTES_PER_FIELD_ELEMENT; + /** + * Loads the trusted setup from a file. Once loaded, the same setup will be used for all the + * native calls. To load a new setup, free the current one by calling {@link #freeTrustedSetup()} + * and then load the new one. If no trusted setup has been loaded, all the native calls will throw + * an exception. + * + * @param file A path to a trusted setup file + */ public static native void loadTrustedSetup(String file); + /** + * Free the current trusted setup. This method will throw an exception if no trusted setup has + * been loaded. + */ public static native void freeTrustedSetup(); + /** + * Calculates aggregated proof for the given blobs + * + * @param blobs blobs as flattened bytes + * @param count the count of the blobs + * @return the aggregated proof + */ public static native byte[] computeAggregateKzgProof(byte[] blobs, long count); + /** + * Verify aggregated proof and commitments for the given blobs + * + * @param blobs blobs as flattened bytes + * @param commitments commitments as flattened bytes + * @param count the count of the blobs (should be same as the count of the commitments) + * @param proof the proof that needs verifying + * @return true if the proof is valid and false otherwise + */ public static native boolean verifyAggregateKzgProof(byte[] blobs, byte[] commitments, long count, byte[] proof); + /** + * Calculates commitment for a given blob + * + * @param blob blob bytes + * @return the commitment + */ public static native byte[] blobToKzgCommitment(byte[] blob); + /** + * Verify the proof by point evaluation for the given commitment + * + * @param commitment commitment bytes + * @param z Z + * @param y Y + * @param proof the proof that needs verifying + * @return true if the proof is valid and false otherwise + */ public static native boolean verifyKzgProof(byte[] commitment, byte[] z, byte[] y, byte[] proof); } diff --git a/bindings/java/src/test/java/CKZg4844JNITest.java b/bindings/java/src/test/java/ethereum/ckzg4844/CKZg4844JNITest.java similarity index 63% rename from bindings/java/src/test/java/CKZg4844JNITest.java rename to bindings/java/src/test/java/ethereum/ckzg4844/CKZg4844JNITest.java index 13f9d53..62f8c0b 100644 --- a/bindings/java/src/test/java/CKZg4844JNITest.java +++ b/bindings/java/src/test/java/ethereum/ckzg4844/CKZg4844JNITest.java @@ -1,3 +1,5 @@ +package ethereum.ckzg4844; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -6,6 +8,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Random; +import java.util.stream.IntStream; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; public class CKZg4844JNITest { @@ -26,7 +30,6 @@ public class CKZg4844JNITest { assertEquals(CKzg4844JNI.BYTES_PER_COMMITMENT, commitment.length); assertEquals(CKzg4844JNI.BYTES_PER_COMMITMENT, commitment2.length); - // flatten blobs and commitments final byte[] blobs = flatten(blob, blob2); final byte[] commitments = flatten(commitment, commitment2); @@ -36,7 +39,7 @@ public class CKZg4844JNITest { assertTrue(CKzg4844JNI.verifyAggregateKzgProof(blobs, commitments, 2, proof)); - final byte[] fakeProof = createRandomProof(); + final byte[] fakeProof = createRandomProof(2); assertFalse(CKzg4844JNI.verifyAggregateKzgProof(blobs, commitments, 2, fakeProof)); CKzg4844JNI.freeTrustedSetup(); @@ -61,6 +64,37 @@ public class CKZg4844JNITest { } + @Disabled("Use for manually testing performance.") + @Test + public void testPerformance() { + + loadTrustedSetup(); + + final int count = 100; + + final byte[] blobs = createRandomBlobs(count); + final byte[] commitments = getCommitmentsForBlobs(blobs, count); + + long startTime = System.currentTimeMillis(); + final byte[] proof = CKzg4844JNI.computeAggregateKzgProof(blobs, count); + long endTime = System.currentTimeMillis(); + + System.out.printf("Computing aggregate proof for %d blobs took %d milliseconds%n", count, + endTime - startTime); + + startTime = System.currentTimeMillis(); + boolean proofValidity = CKzg4844JNI.verifyAggregateKzgProof(blobs, commitments, count, proof); + endTime = System.currentTimeMillis(); + + assertTrue(proofValidity); + + System.out.printf("Verifying aggregate proof for %d blobs took %d milliseconds%n", count, + endTime - startTime); + + CKzg4844JNI.freeTrustedSetup(); + + } + @Test public void throwsIfMethodIsUsedWithoutLoadingTrustedSetup() { @@ -82,6 +116,7 @@ public class CKZg4844JNITest { exception.getMessage()); CKzg4844JNI.freeTrustedSetup(); + } @Test @@ -108,12 +143,27 @@ public class CKZg4844JNITest { return blob; } - private byte[] createRandomProof() { - final byte[] blob = createRandomBlob(); - return CKzg4844JNI.computeAggregateKzgProof(blob, 1); + private byte[] createRandomBlobs(final int count) { + final byte[][] blobs = IntStream.rangeClosed(1, count).mapToObj(__ -> createRandomBlob()) + .toArray(byte[][]::new); + return flatten(blobs); } - private byte[] flatten(byte[]... bytes) { + private byte[] createRandomProof(final int count) { + return CKzg4844JNI.computeAggregateKzgProof(createRandomBlobs(count), count); + } + + private byte[] getCommitmentsForBlobs(final byte[] blobs, final int count) { + final byte[][] commitments = new byte[count][]; + IntStream.range(0, count).forEach(i -> { + final byte[] blob = Arrays.copyOfRange(blobs, i * CKzg4844JNI.BYTES_PER_BLOB, + (i + 1) * CKzg4844JNI.BYTES_PER_BLOB); + commitments[i] = CKzg4844JNI.blobToKzgCommitment(blob); + }); + return flatten(commitments); + } + + private byte[] flatten(final byte[]... bytes) { final int capacity = Arrays.stream(bytes).mapToInt(b -> b.length).sum(); final ByteBuffer buffer = ByteBuffer.allocate(capacity); Arrays.stream(bytes).forEach(buffer::put);