diff --git a/gradle/versions.gradle b/gradle/versions.gradle index 1aa6f769ebb..d09d39cc709 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -41,6 +41,7 @@ dependencyManagement { dependency 'io.libp2p:jvm-libp2p-minimal:0.10.0-RELEASE' dependency 'tech.pegasys:jblst:0.3.8' + dependency 'tech.pegasys:jc-kzg-4844:develop' dependency 'org.hdrhistogram:HdrHistogram:2.1.12' diff --git a/infrastructure/kzg/build.gradle b/infrastructure/kzg/build.gradle index 152af374394..60280534149 100644 --- a/infrastructure/kzg/build.gradle +++ b/infrastructure/kzg/build.gradle @@ -1,12 +1,8 @@ -repositories { - maven { url "https://artifacts.consensys.net/public/maven/maven/" } -} - dependencies { + implementation project(':infrastructure:io') + implementation 'org.apache.tuweni:tuweni-bytes' implementation 'org.apache.tuweni:tuweni-ssz' - implementation("tech.pegasys:jc-kzg-4844:develop") + implementation 'tech.pegasys:jc-kzg-4844' implementation 'commons-io:commons-io' - - implementation project(":infrastructure:io") } diff --git a/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/KZG.java b/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/KZG.java index 3673b829a1d..c89584fb9b8 100644 --- a/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/KZG.java +++ b/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/KZG.java @@ -13,119 +13,66 @@ package tech.pegasys.teku.kzg; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.net.URISyntaxException; import java.net.URL; -import java.nio.file.Paths; import java.util.List; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; -import tech.pegasys.teku.infrastructure.io.resource.ResourceLoader; -import tech.pegasys.teku.kzg.impl.KZG4844; -import tech.pegasys.teku.kzg.impl.KzgException; -import tech.pegasys.teku.kzg.impl.ckzg.CkzgLoader; /** - * Implements the standard KZG functions needed for the EIP-4844 specification. - * - *

This package strives to implement the KZG standard as used in the Eth2 specification and is - * the entry-point for all KZG operations in Teku. Do not rely on any of the classes used by this - * one conforming to the specification or standard. + * This interface specifies all the KZG functions needed for the EIP-4844 specification and is the + * entry-point for all KZG operations in Teku. */ -public final class KZG { - private static final Logger LOG = LogManager.getLogger(); - private static final String FILE_SCHEME = "file"; - - private static KZG4844 kzgImpl; - - static { - resetKzgImplementation(); - } - - public static void setKzgImplementation(final KZG4844 kzgImpl) { - KZG.kzgImpl = kzgImpl; - } - - public static void resetKzgImplementation() { - if (CkzgLoader.INSTANCE.isPresent()) { - kzgImpl = CkzgLoader.INSTANCE.get(); - LOG.info("KZG: loaded CKZG library"); - } else { - throw new KzgException("Failed to load CKZG library."); - } - } - - public static KZG4844 getKzgImpl() { - return kzgImpl; - } - - public static void resetTrustedSetup() { - try { - kzgImpl.resetTrustedSetup(); - } catch (final KzgException ex) { - LOG.trace("Trying to reset KZG trusted setup which is not loaded"); - } - } - - public static void loadTrustedSetup(final URL url) { - final String filePath; - try { - filePath = copyResourceToTempFileIfNeeded(url); - } catch (final IOException ex) { - throw new KzgException( - String.format("Failed to copy trusted setup '%s' to temporary file", url), ex); - } - kzgImpl.loadTrustedSetup(filePath); - } - - public static KZGProof computeAggregateKzgProof(final List blobs) { - return kzgImpl.computeAggregateKzgProof(blobs); - } - - public static boolean verifyAggregateKzgProof( - final List blobs, final List kzgCommitments, final KZGProof kzgProof) { - return kzgImpl.verifyAggregateKzgProof(blobs, kzgCommitments, kzgProof); - } - - public static KZGCommitment blobToKzgCommitment(final Bytes blob) { - return kzgImpl.blobToKzgCommitment(blob); - } - - public static boolean verifyKzgProof( - final KZGCommitment kzgCommitment, - final Bytes32 z, - final Bytes32 y, - final KZGProof kzgProof) { - return kzgImpl.verifyKzgProof(kzgCommitment, z, y, kzgProof); - } - - private static String copyResourceToTempFileIfNeeded(final URL url) throws IOException { - try { - if (url.toURI().getScheme().equals(FILE_SCHEME)) { - // Platform-agnostic safe way to get path - return Paths.get(url.toURI()).toFile().getPath(); - } - } catch (final URISyntaxException ex) { - throw new KzgException(String.format("%s is incorrect file path", url), ex); - } - - final Bytes resource = - ResourceLoader.urlOrFile("application/octet-stream") - .loadBytes(url.toExternalForm()) - .orElseThrow(() -> new FileNotFoundException("Not found")); - - File temp = File.createTempFile("resource", ".tmp"); - temp.deleteOnExit(); - - try (final FileOutputStream out = new FileOutputStream(temp)) { - out.write(resource.toArray()); - } - - return temp.getAbsolutePath(); - } +public interface KZG { + + KZG NOOP = + new KZG() { + @Override + public void loadTrustedSetup(final URL trustedSetup) throws KZGException {} + + @Override + public void freeTrustedSetup() throws KZGException {} + + @Override + public KZGProof computeAggregateKzgProof(final List blobs) throws KZGException { + return KZGProof.infinity(); + } + + @Override + public boolean verifyAggregateKzgProof( + final List blobs, + final List kzgCommitments, + final KZGProof kzgProof) + throws KZGException { + return true; + } + + @Override + public KZGCommitment blobToKzgCommitment(final Bytes blob) throws KZGException { + return KZGCommitment.infinity(); + } + + @Override + public boolean verifyKzgProof( + final KZGCommitment kzgCommitment, + final Bytes32 z, + final Bytes32 y, + final KZGProof kzgProof) + throws KZGException { + return true; + } + }; + + void loadTrustedSetup(URL trustedSetup) throws KZGException; + + void freeTrustedSetup() throws KZGException; + + KZGProof computeAggregateKzgProof(List blobs) throws KZGException; + + boolean verifyAggregateKzgProof( + List blobs, List kzgCommitments, KZGProof kzgProof) throws KZGException; + + KZGCommitment blobToKzgCommitment(Bytes blob) throws KZGException; + + boolean verifyKzgProof(KZGCommitment kzgCommitment, Bytes32 z, Bytes32 y, KZGProof kzgProof) + throws KZGException; } diff --git a/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/KZGCommitment.java b/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/KZGCommitment.java index a1c41568712..fea3c0fd971 100644 --- a/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/KZGCommitment.java +++ b/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/KZGCommitment.java @@ -47,11 +47,14 @@ public static KZGCommitment fromSSZBytes(final Bytes bytes) { reader -> new KZGCommitment(Bytes48.wrap(reader.readFixedBytes(KZG_COMMITMENT_SIZE)))); } - public static KZGCommitment fromBytesCompressed(final Bytes48 bytes) - throws IllegalArgumentException { + public static KZGCommitment fromBytesCompressed(final Bytes48 bytes) { return new KZGCommitment(bytes); } + public static KZGCommitment fromArray(final byte[] bytes) { + return fromBytesCompressed(Bytes48.wrap(bytes)); + } + private final Bytes48 bytesCompressed; public KZGCommitment(final Bytes48 bytesCompressed) { @@ -71,6 +74,10 @@ public Bytes48 getBytesCompressed() { return bytesCompressed; } + public byte[] toArray() { + return bytesCompressed.toArray(); + } + public String toAbbreviatedString() { return getBytesCompressed().toUnprefixedHexString().substring(0, 7); } @@ -98,7 +105,7 @@ public boolean equals(final Object obj) { return false; } - KZGCommitment other = (KZGCommitment) obj; + final KZGCommitment other = (KZGCommitment) obj; return Objects.equals(this.getBytesCompressed(), other.getBytesCompressed()); } diff --git a/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/impl/KzgException.java b/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/KZGException.java similarity index 76% rename from infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/impl/KzgException.java rename to infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/KZGException.java index f5569929f2c..760e6eeb9fc 100644 --- a/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/impl/KzgException.java +++ b/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/KZGException.java @@ -11,15 +11,15 @@ * specific language governing permissions and limitations under the License. */ -package tech.pegasys.teku.kzg.impl; +package tech.pegasys.teku.kzg; -public class KzgException extends IllegalArgumentException { +public class KZGException extends RuntimeException { - public KzgException(String message) { + public KZGException(final String message) { super(message); } - public KzgException(String message, Throwable cause) { + public KZGException(final String message, final Throwable cause) { super(message, cause); } } diff --git a/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/KZGProof.java b/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/KZGProof.java index 1683acb269d..a9a0310c36b 100644 --- a/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/KZGProof.java +++ b/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/KZGProof.java @@ -50,6 +50,10 @@ public static KZGProof fromBytesCompressed(final Bytes48 bytes) throws IllegalAr return new KZGProof(bytes); } + public static KZGProof fromArray(final byte[] bytes) { + return fromBytesCompressed(Bytes48.wrap(bytes)); + } + private final Bytes48 bytesCompressed; public KZGProof(final Bytes48 bytesCompressed) { @@ -57,9 +61,9 @@ public KZGProof(final Bytes48 bytesCompressed) { } /** - * Returns the SSZ serialization of the compressed form of the commitment + * Returns the SSZ serialization of the compressed form of the proof * - * @return the serialization of the compressed form of the commitment. + * @return the serialization of the compressed form of the proof. */ public Bytes toSSZBytes() { return SSZ.encode(writer -> writer.writeFixedBytes(getBytesCompressed())); @@ -69,6 +73,10 @@ public Bytes48 getBytesCompressed() { return bytesCompressed; } + public byte[] toArray() { + return bytesCompressed.toArray(); + } + public String toAbbreviatedString() { return getBytesCompressed().toUnprefixedHexString().substring(0, 7); } @@ -96,7 +104,7 @@ public boolean equals(final Object obj) { return false; } - KZGProof other = (KZGProof) obj; + final KZGProof other = (KZGProof) obj; return Objects.equals(this.getBytesCompressed(), other.getBytesCompressed()); } diff --git a/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/ckzg4844/CKZG4844.java b/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/ckzg4844/CKZG4844.java new file mode 100644 index 00000000000..76032f52ef3 --- /dev/null +++ b/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/ckzg4844/CKZG4844.java @@ -0,0 +1,128 @@ +/* + * Copyright ConsenSys Software Inc., 2022 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package tech.pegasys.teku.kzg.ckzg4844; + +import ethereum.ckzg4844.CKzg4844JNI; +import java.net.URL; +import java.util.List; +import java.util.stream.Stream; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import tech.pegasys.teku.kzg.KZG; +import tech.pegasys.teku.kzg.KZGCommitment; +import tech.pegasys.teku.kzg.KZGException; +import tech.pegasys.teku.kzg.KZGProof; + +/** + * Wrapper around jc-kzg-4844 + * + *

This class should be a singleton + */ +public final class CKZG4844 implements KZG { + + private static final Logger LOG = LogManager.getLogger(); + + private static CKZG4844 instance; + + public static synchronized CKZG4844 createOrGetInstance() { + if (instance == null) { + instance = new CKZG4844(); + } + return instance; + } + + private CKZG4844() { + try { + LOG.debug("Loaded C-KZG-4844 library"); + } catch (final Exception ex) { + throw new KZGException("Failed to load C-KZG-4844 library", ex); + } + } + + @Override + public void loadTrustedSetup(final URL trustedSetup) throws KZGException { + try { + final String file = CKZG4844Utils.copyTrustedSetupToTempFileIfNeeded(trustedSetup); + CKzg4844JNI.loadTrustedSetup(file); + LOG.debug("Loaded trusted setup from {}", file); + } catch (final Exception ex) { + throw new KZGException("Failed to load trusted setup from " + trustedSetup, ex); + } + } + + @Override + public void freeTrustedSetup() throws KZGException { + try { + CKzg4844JNI.freeTrustedSetup(); + } catch (final Exception ex) { + throw new KZGException("Failed to free trusted setup", ex); + } + } + + @Override + public KZGProof computeAggregateKzgProof(final List blobs) throws KZGException { + try { + final byte[] blobsBytes = CKZG4844Utils.flattenBytesListToArray(blobs); + final byte[] proof = CKzg4844JNI.computeAggregateKzgProof(blobsBytes, blobs.size()); + return KZGProof.fromArray(proof); + } catch (final Exception ex) { + throw new KZGException("Failed to compute aggregated KZG proof for blobs", ex); + } + } + + @Override + public boolean verifyAggregateKzgProof( + final List blobs, final List kzgCommitments, final KZGProof kzgProof) + throws KZGException { + try { + final byte[] blobsBytes = CKZG4844Utils.flattenBytesListToArray(blobs); + final Stream commitmentsBytesStream = + kzgCommitments.stream().map(KZGCommitment::getBytesCompressed); + final byte[] commitmentsBytes = + CKZG4844Utils.flattenBytesStreamToArray(commitmentsBytesStream); + return CKzg4844JNI.verifyAggregateKzgProof( + blobsBytes, commitmentsBytes, blobs.size(), kzgProof.toArray()); + } catch (final Exception ex) { + throw new KZGException( + "Failed to verify blobs and commitments against KZG proof " + kzgProof, ex); + } + } + + @Override + public KZGCommitment blobToKzgCommitment(final Bytes blob) throws KZGException { + try { + final byte[] commitmentBytes = CKzg4844JNI.blobToKzgCommitment(blob.toArray()); + return KZGCommitment.fromArray(commitmentBytes); + } catch (final Exception ex) { + throw new KZGException("Failed to produce KZG commitment from blob", ex); + } + } + + @Override + public boolean verifyKzgProof( + final KZGCommitment kzgCommitment, final Bytes32 z, final Bytes32 y, final KZGProof kzgProof) + throws KZGException { + try { + return CKzg4844JNI.verifyKzgProof( + kzgCommitment.toArray(), z.toArray(), y.toArray(), kzgProof.toArray()); + } catch (final Exception ex) { + throw new KZGException( + String.format( + "Failed to verify KZG commitment %s against KZG proof %s", kzgCommitment, kzgProof), + ex); + } + } +} diff --git a/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/ckzg4844/CKZG4844Utils.java b/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/ckzg4844/CKZG4844Utils.java new file mode 100644 index 00000000000..e627b31079c --- /dev/null +++ b/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/ckzg4844/CKZG4844Utils.java @@ -0,0 +1,64 @@ +/* + * Copyright ConsenSys Software Inc., 2022 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package tech.pegasys.teku.kzg.ckzg4844; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.List; +import java.util.stream.Stream; +import org.apache.tuweni.bytes.Bytes; +import tech.pegasys.teku.infrastructure.io.resource.ResourceLoader; + +class CKZG4844Utils { + + private static final String FILE_SCHEME = "file"; + + public static byte[] flattenBytesListToArray(final List bytes) { + return flattenBytesStreamToArray(bytes.stream()); + } + + public static byte[] flattenBytesStreamToArray(final Stream bytes) { + return bytes.reduce(Bytes::wrap).orElse(Bytes.EMPTY).toArray(); + } + + public static String copyTrustedSetupToTempFileIfNeeded(final URL trustedSetup) + throws IOException { + try { + if (trustedSetup.toURI().getScheme().equals(FILE_SCHEME)) { + // Platform-agnostic safe way to get path + return Paths.get(trustedSetup.toURI()).toFile().getPath(); + } + } catch (final URISyntaxException ex) { + throw new IllegalArgumentException(trustedSetup + " is incorrect file path", ex); + } + + final InputStream resource = + ResourceLoader.urlOrFile("application/octet-stream") + .load(trustedSetup.toExternalForm()) + .orElseThrow(() -> new FileNotFoundException(trustedSetup + " is not found")); + + final Path temp = Files.createTempFile("trusted_setup", ".tmp"); + temp.toFile().deleteOnExit(); + Files.copy(resource, temp, StandardCopyOption.REPLACE_EXISTING); + + return temp.toFile().getAbsolutePath(); + } +} diff --git a/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/impl/KZG4844.java b/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/impl/KZG4844.java deleted file mode 100644 index 32209dd8735..00000000000 --- a/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/impl/KZG4844.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright ConsenSys Software Inc., 2022 - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package tech.pegasys.teku.kzg.impl; - -import java.util.List; -import org.apache.tuweni.bytes.Bytes; -import org.apache.tuweni.bytes.Bytes32; -import tech.pegasys.teku.kzg.KZGCommitment; -import tech.pegasys.teku.kzg.KZGProof; - -public interface KZG4844 { - - /** - * Free the current trusted setup - * - * @throws KzgException if no trusted setup has been loaded. - */ - void resetTrustedSetup() throws KzgException; - - /** - * Loads the trusted setup from a file. Once loaded, the same setup will be used for all the - * calls. To load a new setup, reset the current one by calling {@link #resetTrustedSetup()} and - * then load the new one. If no trusted setup has been loaded, all other API calls will throw an - * exception. - * - * @param path a path to a trusted setup file in filesystem - * @throws KzgException if file not found or arguments from file are incorrect - */ - void loadTrustedSetup(String path) throws KzgException; - - /** - * Calculates aggregated proof for the given blobs - * - * @param blobs Blobs - * @return the aggregated proof - */ - KZGProof computeAggregateKzgProof(List blobs) throws KzgException; - - /** - * Verify aggregated proof and commitments for the given blobs - * - * @param blobs Blobs - * @param kzgCommitments KZG Commitments - * @param kzgProof The proof that needs verifying - * @return true if the proof is valid and false otherwise - */ - boolean verifyAggregateKzgProof( - List blobs, List kzgCommitments, KZGProof kzgProof) throws KzgException; - - /** - * Calculates commitment for a given blob - * - * @param blob Blob - * @return the commitment - */ - KZGCommitment blobToKzgCommitment(Bytes blob) throws KzgException; - - /** - * Verify the proof by point evaluation for the given commitment - * - * @param kzgCommitment KZG Commitment - * @param z Z - * @param y Y - * @param kzgProof The proof that needs verifying - * @return true if the proof is valid and false otherwise - */ - boolean verifyKzgProof(KZGCommitment kzgCommitment, Bytes32 z, Bytes32 y, KZGProof kzgProof) - throws KzgException; -} diff --git a/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/impl/ckzg/CkzgKZG4844.java b/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/impl/ckzg/CkzgKZG4844.java deleted file mode 100644 index 369521561d7..00000000000 --- a/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/impl/ckzg/CkzgKZG4844.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright ConsenSys Software Inc., 2022 - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package tech.pegasys.teku.kzg.impl.ckzg; - -import ethereum.ckzg4844.CKzg4844JNI; -import java.util.List; -import org.apache.tuweni.bytes.Bytes; -import org.apache.tuweni.bytes.Bytes32; -import org.apache.tuweni.bytes.Bytes48; -import tech.pegasys.teku.kzg.KZGCommitment; -import tech.pegasys.teku.kzg.KZGProof; -import tech.pegasys.teku.kzg.impl.KZG4844; -import tech.pegasys.teku.kzg.impl.KzgException; - -/** - * Wrapper around JNI C-KZG library implementing stripped down KZG specification needed for EIP-4844 - */ -public final class CkzgKZG4844 implements KZG4844 { - - @Override - public void resetTrustedSetup() throws KzgException { - try { - CKzg4844JNI.freeTrustedSetup(); - } catch (final Exception ex) { - throw new KzgException("Failed to reset trusted setup", ex); - } - } - - @Override - public void loadTrustedSetup(final String path) throws KzgException { - try { - CKzg4844JNI.loadTrustedSetup(path); - } catch (final Exception ex) { - throw new KzgException(String.format("Failed to load trusted setup: %s", path), ex); - } - } - - @Override - public KZGProof computeAggregateKzgProof(final List blobs) throws KzgException { - try { - final byte[] result = - CKzg4844JNI.computeAggregateKzgProof( - blobs.stream().reduce(Bytes::wrap).orElse(Bytes.EMPTY).toArray(), blobs.size()); - return KZGProof.fromBytesCompressed(Bytes48.wrap(result)); - } catch (final Exception ex) { - throw new KzgException("Failed to compute aggregated KZG Proof for Blobs", ex); - } - } - - @Override - public boolean verifyAggregateKzgProof( - final List blobs, final List kzgCommitments, final KZGProof kzgProof) - throws KzgException { - try { - return CKzg4844JNI.verifyAggregateKzgProof( - blobs.stream().reduce(Bytes::wrap).orElse(Bytes.EMPTY).toArray(), - kzgCommitments.stream() - .map(kzgCommitment -> (Bytes) kzgCommitment.getBytesCompressed()) - .reduce(Bytes::wrap) - .orElse(Bytes.EMPTY) - .toArray(), - blobs.size(), - kzgProof.getBytesCompressed().toArray()); - } catch (final Exception ex) { - throw new KzgException( - String.format("Failed to verify blobs against KZG Proof %s", kzgProof), ex); - } - } - - @Override - public KZGCommitment blobToKzgCommitment(final Bytes blob) throws KzgException { - try { - return KZGCommitment.fromBytesCompressed( - Bytes48.wrap(CKzg4844JNI.blobToKzgCommitment(blob.toArray()))); - } catch (final Exception ex) { - throw new KzgException("Failed to produce KZG Commitment from Blob", ex); - } - } - - @Override - public boolean verifyKzgProof( - final KZGCommitment kzgCommitment, final Bytes32 z, final Bytes32 y, final KZGProof kzgProof) - throws KzgException { - try { - return CKzg4844JNI.verifyKzgProof( - kzgCommitment.getBytesCompressed().toArray(), - z.toArray(), - y.toArray(), - kzgProof.getBytesCompressed().toArray()); - } catch (final Exception ex) { - throw new KzgException( - String.format( - "Failed to verify KZG Commitment %s against KZG Proof %s", kzgCommitment, kzgProof), - ex); - } - } -} diff --git a/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/impl/ckzg/CkzgLoader.java b/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/impl/ckzg/CkzgLoader.java deleted file mode 100644 index e5d9e4ef319..00000000000 --- a/infrastructure/kzg/src/main/java/tech/pegasys/teku/kzg/impl/ckzg/CkzgLoader.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright ConsenSys Software Inc., 2022 - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package tech.pegasys.teku.kzg.impl.ckzg; - -import java.lang.reflect.InvocationTargetException; -import java.util.Optional; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import tech.pegasys.teku.kzg.impl.KZG4844; - -public final class CkzgLoader { - - private static final Logger LOG = LogManager.getLogger(); - public static final Optional INSTANCE = loadCkzg(); - - private static Optional loadCkzg() { - try { - final Class kzgClass = Class.forName("tech.pegasys.teku.kzg.impl.ckzg.CkzgKZG4844"); - return Optional.of((KZG4844) kzgClass.getDeclaredConstructor().newInstance()); - } catch (final InstantiationException - | ExceptionInInitializerError - | InvocationTargetException - | NoSuchMethodException - | IllegalAccessException - | ClassNotFoundException e) { - LOG.error("Couldn't load native KZG library", e); - return Optional.empty(); - } - } -} diff --git a/infrastructure/kzg/src/test/java/tech/pegasys/teku/kzg/KZGTest.java b/infrastructure/kzg/src/test/java/tech/pegasys/teku/kzg/KZGTest.java index 1d3f87429cf..94e74f77c87 100644 --- a/infrastructure/kzg/src/test/java/tech/pegasys/teku/kzg/KZGTest.java +++ b/infrastructure/kzg/src/test/java/tech/pegasys/teku/kzg/KZGTest.java @@ -27,11 +27,12 @@ import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.bytes.Bytes48; import org.apache.tuweni.units.bigints.UInt256; -import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import tech.pegasys.teku.kzg.impl.KzgException; +import tech.pegasys.teku.kzg.ckzg4844.CKZG4844; public final class KZGTest { + private static final String MAINNET_TRUSTED_SETUP_TEST = "trusted_setups/test_mainnet.txt"; private static final BigInteger BLS_MODULUS = new BigInteger( @@ -40,27 +41,28 @@ public final class KZGTest { private static final int RANDOM_SEED = 5566; private static final Random RND = new Random(RANDOM_SEED); - @BeforeEach - public void setup() { - KZG.resetTrustedSetup(); - } + private final KZG kzg = CKZG4844.createOrGetInstance(); - @Test - public void testKzgDoubleResetDoNotThrow() { - KZG.resetTrustedSetup(); - KZG.resetTrustedSetup(); + @AfterEach + public void cleanUpIfNeeded() { + try { + kzg.freeTrustedSetup(); + } catch (final KZGException ex) { + // NOOP + } } @Test - public void testKzgLoadTrustedSetup() { + public void testKzgLoadTrustedSetupTwice_shouldThrowException() { loadTrustedSetup(); - KZG.resetTrustedSetup(); + assertThrows(KZGException.class, this::loadTrustedSetup); } @Test - public void testKzgLoadTrustedSetupTwice_shouldThrowException() { + public void testKzgFreeTrustedSetupTwice_shouldThrowException() { loadTrustedSetup(); - assertThrows(KzgException.class, this::loadTrustedSetup); + kzg.freeTrustedSetup(); + assertThrows(KZGException.class, kzg::freeTrustedSetup); } @Test @@ -69,15 +71,15 @@ public void testUsageWithoutLoadedTrustedSetup_shouldThrowException() { final KZGCommitment kzgCommitment = new KZGCommitment(emptyCommitment); final KZGProof kzgProof = new KZGProof(emptyCommitment); assertThrows( - KzgException.class, + KZGException.class, () -> - KZG.verifyAggregateKzgProof( + kzg.verifyAggregateKzgProof( Collections.emptyList(), Collections.emptyList(), kzgProof)); - assertThrows(KzgException.class, () -> KZG.blobToKzgCommitment(Bytes.EMPTY)); - assertThrows(KzgException.class, () -> KZG.computeAggregateKzgProof(Collections.emptyList())); + assertThrows(KZGException.class, () -> kzg.blobToKzgCommitment(Bytes.EMPTY)); + assertThrows(KZGException.class, () -> kzg.computeAggregateKzgProof(Collections.emptyList())); assertThrows( - KzgException.class, - () -> KZG.verifyKzgProof(kzgCommitment, Bytes32.ZERO, Bytes32.ZERO, kzgProof)); + KZGException.class, + () -> kzg.verifyKzgProof(kzgCommitment, Bytes32.ZERO, Bytes32.ZERO, kzgProof)); } @Test @@ -89,15 +91,15 @@ public void testVerifyKzgProof() { .mapToObj(__ -> getSampleBlob()) .collect(Collectors.toList()); final List kzgCommitments = - blobs.stream().map(KZG::blobToKzgCommitment).collect(Collectors.toList()); - final KZGProof kzgProof = KZG.computeAggregateKzgProof(blobs); - assertThat(KZG.verifyAggregateKzgProof(blobs, kzgCommitments, kzgProof)).isTrue(); + blobs.stream().map(kzg::blobToKzgCommitment).collect(Collectors.toList()); + final KZGProof kzgProof = kzg.computeAggregateKzgProof(blobs); + assertThat(kzg.verifyAggregateKzgProof(blobs, kzgCommitments, kzgProof)).isTrue(); assertThat( - KZG.verifyAggregateKzgProof( + kzg.verifyAggregateKzgProof( blobs.subList(0, 2), kzgCommitments.subList(0, 2), kzgProof)) .isFalse(); - final KZGProof invalidProof = KZG.computeAggregateKzgProof(blobs.subList(0, 2)); - assertThat(KZG.verifyAggregateKzgProof(blobs, kzgCommitments, invalidProof)).isFalse(); + final KZGProof invalidProof = kzg.computeAggregateKzgProof(blobs.subList(0, 2)); + assertThat(kzg.verifyAggregateKzgProof(blobs, kzgCommitments, invalidProof)).isFalse(); } @Test @@ -106,19 +108,19 @@ public void testVerifyPointEvaluationPrecompile() { final Bytes48 emptyCommitment = Bytes48.rightPad(Bytes.fromHexString("c0")); final KZGCommitment kzgCommitment = new KZGCommitment(emptyCommitment); final KZGProof kzgProof = new KZGProof(emptyCommitment); - assertThat(KZG.verifyKzgProof(kzgCommitment, Bytes32.ZERO, Bytes32.ZERO, kzgProof)).isTrue(); - assertThat(KZG.computeAggregateKzgProof(Collections.emptyList())).isEqualTo(kzgProof); + assertThat(kzg.verifyKzgProof(kzgCommitment, Bytes32.ZERO, Bytes32.ZERO, kzgProof)).isTrue(); + assertThat(kzg.computeAggregateKzgProof(Collections.emptyList())).isEqualTo(kzgProof); assertThat( - KZG.blobToKzgCommitment(Bytes.wrap(new byte[FIELD_ELEMENTS_PER_BLOB * Bytes32.SIZE]))) + kzg.blobToKzgCommitment(Bytes.wrap(new byte[FIELD_ELEMENTS_PER_BLOB * Bytes32.SIZE]))) .isEqualTo(kzgCommitment); assertThat( - KZG.verifyAggregateKzgProof(Collections.emptyList(), Collections.emptyList(), kzgProof)) + kzg.verifyAggregateKzgProof(Collections.emptyList(), Collections.emptyList(), kzgProof)) .isTrue(); } private void loadTrustedSetup() { - final URL resourceUrl = KZGTest.class.getResource(MAINNET_TRUSTED_SETUP_TEST); - KZG.loadTrustedSetup(resourceUrl); + final URL trustedSetup = KZGTest.class.getResource(MAINNET_TRUSTED_SETUP_TEST); + kzg.loadTrustedSetup(trustedSetup); } private BigInteger randomBigIntegerInModulus(final BigInteger modulus, final Random rnd) { @@ -135,6 +137,6 @@ private Bytes getSampleBlob() { .mapToObj(__ -> randomBigIntegerInModulus(BLS_MODULUS, RND)) .map(bi -> (Bytes) UInt256.valueOf(bi).toBytes()) .reduce(Bytes::wrap) - .get(); + .orElse(Bytes.EMPTY); } }