[Java binding] Add test vectors for `verifyKzgProof`

This commit is contained in:
Stefan Bratanov 2022-12-15 09:38:41 +02:00
parent 1768321fc3
commit 17fe743fa3
7 changed files with 176 additions and 27 deletions

View File

@ -1,18 +1,21 @@
# Build Shared Library # Java binding
## Prerequisites ## Build shared library
### Prerequisites
* Follow the instructions in the [README.md](../../README.md) to build blst. * Follow the instructions in the [README.md](../../README.md) to build blst.
* `JAVA_HOME` environment variable is set to a JDK with an `include` folder containing a `jni.h` * `JAVA_HOME` environment variable is set to a JDK with an `include` folder containing a `jni.h`
file. file.
## Build ### Build
```bash ```bash
make build make build
``` ```
This will install the library in `src/main/resources/ethereum/ckzg4844/lib` with a folder structure This will install the shared library in `src/main/resources/ethereum/ckzg4844/lib` with a folder
structure
and name according to the preset selected (mainnet or minimal) and your OS. and name according to the preset selected (mainnet or minimal) and your OS.
All variables which could be passed to the `make` command and the defaults can be found in All variables which could be passed to the `make` command and the defaults can be found in
@ -27,8 +30,14 @@ make test
## Benchmark ## Benchmark
JMH is used for benchmarking. JMH is used for benchmarking.
See [CKZG4844JNIBenchmark.java](src/jmh/java/ethereum/ckzg4844/CKZG4844JNIBenchmark.java) for more information. See [CKZG4844JNIBenchmark.java](src/jmh/java/ethereum/ckzg4844/CKZG4844JNIBenchmark.java) for more
information.
```bash ```bash
make benchmark make benchmark
``` ```
## Library
The library which uses this binding and publishes a package to a public maven repo
is [jc-kzg-4844](https://github.com/ConsenSys/jc-kzg-4844).

View File

@ -21,6 +21,7 @@ dependencies {
testImplementation("org.junit.jupiter:junit-jupiter-params:${junitVersion}") testImplementation("org.junit.jupiter:junit-jupiter-params:${junitVersion}")
testFixturesImplementation("org.apache.tuweni:tuweni-units:2.3.1") testFixturesImplementation("org.apache.tuweni:tuweni-units:2.3.1")
testFixturesImplementation("com.fasterxml.jackson.core:jackson-databind:2.14.1")
} }

View File

@ -64,6 +64,24 @@ public class CKZG4844JNIBenchmark {
} }
} }
@State(Scope.Benchmark)
public static class VerifyKzgProofState {
private byte[] commitment;
private byte[] z;
private byte[] y;
private byte[] proof;
@Setup
public void setUp() {
final VerifyKzgProofParameters parameters = TestUtils.getVerifyKzgProofTestVectors().get(2);
commitment = parameters.getCommitment();
z = parameters.getZ();
y = parameters.getY();
proof = parameters.getProof();
}
}
@Setup @Setup
public void setUp() { public void setUp() {
CKZG4844JNI.loadTrustedSetup("../../src/trusted_setup.txt"); CKZG4844JNI.loadTrustedSetup("../../src/trusted_setup.txt");
@ -90,4 +108,10 @@ public class CKZG4844JNIBenchmark {
state.proof); state.proof);
} }
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public boolean verifyKzgProof(final VerifyKzgProofState state) {
return CKZG4844JNI.verifyKzgProof(state.commitment, state.z, state.y, state.proof);
}
} }

View File

@ -8,7 +8,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import ethereum.ckzg4844.CKZG4844JNI.Preset; import ethereum.ckzg4844.CKZG4844JNI.Preset;
import java.util.Optional; import java.util.Optional;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class CKZG4844JNITest { public class CKZG4844JNITest {
@ -64,22 +67,12 @@ public class CKZG4844JNITest {
} }
@Test @ParameterizedTest(name = "{index}")
public void verifiesPointEvaluationPrecompile() { @MethodSource("getVerifyKzgProofTestVectors")
public void testVerifyKzgProof(final VerifyKzgProofParameters parameters) {
loadTrustedSetup(); assertTrue(
CKZG4844JNI.verifyKzgProof(parameters.getCommitment(), parameters.getZ(), parameters.getY(),
final byte[] commitment = new byte[CKZG4844JNI.BYTES_PER_COMMITMENT]; parameters.getProof()));
commitment[0] = (byte) 0xc0;
final byte[] z = new byte[32];
final byte[] y = new byte[32];
final byte[] proof = new byte[CKZG4844JNI.BYTES_PER_PROOF];
proof[0] = (byte) 0xc0;
assertTrue(CKZG4844JNI.verifyKzgProof(commitment, z, y, proof));
CKZG4844JNI.freeTrustedSetup();
} }
@Test @Test
@ -119,7 +112,8 @@ public class CKZG4844JNITest {
loadTrustedSetup(); loadTrustedSetup();
final RuntimeException exception = assertThrows(RuntimeException.class, this::loadTrustedSetup); final RuntimeException exception = assertThrows(RuntimeException.class,
CKZG4844JNITest::loadTrustedSetup);
assertEquals("Trusted Setup is already loaded. Free it before loading a new one.", assertEquals("Trusted Setup is already loaded. Free it before loading a new one.",
exception.getMessage()); exception.getMessage());
@ -142,11 +136,17 @@ public class CKZG4844JNITest {
assertEquals("Trusted Setup is not loaded.", exception.getMessage()); assertEquals("Trusted Setup is not loaded.", exception.getMessage());
} }
private void loadTrustedSetup() { private static void loadTrustedSetup() {
if (PRESET.equals(Preset.MINIMAL)) { if (PRESET.equals(Preset.MINIMAL)) {
CKZG4844JNI.loadTrustedSetup("../../src/trusted_setup_4.txt"); CKZG4844JNI.loadTrustedSetup("../../src/trusted_setup_4.txt");
} else { } else {
CKZG4844JNI.loadTrustedSetup("../../src/trusted_setup.txt"); CKZG4844JNI.loadTrustedSetup("../../src/trusted_setup.txt");
} }
} }
private static Stream<VerifyKzgProofParameters> getVerifyKzgProofTestVectors() {
loadTrustedSetup();
return TestUtils.getVerifyKzgProofTestVectors().stream()
.onClose(CKZG4844JNI::freeTrustedSetup);
}
} }

