Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only use MAINNET version of KZG #5095

Merged
merged 1 commit into from
Feb 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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");

Check warning

Code scanning / CodeQL

Local information disclosure in a temporary directory

Local information disclosure vulnerability due to use of file readable by other local users.
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