Skip to content

Commit

Permalink
Only use MAINNET version of KZG (#5095)
Browse files Browse the repository at this point in the history
Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
  • Loading branch information
fab-10 committed Feb 15, 2023
1 parent c4b4598 commit bff05d8
Show file tree
Hide file tree
Showing 6 changed files with 4,408 additions and 5,801 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,13 @@
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;

import com.google.common.annotations.VisibleForTesting;
import ethereum.ckzg4844.CKZG4844JNI;
Expand All @@ -37,6 +35,9 @@

/** The KZGPointEval precompile contract. */
public class KZGPointEvalPrecompiledContract implements PrecompiledContract {
private static final AtomicBoolean loaded = new AtomicBoolean(false);

private final Bytes successResult;

/** Instantiates a new KZGPointEval precompile contract. */
public KZGPointEvalPrecompiledContract() {
Expand All @@ -49,53 +50,41 @@ public KZGPointEvalPrecompiledContract() {
* @param pathToTrustedSetup the trusted setup path
*/
public KZGPointEvalPrecompiledContract(final Optional<Path> pathToTrustedSetup) {

String absolutePathToSetup;
CKZG4844JNI.Preset bitLength;
if (pathToTrustedSetup.isPresent()) {
Path pathToSetup = pathToTrustedSetup.get();
absolutePathToSetup = pathToSetup.toAbsolutePath().toString();
} else {
InputStream is =
KZGPointEvalPrecompiledContract.class.getResourceAsStream(
"mainnet_kzg_trusted_setup_4096.txt");
try {
File jniWillLoadFrom = File.createTempFile("kzgTrustedSetup", "txt");
jniWillLoadFrom.deleteOnExit();
Files.copy(is, jniWillLoadFrom.toPath(), REPLACE_EXISTING);
is.close();
absolutePathToSetup = jniWillLoadFrom.getAbsolutePath();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
try (BufferedReader setupFile =
Files.newBufferedReader(Paths.get(absolutePathToSetup), Charset.defaultCharset())) {
String firstLine = setupFile.readLine();
if ("4".equals(firstLine)) {
bitLength = CKZG4844JNI.Preset.MINIMAL;
} else if ("4096".equals(firstLine)) {
bitLength = CKZG4844JNI.Preset.MAINNET;
if (loaded.compareAndSet(false, true)) {
String absolutePathToSetup;
if (pathToTrustedSetup.isPresent()) {
Path pathToSetup = pathToTrustedSetup.get();
absolutePathToSetup = pathToSetup.toAbsolutePath().toString();
} else {
throw new IllegalArgumentException("provided file not a setup for either 4 or 4096 bits");
}
CKZG4844JNI.loadNativeLibrary(bitLength);
try {
CKZG4844JNI.loadTrustedSetup(absolutePathToSetup);
} catch (RuntimeException mightBeAlreadyLoaded) {
if (!mightBeAlreadyLoaded.getMessage().contains("Trusted Setup is already loaded")) {
throw mightBeAlreadyLoaded;
InputStream is =
KZGPointEvalPrecompiledContract.class.getResourceAsStream(
"mainnet_kzg_trusted_setup_4096.txt");
try {
File jniWillLoadFrom = File.createTempFile("kzgTrustedSetup", "txt");
jniWillLoadFrom.deleteOnExit();
Files.copy(is, jniWillLoadFrom.toPath(), REPLACE_EXISTING);
is.close();
absolutePathToSetup = jniWillLoadFrom.getAbsolutePath();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
CKZG4844JNI.loadNativeLibrary(CKZG4844JNI.Preset.MAINNET);
CKZG4844JNI.loadTrustedSetup(absolutePathToSetup);
}
Bytes fieldElementsPerBlob =
Bytes32.wrap(Bytes.ofUnsignedInt(CKZG4844JNI.getFieldElementsPerBlob()).xor(Bytes32.ZERO));
Bytes blsModulus =
Bytes32.wrap(Bytes.of(CKZG4844JNI.BLS_MODULUS.toByteArray()).xor(Bytes32.ZERO));

successResult = Bytes.concatenate(fieldElementsPerBlob, blsModulus);
}

/** free up resources. */
@VisibleForTesting
public void tearDown() {
CKZG4844JNI.freeTrustedSetup();
loaded.set(false);
}

@Override
Expand Down Expand Up @@ -126,29 +115,20 @@ public PrecompileContractResult computePrecompile(
Bytes commitment = input.slice(96, 48);
Bytes proof = input.slice(144, 48);

Bytes output = Bytes.EMPTY;
PrecompileContractResult result;
try {
boolean proved =
CKZG4844JNI.verifyKzgProof(
commitment.toArray(), z.toArray(), y.toArray(), proof.toArray());

if (proved) {
Bytes fieldElementsPerBlob =
Bytes32.wrap(
Bytes.of(CKZG4844JNI.getFieldElementsPerBlob()).xor(Bytes32.ZERO)); // usually 4096
Bytes blsModulus =
Bytes32.wrap(Bytes.of(CKZG4844JNI.BLS_MODULUS.toByteArray()).xor(Bytes32.ZERO));

output = Bytes.concatenate(fieldElementsPerBlob, blsModulus);

result =
new PrecompileContractResult(
output, false, MessageFrame.State.COMPLETED_SUCCESS, Optional.empty());
successResult, false, MessageFrame.State.COMPLETED_SUCCESS, Optional.empty());
} else {
result =
new PrecompileContractResult(
output,
Bytes.EMPTY,
false,
MessageFrame.State.COMPLETED_FAILED,
Optional.of(ExceptionalHaltReason.PRECOMPILE_ERROR));
Expand All @@ -158,7 +138,7 @@ public PrecompileContractResult computePrecompile(
System.out.println(kzgFailed.getMessage());
result =
new PrecompileContractResult(
output,
Bytes.EMPTY,
false,
MessageFrame.State.COMPLETED_FAILED,
Optional.of(ExceptionalHaltReason.PRECOMPILE_ERROR));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,77 +15,79 @@
package org.hyperledger.besu.evm.precompile;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.mockito.Mockito.mock;

import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import ethereum.ckzg4844.CKZG4844JNI;
import com.fasterxml.jackson.databind.node.ArrayNode;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

public class KZGPointEvalPrecompileContractTest {

private static KZGPointEvalPrecompiledContract contract;
private final MessageFrame toRun = mock(MessageFrame.class);

@BeforeClass
@BeforeAll
public static void init() {
Path testSetupAbsolutePath =
Path.of(
KZGPointEvalPrecompileContractTest.class.getResource("trusted_setup_4.txt").getPath());
KZGPointEvalPrecompileContractTest.class
.getResource("trusted_setup_4096.txt")
.getPath());
contract = new KZGPointEvalPrecompiledContract(Optional.of(testSetupAbsolutePath));
}

@Test
public void happyPath() {
Bytes input =
Bytes.fromHexString(
"013c03613f6fc558fb7e61e75602241ed9a2f04e36d8670aadd286e71b5ca9cc420000000000000000000000000000000000000000000000000000000000000031e5a2356cbc2ef6a733eae8d54bf48719ae3d990017ca787c419c7d369f8e3c83fac17c3f237fc51f90e2c660eb202a438bc2025baded5cd193c1a018c5885bc9281ba704d5566082e851235c7be763b2a99adff965e0a121ee972ebc472d02944a74f5c6243e14052e105124b70bf65faf85ad3a494325e269fad097842cba");

Bytes fieldElementsPerBlob =
Bytes32.wrap(Bytes.ofUnsignedInt(CKZG4844JNI.getFieldElementsPerBlob()).xor(Bytes32.ZERO));
Bytes blsModulus =
Bytes32.wrap(Bytes.of(CKZG4844JNI.BLS_MODULUS.toByteArray()).xor(Bytes32.ZERO));
Bytes expectedOutput = Bytes.concatenate(fieldElementsPerBlob, blsModulus);
// contract input is encoded as follows: versioned_hash | z | y | commitment | proof |
PrecompiledContract.PrecompileContractResult result = contract.computePrecompile(input, toRun);
assertThat(result.getOutput()).isEqualTo(expectedOutput);
MessageFrame.State endState = result.getState();
assertThat(endState).isEqualTo(MessageFrame.State.COMPLETED_SUCCESS);
@AfterAll
public static void tearDown() {
contract.tearDown();
}

@Test
public void sadPaths() {
try (InputStream failVectors =
KZGPointEvalPrecompileContractTest.class.getResourceAsStream("fail_pointEvaluation.json")) {

ObjectMapper jsonMapper = new ObjectMapper();
JsonNode failJson = jsonMapper.readTree(failVectors);

for (JsonNode testCase : failJson) {

Bytes input = Bytes.fromHexString(testCase.get("Input").asText());
@ParameterizedTest(name = "{index}")
@MethodSource("getPointEvaluationPrecompileTestVectors")
public void testComputePrecompile(final PrecompileTestParameters parameters) {
PrecompiledContract.PrecompileContractResult result =
contract.computePrecompile(parameters.input, toRun);
if (parameters.valid) {
assertThat(result.getState()).isEqualTo(MessageFrame.State.COMPLETED_SUCCESS);
assertThat(result.getOutput()).isEqualTo(parameters.returnValue);
} else {
assertThat(result.getState()).isNotEqualTo(MessageFrame.State.COMPLETED_SUCCESS);
}
}

PrecompiledContract.PrecompileContractResult result =
contract.computePrecompile(input, toRun);
MessageFrame.State endState = result.getState();
assertThat(endState).isEqualTo(MessageFrame.State.COMPLETED_FAILED);
assertThat(result.getHaltReason()).isPresent();
assertThat(result.getHaltReason().get()).isEqualTo(ExceptionalHaltReason.PRECOMPILE_ERROR);
}
} catch (IOException ioe) {
fail("couldn't load test vectors", ioe);
public static List<PrecompileTestParameters> getPointEvaluationPrecompileTestVectors()
throws IOException {
final JsonNode jsonNode;
try (final InputStream testVectors =
KZGPointEvalPrecompileContractTest.class.getResourceAsStream(
"pointEvaluationPrecompile.json")) {
jsonNode = new ObjectMapper().readTree(testVectors);
}
final ArrayNode testCases = (ArrayNode) jsonNode.get("TestCases");
final Bytes returnValue = Bytes.fromHexString(jsonNode.get("PrecompileReturnValue").asText());
return IntStream.range(0, testCases.size())
.mapToObj(
i -> {
final JsonNode testCase = testCases.get(i);
final Bytes input = Bytes.fromHexString(testCase.get("Input").asText());
final boolean valid = testCase.get("Valid").asBoolean();
return new PrecompileTestParameters(input, valid, returnValue);
})
.collect(Collectors.toList());
}

record PrecompileTestParameters(Bytes input, boolean valid, Bytes returnValue) {}
}
Loading

0 comments on commit bff05d8

Please sign in to comment.