View File

@ -1,15 +1,28 @@
package ethereum.ckzg4844; package ethereum.ckzg4844;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256; import org.apache.tuweni.units.bigints.UInt256;
public class TestUtils { public class TestUtils {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final Random RANDOM = new Random(); private static final Random RANDOM = new Random();
public static byte[] flatten(final byte[]... bytes) { public static byte[] flatten(final byte[]... bytes) {
@ -21,8 +34,8 @@ public class TestUtils {
public static byte[] createRandomBlob() { public static byte[] createRandomBlob() {
final byte[][] blob = IntStream.range(0, CKZG4844JNI.getFieldElementsPerBlob()) final byte[][] blob = IntStream.range(0, CKZG4844JNI.getFieldElementsPerBlob())
.mapToObj(__ -> randomBlsFieldElement()) .mapToObj(__ -> randomBLSFieldElement())
.map(blsFieldElement -> blsFieldElement.toArray(ByteOrder.LITTLE_ENDIAN)) .map(fieldElement -> fieldElement.toArray(ByteOrder.LITTLE_ENDIAN))
.toArray(byte[][]::new); .toArray(byte[][]::new);
return flatten(blob); return flatten(blob);
} }
@ -47,12 +60,47 @@ public class TestUtils {
return flatten(commitments); return flatten(commitments);
} }
private static UInt256 randomBlsFieldElement() { /**
* Generated using <a
* href="https://github.com/crate-crypto/proto-danksharding-fuzzy-test/">proto-danksharding-fuzzy-test</a>
*/
public static List<VerifyKzgProofParameters> getVerifyKzgProofTestVectors() {
final JsonNode jsonNode;
try (InputStream testVectors = readResource("test-vectors/public_verify_kzg_proof.json")) {
jsonNode = OBJECT_MAPPER.readTree(testVectors);
} catch (final IOException ex) {
throw new UncheckedIOException(ex);
}
final ArrayNode testCases = (ArrayNode) jsonNode.get("TestCases");
final Stream.Builder<VerifyKzgProofParameters> testVectors = Stream.builder();
testVectors.add(VerifyKzgProofParameters.ZERO);
IntStream.range(0,
jsonNode.get("NumTestCases").asInt())
.mapToObj(i -> {
final JsonNode testCase = testCases.get(i);
final Bytes32 z = Bytes32.fromHexString(testCase.get("InputPoint").asText());
final Bytes32 y = Bytes32.fromHexString(testCase.get("ClaimedValue").asText());
final Bytes commitment = Bytes.fromHexString(testCase.get("Commitment").asText(),
CKZG4844JNI.BYTES_PER_COMMITMENT);
final Bytes proof = Bytes.fromHexString(testCase.get("Proof").asText(),
CKZG4844JNI.BYTES_PER_PROOF);
return new VerifyKzgProofParameters(commitment.toArray(), z.toArray(), y.toArray(),
proof.toArray());
})
.forEach(testVectors::add);
return testVectors.build().collect(Collectors.toList());
}
private static UInt256 randomBLSFieldElement() {
final BigInteger attempt = new BigInteger(CKZG4844JNI.BLS_MODULUS.bitLength(), RANDOM); final BigInteger attempt = new BigInteger(CKZG4844JNI.BLS_MODULUS.bitLength(), RANDOM);
if (attempt.compareTo(CKZG4844JNI.BLS_MODULUS) < 0) { if (attempt.compareTo(CKZG4844JNI.BLS_MODULUS) < 0) {
return UInt256.valueOf(attempt); return UInt256.valueOf(attempt);
} }
return randomBlsFieldElement(); return randomBLSFieldElement();
}
private static InputStream readResource(final String resource) {
return Thread.currentThread().getContextClassLoader().getResourceAsStream(resource);
} }
} }

View File

@ -0,0 +1,46 @@
package ethereum.ckzg4844;
public class VerifyKzgProofParameters {
public static final VerifyKzgProofParameters ZERO;
static {
final byte[] commitment = new byte[CKZG4844JNI.BYTES_PER_COMMITMENT];
commitment[0] = (byte) 0xc0;
final byte[] z = new byte[32];
final byte[] y = new byte[32];
final byte[] proof = new byte[CKZG4844JNI.BYTES_PER_PROOF];
proof[0] = (byte) 0xc0;
ZERO = new VerifyKzgProofParameters(commitment, z, y, proof);
}
private final byte[] commitment;
private final byte[] z;
private final byte[] y;
private final byte[] proof;
public VerifyKzgProofParameters(final byte[] commitment, final byte[] z, final byte[] y,
final byte[] proof) {
this.commitment = commitment;
this.z = z;
this.y = y;
this.proof = proof;
}
public byte[] getCommitment() {
return commitment;
}
public byte[] getZ() {
return z;
}
public byte[] getY() {
return y;
}
public byte[] getProof() {
return proof;
}
}

File diff suppressed because one or more lines are too long