diff --git a/.gitattributes b/.gitattributes index ef96416a7bd..3fd26a43dfe 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,6 +3,7 @@ *.bat -text *.pcap binary *.blocks binary +*.dll binary *.dylib binary *.so binary *.gz binary diff --git a/bls/build.gradle b/bls/build.gradle index f2555383cc6..98770731dd1 100644 --- a/bls/build.gradle +++ b/bls/build.gradle @@ -7,6 +7,8 @@ dependencies { implementation 'org.apache.tuweni:tuweni-crypto' implementation 'org.apache.tuweni:tuweni-ssz' implementation 'org.miracl.milagro.amcl:milagro-crypto-java' + implementation 'tech.pegasys:jblst' + testImplementation project(':logging') testImplementation('com.googlecode.json-simple:json-simple') { diff --git a/bls/src/main/java/tech/pegasys/teku/bls/BLS.java b/bls/src/main/java/tech/pegasys/teku/bls/BLS.java index dc2030086dd..771e8499d50 100644 --- a/bls/src/main/java/tech/pegasys/teku/bls/BLS.java +++ b/bls/src/main/java/tech/pegasys/teku/bls/BLS.java @@ -29,6 +29,7 @@ import tech.pegasys.teku.bls.impl.BLS12381; import tech.pegasys.teku.bls.impl.PublicKey; import tech.pegasys.teku.bls.impl.PublicKeyMessagePair; +import tech.pegasys.teku.bls.impl.blst.BlstBLS12381; import tech.pegasys.teku.bls.impl.mikuli.MikuliBLS12381; /** @@ -43,7 +44,8 @@ public class BLS { private static final Logger LOG = LogManager.getLogger(); - private static BLS12381 BlsImpl = MikuliBLS12381.INSTANCE; + private static final BLS12381 BlsImpl = + BlstBLS12381.INSTANCE.map(bls -> (BLS12381) bls).orElse(MikuliBLS12381.INSTANCE); /* * The following are the methods used directly in the Ethereum 2.0 specifications. These strictly adhere to the standard. @@ -91,8 +93,10 @@ public static boolean verify(BLSPublicKey publicKey, Bytes message, BLSSignature * * @param signatures the list of signatures to be aggregated * @return the aggregated signature + * @throws IllegalArgumentException if any of supplied signatures is invalid */ - public static BLSSignature aggregate(List signatures) { + public static BLSSignature aggregate(List signatures) + throws IllegalArgumentException { checkArgument(signatures.size() > 0, "Aggregating zero signatures is invalid."); return new BLSSignature( getBlsImpl() diff --git a/bls/src/main/java/tech/pegasys/teku/bls/BLSConstants.java b/bls/src/main/java/tech/pegasys/teku/bls/BLSConstants.java index f6b39bedf29..5a982d82c08 100644 --- a/bls/src/main/java/tech/pegasys/teku/bls/BLSConstants.java +++ b/bls/src/main/java/tech/pegasys/teku/bls/BLSConstants.java @@ -13,8 +13,20 @@ package tech.pegasys.teku.bls; +import java.math.BigInteger; +import java.nio.ByteOrder; +import org.apache.tuweni.bytes.Bytes32; + public class BLSConstants { + public static final int BLS_PUBKEY_SIZE = 48; + public static final int BLS_SIGNATURE_SIZE = 96; + + static final Bytes32 CURVE_ORDER_BYTES = + Bytes32.fromHexString("0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); + static final BigInteger CURVE_ORDER_BI = + CURVE_ORDER_BYTES.toUnsignedBigInteger(ByteOrder.BIG_ENDIAN); + public static boolean VERIFICATION_DISABLED = false; public static void disableBLSVerification() { diff --git a/bls/src/main/java/tech/pegasys/teku/bls/BLSKeyPair.java b/bls/src/main/java/tech/pegasys/teku/bls/BLSKeyPair.java index 5d5e6aa724c..ded48d9fb8b 100644 --- a/bls/src/main/java/tech/pegasys/teku/bls/BLSKeyPair.java +++ b/bls/src/main/java/tech/pegasys/teku/bls/BLSKeyPair.java @@ -16,6 +16,8 @@ import com.google.common.base.MoreObjects; import java.security.SecureRandom; import java.util.Objects; +import java.util.Random; +import org.apache.tuweni.bytes.Bytes32; import tech.pegasys.teku.bls.impl.KeyPair; public final class BLSKeyPair { @@ -39,7 +41,9 @@ public static BLSKeyPair random(final SecureRandom srng) { * @return a keypair generated from a seed */ public static BLSKeyPair random(int seed) { - return new BLSKeyPair(BLS.getBlsImpl().generateKeyPair(seed)); + BLSSecretKey pseudoRandomSecretBytes = + BLSSecretKey.fromBytesModR(Bytes32.random(new Random(seed))); + return new BLSKeyPair(pseudoRandomSecretBytes); } private final BLSPublicKey publicKey; diff --git a/bls/src/main/java/tech/pegasys/teku/bls/BLSPublicKey.java b/bls/src/main/java/tech/pegasys/teku/bls/BLSPublicKey.java index ad15a496b42..d33be9b2a6a 100644 --- a/bls/src/main/java/tech/pegasys/teku/bls/BLSPublicKey.java +++ b/bls/src/main/java/tech/pegasys/teku/bls/BLSPublicKey.java @@ -15,9 +15,13 @@ import static com.google.common.base.Preconditions.checkArgument; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes48; import org.apache.tuweni.ssz.SSZ; import tech.pegasys.teku.bls.impl.PublicKey; import tech.pegasys.teku.ssz.sos.SimpleOffsetSerializable; @@ -26,7 +30,7 @@ public final class BLSPublicKey implements SimpleOffsetSerializable { // The number of SimpleSerialize basic types in this SSZ Container/POJO. public static final int SSZ_FIELD_COUNT = 1; - public static final int BLS_PUBKEY_SIZE = 48; + public static final int SSZ_BLS_PUBKEY_SIZE = BLSConstants.BLS_PUBKEY_SIZE; /** * Generates a compressed, serialized, random, valid public key based on a seed. @@ -43,7 +47,21 @@ public static BLSPublicKey random(int seed) { * @return the empty public key as per the Eth2 spec */ public static BLSPublicKey empty() { - return BLSPublicKey.fromBytes(Bytes.wrap(new byte[BLS_PUBKEY_SIZE])); + return BLSPublicKey.fromBytesCompressed(Bytes48.ZERO); + } + + /** + * Aggregates list of PublicKeys, returns the public key that corresponds to G1 point at infinity + * if list is empty + * + * @param publicKeys The list of public keys to aggregate + * @return PublicKey The public key + */ + public static BLSPublicKey aggregate(List publicKeys) { + return new BLSPublicKey( + BLS.getBlsImpl() + .aggregatePublicKeys( + publicKeys.stream().map(BLSPublicKey::getPublicKey).collect(Collectors.toList()))); } @Override @@ -53,36 +71,49 @@ public int getSSZFieldCount() { @Override public List get_fixed_parts() { - return List.of(toBytes()); + return List.of(toSSZBytes()); } - public static BLSPublicKey fromBytes(Bytes bytes) { + public static BLSPublicKey fromSSZBytes(Bytes bytes) { checkArgument( - bytes.size() == BLS_PUBKEY_SIZE, - "Expected " + BLS_PUBKEY_SIZE + " bytes but received %s.", + bytes.size() == SSZ_BLS_PUBKEY_SIZE, + "Expected " + SSZ_BLS_PUBKEY_SIZE + " bytes but received %s.", bytes.size()); return SSZ.decode( bytes, - reader -> - new BLSPublicKey( - BLS.getBlsImpl().publicKeyFromCompressed(reader.readFixedBytes(BLS_PUBKEY_SIZE)))); - } - - public static BLSPublicKey fromBytesCompressed(Bytes bytes) { - return new BLSPublicKey(BLS.getBlsImpl().publicKeyFromCompressed(bytes)); + reader -> new BLSPublicKey(Bytes48.wrap(reader.readFixedBytes(SSZ_BLS_PUBKEY_SIZE)))); } - private final PublicKey publicKey; - /** - * Copy constructor. + * Create a PublicKey from 48-byte compressed format * - * @param publicKey A BLSPublicKey + * @param bytes 48 bytes to read the public key from + * @return a public key. Note that implementation may lazily evaluate passed bytes so the method + * may not immediately fail if the supplied bytes are invalid. Use {@link + * BLSPublicKey#fromBytesCompressedValidate(Bytes48)} to validate immediately + * @throws IllegalArgumentException If the supplied bytes are not a valid public key However if + * implementing class lazily parses bytes the exception might not be thrown on invalid input + * but throw on later usage. Use {@link BLSPublicKey#fromBytesCompressedValidate(Bytes48)} if + * need to immediately ensure input validity */ - public BLSPublicKey(BLSPublicKey publicKey) { - this.publicKey = publicKey.getPublicKey(); + public static BLSPublicKey fromBytesCompressed(Bytes48 bytes) throws IllegalArgumentException { + return new BLSPublicKey(bytes); + } + + public static BLSPublicKey fromBytesCompressedValidate(Bytes48 bytes) + throws IllegalArgumentException { + BLSPublicKey ret = new BLSPublicKey(bytes); + ret.getPublicKey().forceValidation(); + return ret; } + // Sometimes we are dealing with random, invalid pubkey points, e.g. when testing. + // Let's only interpret the raw data into a point when necessary to do so. + // And vice versa while aggregating we are dealing with points only so let's + // convert point to raw data when necessary to do so. + private final Supplier publicKey; + private final Supplier bytesCompressed; + /** * Construct from a BLSSecretKey object. * @@ -97,8 +128,19 @@ public BLSPublicKey(BLSSecretKey secretKey) { * * @param publicKey A Mikuli PublicKey */ - public BLSPublicKey(PublicKey publicKey) { + BLSPublicKey(PublicKey publicKey) { + this(() -> publicKey, Suppliers.memoize(publicKey::toBytesCompressed)); + } + + BLSPublicKey(Bytes48 bytesCompressed) { + this( + Suppliers.memoize(() -> BLS.getBlsImpl().publicKeyFromCompressed(bytesCompressed)), + () -> bytesCompressed); + } + + private BLSPublicKey(Supplier publicKey, Supplier bytesCompressed) { this.publicKey = publicKey; + this.bytesCompressed = bytesCompressed; } /** @@ -106,33 +148,19 @@ public BLSPublicKey(PublicKey publicKey) { * * @return the serialization of the compressed form of the signature. */ - public Bytes toBytes() { + public Bytes toSSZBytes() { return SSZ.encode( writer -> { - writer.writeFixedBytes(publicKey.toBytesCompressed()); + writer.writeFixedBytes(toBytesCompressed()); }); } - public Bytes toBytesCompressed() { - return publicKey.toBytesCompressed(); + public Bytes48 toBytesCompressed() { + return bytesCompressed.get(); } - public PublicKey getPublicKey() { - return publicKey; - } - - /** - * Force validation of the given key's contents. - * - * @return true if the given key is valid, false otherwise - */ - public boolean isValid() { - try { - getPublicKey().forceValidation(); - return true; - } catch (IllegalArgumentException e) { - return false; - } + PublicKey getPublicKey() { + return publicKey.get(); } @Override @@ -155,11 +183,11 @@ public boolean equals(Object obj) { } BLSPublicKey other = (BLSPublicKey) obj; - return Objects.equals(this.getPublicKey(), other.getPublicKey()); + return Objects.equals(this.toBytesCompressed(), other.toBytesCompressed()); } @Override public int hashCode() { - return Objects.hash(publicKey); + return Objects.hash(toBytesCompressed()); } } diff --git a/bls/src/main/java/tech/pegasys/teku/bls/BLSSecretKey.java b/bls/src/main/java/tech/pegasys/teku/bls/BLSSecretKey.java index 39142c10a80..40b4fee42e1 100644 --- a/bls/src/main/java/tech/pegasys/teku/bls/BLSSecretKey.java +++ b/bls/src/main/java/tech/pegasys/teku/bls/BLSSecretKey.java @@ -13,15 +13,44 @@ package tech.pegasys.teku.bls; +import java.math.BigInteger; +import java.nio.ByteOrder; import java.util.Objects; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; import tech.pegasys.teku.bls.impl.SecretKey; public final class BLSSecretKey { + /** + * Creates a secret key instance from bytes + * + * @param bytes Should be in range [0, ) + * @throws IllegalArgumentException if bytes are not in the valid range + */ + public static BLSSecretKey fromBytes(Bytes32 bytes) throws IllegalArgumentException { + if (bytes.compareTo(BLSConstants.CURVE_ORDER_BYTES) >= 0) { + throw new IllegalArgumentException( + "Invalid bytes for secret key (0 <= SK < r, where r is " + + BLSConstants.CURVE_ORDER_BYTES + + "): " + + bytes); + } else { + return new BLSSecretKey(BLS.getBlsImpl().secretKeyFromBytes(bytes)); + } + } - public static BLSSecretKey fromBytes(Bytes32 bytes) { - return new BLSSecretKey(BLS.getBlsImpl().secretKeyFromBytes(bytes)); + static BLSSecretKey fromBytesModR(Bytes32 secretKeyBytes) { + final Bytes32 keyBytes; + if (secretKeyBytes.compareTo(BLSConstants.CURVE_ORDER_BYTES) >= 0) { + BigInteger validSK = + secretKeyBytes + .toUnsignedBigInteger(ByteOrder.BIG_ENDIAN) + .mod(BLSConstants.CURVE_ORDER_BI); + keyBytes = Bytes32.leftPad(Bytes.wrap(validSK.toByteArray())); + } else { + keyBytes = secretKeyBytes; + } + return fromBytes(keyBytes); } private SecretKey secretKey; @@ -35,19 +64,16 @@ public BLSSecretKey(SecretKey secretKey) { this.secretKey = secretKey; } - public SecretKey getSecretKey() { + SecretKey getSecretKey() { return secretKey; } - public Bytes toBytes() { - final Bytes bytes = secretKey.toBytes(); - if (bytes.size() == 48) { - final int paddingLength = 48 - 32; - if (bytes.slice(0, paddingLength).isZero()) { - return bytes.slice(paddingLength, 32); - } - } - return bytes; + public BLSPublicKey toPublicKey() { + return new BLSPublicKey(getSecretKey().derivePublicKey()); + } + + public Bytes32 toBytes() { + return secretKey.toBytes(); } /** Overwrites the key with zeros so that it is no longer in memory */ @@ -67,4 +93,9 @@ public boolean equals(final Object o) { public int hashCode() { return Objects.hash(secretKey); } + + @Override + public String toString() { + return "BLSSecretKey{" + toBytes() + '}'; + } } diff --git a/bls/src/main/java/tech/pegasys/teku/bls/BLSSignature.java b/bls/src/main/java/tech/pegasys/teku/bls/BLSSignature.java index 31fbddd4104..48a901d1124 100644 --- a/bls/src/main/java/tech/pegasys/teku/bls/BLSSignature.java +++ b/bls/src/main/java/tech/pegasys/teku/bls/BLSSignature.java @@ -14,8 +14,11 @@ package tech.pegasys.teku.bls; import static com.google.common.base.Preconditions.checkArgument; +import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Objects.isNull; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; import java.util.List; import java.util.Objects; import java.util.Random; @@ -28,7 +31,7 @@ public class BLSSignature implements SimpleOffsetSerializable { // The number of SimpleSerialize basic types in this SSZ Container/POJO. public static final int SSZ_FIELD_COUNT = 1; - public static final int BLS_SIGNATURE_SIZE = 96; + public static final int SSZ_BLS_SIGNATURE_SIZE = BLSConstants.BLS_SIGNATURE_SIZE; /** * Create a random, but valid, signature. @@ -48,7 +51,9 @@ static BLSSignature random() { * @return the signature */ public static BLSSignature random(int entropy) { - return new BLSSignature(BLS.getBlsImpl().randomSignature(entropy)); + BLSKeyPair keyPair = BLSKeyPair.random(entropy); + byte[] message = "Hello, world!".getBytes(UTF_8); + return BLS.sign(keyPair.getSecretKey(), Bytes.wrap(message)); } /** @@ -57,7 +62,7 @@ public static BLSSignature random(int entropy) { * @return the empty signature */ public static BLSSignature empty() { - return BLSSignature.fromBytes(Bytes.wrap(new byte[BLS_SIGNATURE_SIZE])); + return BLSSignature.fromBytesCompressed(Bytes.wrap(new byte[SSZ_BLS_SIGNATURE_SIZE])); } @Override @@ -67,40 +72,51 @@ public int getSSZFieldCount() { @Override public List get_fixed_parts() { - return List.of(SSZ.encode(writer -> writer.writeFixedBytes(signature.toBytesCompressed()))); + return List.of(toSSZBytes()); } - public static BLSSignature fromBytes(Bytes bytes) { + public static BLSSignature fromBytesCompressed(Bytes bytes) { checkArgument( - bytes.size() == BLS_SIGNATURE_SIZE, - "Expected " + BLS_SIGNATURE_SIZE + " bytes but received %s.", + bytes.size() == BLSConstants.BLS_SIGNATURE_SIZE, + "Expected " + BLSConstants.BLS_SIGNATURE_SIZE + " bytes but received %s.", bytes.size()); - return SSZ.decode( - bytes, - reader -> - new BLSSignature( - BLS.getBlsImpl() - .signatureFromCompressed(reader.readFixedBytes(BLS_SIGNATURE_SIZE)))); + return new BLSSignature(bytes); } - private final Signature signature; - - /** - * Copy constructor. - * - * @param signature A BLSSignature - */ - public BLSSignature(BLSSignature signature) { - this.signature = signature.getSignature(); + public static BLSSignature fromSSZBytes(Bytes bytes) { + checkArgument( + bytes.size() == SSZ_BLS_SIGNATURE_SIZE, + "Expected " + SSZ_BLS_SIGNATURE_SIZE + " bytes but received %s.", + bytes.size()); + return SSZ.decode( + bytes, reader -> new BLSSignature(reader.readFixedBytes(SSZ_BLS_SIGNATURE_SIZE))); } + // Sometimes we are dealing with random, invalid signature points, e.g. when testing. + // Let's only interpret the raw data into a point when necessary to do so. + // And vice versa while aggregating we are dealing with points only so let's + // convert point to raw data when necessary to do so. + private final Supplier signature; + private final Supplier bytesCompressed; + /** * Construct from a Mikuli Signature object. * * @param signature A Mikuli Signature */ - public BLSSignature(Signature signature) { + BLSSignature(Signature signature) { + this(() -> signature, Suppliers.memoize(signature::toBytesCompressed)); + } + + BLSSignature(Bytes signatureBytes) { + this( + Suppliers.memoize(() -> BLS.getBlsImpl().signatureFromCompressed(signatureBytes)), + () -> signatureBytes); + } + + private BLSSignature(Supplier signature, Supplier bytesCompressed) { this.signature = signature; + this.bytesCompressed = bytesCompressed; } /** @@ -108,25 +124,29 @@ public BLSSignature(Signature signature) { * * @return the serialization of the compressed form of the signature. */ - public Bytes toBytes() { + public Bytes toSSZBytes() { return SSZ.encode( writer -> { - writer.writeFixedBytes(signature.toBytesCompressed()); + writer.writeFixedBytes(bytesCompressed.get()); }); } - public Signature getSignature() { - return signature; + public Bytes toBytesCompressed() { + return bytesCompressed.get(); + } + + Signature getSignature() { + return signature.get(); } @Override public String toString() { - return toBytes().toString(); + return toBytesCompressed().toString(); } @Override public int hashCode() { - return signature.hashCode(); + return toBytesCompressed().hashCode(); } @Override @@ -141,6 +161,6 @@ public boolean equals(Object obj) { return false; } BLSSignature other = (BLSSignature) obj; - return Objects.equals(this.signature, other.signature); + return Objects.equals(toBytesCompressed(), other.toBytesCompressed()); } } diff --git a/bls/src/main/java/tech/pegasys/teku/bls/impl/BLS12381.java b/bls/src/main/java/tech/pegasys/teku/bls/impl/BLS12381.java index 78b13e71ec7..9fd046fbc8d 100644 --- a/bls/src/main/java/tech/pegasys/teku/bls/impl/BLS12381.java +++ b/bls/src/main/java/tech/pegasys/teku/bls/impl/BLS12381.java @@ -19,6 +19,7 @@ import java.util.Random; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.bytes.Bytes48; import tech.pegasys.teku.bls.BatchSemiAggregate; /** @@ -61,8 +62,13 @@ default KeyPair generateKeyPair(long seed) { * @return a public key. Note that implementation may lazily evaluate passed bytes so the method * may not immediately fail if the supplied bytes are invalid. Use {@link * PublicKey#forceValidation()} to validate immediately + * @throws IllegalArgumentException If the supplied bytes are not a valid public key However if + * implementing class lazily parses bytes the exception might not be thrown on invalid input + * but throw on later usage. Use {@link PublicKey#forceValidation()} if need to immediately + * ensure input validity */ - PublicKey publicKeyFromCompressed(Bytes compressedPublicKeyBytes); + PublicKey publicKeyFromCompressed(Bytes48 compressedPublicKeyBytes) + throws IllegalArgumentException; /** * Decode a signature from its compressed form serialized representation. @@ -95,8 +101,10 @@ default KeyPair generateKeyPair(long seed) { * * @param signatures The list of signatures to aggregate * @return Signature + * @throws IllegalArgumentException if any of supplied signatures is invalid */ - Signature aggregateSignatures(List signatures); + Signature aggregateSignatures(List signatures) + throws IllegalArgumentException; /** * https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407 diff --git a/bls/src/main/java/tech/pegasys/teku/bls/impl/PublicKey.java b/bls/src/main/java/tech/pegasys/teku/bls/impl/PublicKey.java index 51e71d26596..016092f3dd0 100644 --- a/bls/src/main/java/tech/pegasys/teku/bls/impl/PublicKey.java +++ b/bls/src/main/java/tech/pegasys/teku/bls/impl/PublicKey.java @@ -14,6 +14,7 @@ package tech.pegasys.teku.bls.impl; import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes48; /** This class represents a BLS12-381 public key. */ public interface PublicKey { @@ -23,7 +24,7 @@ public interface PublicKey { * * @return byte array of length 48 representation of the public key */ - Bytes toBytesCompressed(); + Bytes48 toBytesCompressed(); /** * Verifies the given BLS signature against the message bytes using this public key. diff --git a/bls/src/main/java/tech/pegasys/teku/bls/impl/SecretKey.java b/bls/src/main/java/tech/pegasys/teku/bls/impl/SecretKey.java index 8ca650b10f5..378053d81cc 100644 --- a/bls/src/main/java/tech/pegasys/teku/bls/impl/SecretKey.java +++ b/bls/src/main/java/tech/pegasys/teku/bls/impl/SecretKey.java @@ -14,6 +14,7 @@ package tech.pegasys.teku.bls.impl; import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; /** This class represents a BLS12-381 private key. */ public interface SecretKey { @@ -21,9 +22,9 @@ public interface SecretKey { /** * Returns byte secret key representation * - * @return 48 bytes + * @return 32 bytes */ - Bytes toBytes(); + Bytes32 toBytes(); /** Creates a public key corresponding to this secret key */ PublicKey derivePublicKey(); diff --git a/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/BlstBLS12381.java b/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/BlstBLS12381.java new file mode 100644 index 00000000000..c4c94863562 --- /dev/null +++ b/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/BlstBLS12381.java @@ -0,0 +1,231 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * 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.bls.impl.blst; + +import java.math.BigInteger; +import java.util.List; +import java.util.Optional; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Collectors; +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 org.apache.tuweni.bytes.Bytes48; +import tech.pegasys.teku.bls.BatchSemiAggregate; +import tech.pegasys.teku.bls.impl.BLS12381; +import tech.pegasys.teku.bls.impl.KeyPair; +import tech.pegasys.teku.bls.impl.PublicKey; +import tech.pegasys.teku.bls.impl.Signature; +import tech.pegasys.teku.bls.impl.blst.swig.BLST_ERROR; +import tech.pegasys.teku.bls.impl.blst.swig.blst; +import tech.pegasys.teku.bls.impl.blst.swig.p2; +import tech.pegasys.teku.bls.impl.blst.swig.p2_affine; +import tech.pegasys.teku.bls.impl.blst.swig.pairing; + +public class BlstBLS12381 implements BLS12381 { + private static final Logger LOG = LogManager.getLogger(); + + public static final Optional INSTANCE; + + private static final int BATCH_RANDOM_BYTES = 8; + + static { + boolean libraryLoaded; + try { + NativeUtils.loadLibraryFromJar("/" + System.mapLibraryName("jblst")); + libraryLoaded = true; + LOG.info("Successfully loaded native BLS library"); + } catch (Throwable e) { + LOG.warn("Couldn't load native BLS library: " + e); + libraryLoaded = false; + } + INSTANCE = libraryLoaded ? Optional.of(new BlstBLS12381()) : Optional.empty(); + } + + private static Random getRND() { + // Milagro RAND has some issues with generating 'small' random numbers + // and is not thread safe + // Using non-secure random due to the JDK Linux secure random issue: + // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6521844 + // A potential attack here has a very limited application and is not feasible + // Thus using non-secure random doesn't significantly mitigate the security + return ThreadLocalRandom.current(); + } + + public static BlstSignature sign(BlstSecretKey secretKey, Bytes message) { + p2 hash = HashToCurve.hashToG2(message); + p2 p2Signature = new p2(); + try { + blst.sign_pk_in_g1(p2Signature, hash, secretKey.getScalarVal()); + p2_affine p2SignatureAffine = new p2_affine(); + blst.p2_to_affine(p2SignatureAffine, p2Signature); + + return new BlstSignature(p2SignatureAffine, true); + } finally { + hash.delete(); + p2Signature.delete(); + } + } + + public static boolean verify(BlstPublicKey publicKey, Bytes message, BlstSignature signature) { + if (publicKey.isInfinity() || signature.isInfinity()) { + return publicKey.isInfinity() && signature.isInfinity(); + } + BLST_ERROR res = + blst.core_verify_pk_in_g1( + publicKey.ecPoint, + signature.ec2Point, + 1, + message.toArrayUnsafe(), + HashToCurve.ETH2_DST.toArrayUnsafe(), + new byte[0]); + return res == BLST_ERROR.BLST_SUCCESS; + } + + @Override + public KeyPair generateKeyPair(Random random) { + BlstSecretKey secretKey = BlstSecretKey.generateNew(random); + return new KeyPair(secretKey); + } + + @Override + public BlstPublicKey publicKeyFromCompressed(Bytes48 compressedPublicKeyBytes) { + return BlstPublicKey.fromBytes(compressedPublicKeyBytes); + } + + @Override + public BlstSignature signatureFromCompressed(Bytes compressedSignatureBytes) { + return BlstSignature.fromBytes(compressedSignatureBytes); + } + + @Override + public BlstSecretKey secretKeyFromBytes(Bytes32 secretKeyBytes) { + return BlstSecretKey.fromBytes(secretKeyBytes); + } + + @Override + public BlstPublicKey aggregatePublicKeys(List publicKeys) { + return BlstPublicKey.aggregate( + publicKeys.stream().map(k -> (BlstPublicKey) k).collect(Collectors.toList())); + } + + @Override + public BlstSignature aggregateSignatures(List signatures) { + return BlstSignature.aggregate( + signatures.stream().map(s -> (BlstSignature) s).collect(Collectors.toList())); + } + + @Override + public BatchSemiAggregate prepareBatchVerify( + int index, List publicKeys, Bytes message, Signature signature) { + + BlstPublicKey aggrPubKey = aggregatePublicKeys(publicKeys); + BlstSignature blstSignature = (BlstSignature) signature; + if (aggrPubKey.isInfinity() || blstSignature.isInfinity()) { + return new BlstInfiniteSemiAggregate(aggrPubKey.isInfinity() && blstSignature.isInfinity()); + } + return blstPrepareBatchVerify(aggrPubKey, message, blstSignature); + } + + BatchSemiAggregate blstPrepareBatchVerify( + BlstPublicKey pubKey, Bytes message, BlstSignature blstSignature) { + + p2 g2Hash = HashToCurve.hashToG2(message); + p2_affine p2Affine = new p2_affine(); + pairing ctx = new pairing(); + try { + blst.p2_to_affine(p2Affine, g2Hash); + blst.pairing_init(ctx); + BLST_ERROR ret = + blst.pairing_mul_n_aggregate_pk_in_g1( + ctx, + pubKey.ecPoint, + blstSignature.ec2Point, + p2Affine, + nextBatchRandomMultiplier(), + BATCH_RANDOM_BYTES * 8); + if (ret != BLST_ERROR.BLST_SUCCESS) throw new IllegalArgumentException("Error: " + ret); + + blst.pairing_commit(ctx); + + return new BlstFiniteSemiAggregate(ctx); + } catch (Exception e) { + ctx.delete(); + throw e; + } finally { + g2Hash.delete(); + p2Affine.delete(); // not sure if its copied inside pairing_mul_n_aggregate_pk_in_g1 + } + } + + @Override + public BatchSemiAggregate prepareBatchVerify2( + int index, + List publicKeys1, + Bytes message1, + Signature signature1, + List publicKeys2, + Bytes message2, + Signature signature2) { + BatchSemiAggregate aggregate1 = prepareBatchVerify(index, publicKeys1, message1, signature1); + BatchSemiAggregate aggregate2 = + prepareBatchVerify(index + 1, publicKeys2, message2, signature2); + + return BlstFiniteSemiAggregate.merge(aggregate1, aggregate2); + } + + @Override + public boolean completeBatchVerify(List preparedList) { + try { + boolean anyInvalidInfinity = + preparedList.stream() + .filter(a -> a instanceof BlstInfiniteSemiAggregate) + .map(a -> (BlstInfiniteSemiAggregate) a) + .anyMatch(a -> !a.isValid()); + + List blstList = + preparedList.stream() + .filter(a -> a instanceof BlstFiniteSemiAggregate) + .map(b -> (BlstFiniteSemiAggregate) b) + .collect(Collectors.toList()); + + if (blstList.isEmpty()) { + return !anyInvalidInfinity; + } + pairing ctx0 = blstList.get(0).getCtx(); + boolean mergeRes = true; + for (int i = 1; i < blstList.size(); i++) { + BLST_ERROR ret = blst.pairing_merge(ctx0, blstList.get(i).getCtx()); + mergeRes &= ret == BLST_ERROR.BLST_SUCCESS; + } + + int boolRes = blst.pairing_finalverify(ctx0, null); + return mergeRes && boolRes != 0; + + } finally { + preparedList.stream() + .filter(a -> a instanceof BlstFiniteSemiAggregate) + .map(b -> (BlstFiniteSemiAggregate) b) + .forEach(BlstFiniteSemiAggregate::release); + } + } + + static BigInteger nextBatchRandomMultiplier() { + byte[] scalarBytes = new byte[BATCH_RANDOM_BYTES]; + getRND().nextBytes(scalarBytes); + return new BigInteger(1, scalarBytes); + } +} diff --git a/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/BlstFiniteSemiAggregate.java b/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/BlstFiniteSemiAggregate.java new file mode 100644 index 00000000000..08eaeef1741 --- /dev/null +++ b/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/BlstFiniteSemiAggregate.java @@ -0,0 +1,73 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * 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.bls.impl.blst; + +import tech.pegasys.teku.bls.BatchSemiAggregate; +import tech.pegasys.teku.bls.impl.blst.swig.BLST_ERROR; +import tech.pegasys.teku.bls.impl.blst.swig.blst; +import tech.pegasys.teku.bls.impl.blst.swig.pairing; + +final class BlstFiniteSemiAggregate implements BatchSemiAggregate { + + public static BatchSemiAggregate merge(BatchSemiAggregate agg1, BatchSemiAggregate agg2) { + if (agg1 instanceof BlstFiniteSemiAggregate) { + if (agg2 instanceof BlstFiniteSemiAggregate) { + ((BlstFiniteSemiAggregate) agg1).mergeWith((BlstFiniteSemiAggregate) agg2); + ((BlstFiniteSemiAggregate) agg2).release(); + return agg1; + } else { + if (((BlstInfiniteSemiAggregate) agg2).isValid()) { + return agg1; + } else { + ((BlstFiniteSemiAggregate) agg1).release(); + return agg2; + } + } + } else { + if (((BlstInfiniteSemiAggregate) agg1).isValid()) { + return agg2; + } else { + if (agg2 instanceof BlstFiniteSemiAggregate) { + ((BlstFiniteSemiAggregate) agg2).release(); + } + return agg1; + } + } + } + + private final pairing ctx; + private boolean released = false; + + BlstFiniteSemiAggregate(pairing ctx) { + this.ctx = ctx; + } + + pairing getCtx() { + if (released) throw new IllegalStateException("Attempting to use disposed BatchSemiAggregate"); + return ctx; + } + + void release() { + if (released) throw new IllegalStateException("Attempting to use disposed BatchSemiAggregate"); + released = true; + ctx.delete(); + } + + void mergeWith(BlstFiniteSemiAggregate other) { + BLST_ERROR ret = blst.pairing_merge(getCtx(), other.getCtx()); + if (ret != BLST_ERROR.BLST_SUCCESS) { + throw new IllegalStateException("Error merging Blst pairing contexts: " + ret); + } + } +} diff --git a/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/BlstInfiniteSemiAggregate.java b/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/BlstInfiniteSemiAggregate.java new file mode 100644 index 00000000000..5a513628440 --- /dev/null +++ b/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/BlstInfiniteSemiAggregate.java @@ -0,0 +1,28 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * 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.bls.impl.blst; + +import tech.pegasys.teku.bls.BatchSemiAggregate; + +final class BlstInfiniteSemiAggregate implements BatchSemiAggregate { + private final boolean isValid; + + public BlstInfiniteSemiAggregate(boolean isValid) { + this.isValid = isValid; + } + + public boolean isValid() { + return isValid; + } +} diff --git a/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/BlstPublicKey.java b/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/BlstPublicKey.java new file mode 100644 index 00000000000..987cec825f6 --- /dev/null +++ b/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/BlstPublicKey.java @@ -0,0 +1,157 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * 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.bls.impl.blst; + +import static com.google.common.base.Preconditions.checkArgument; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes48; +import tech.pegasys.teku.bls.impl.PublicKey; +import tech.pegasys.teku.bls.impl.blst.swig.BLST_ERROR; +import tech.pegasys.teku.bls.impl.blst.swig.blst; +import tech.pegasys.teku.bls.impl.blst.swig.p1; +import tech.pegasys.teku.bls.impl.blst.swig.p1_affine; + +public class BlstPublicKey implements PublicKey { + private static final int COMPRESSED_PK_SIZE = 48; + private static final int UNCOMPRESSED_PK_LENGTH = 96; + + static final Bytes48 INFINITY_COMPRESSED_BYTES = + Bytes48.fromHexString( + "0x" + + "c0000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000"); + + static final BlstPublicKey INFINITY = + new BlstPublicKey(null) { + @Override + public void forceValidation() {} + + @Override + public Bytes48 toBytesCompressed() { + return INFINITY_COMPRESSED_BYTES; + } + + @Override + public Bytes toBytesUncompressed() { + throw new UnsupportedOperationException(); + } + }; + + public static BlstPublicKey fromBytesUncompressed(Bytes uncompressed) { + checkArgument(uncompressed.size() == UNCOMPRESSED_PK_LENGTH); + p1_affine ecPoint = new p1_affine(); + if (blst.p1_deserialize(ecPoint, uncompressed.toArrayUnsafe()) == BLST_ERROR.BLST_SUCCESS) { + return new BlstPublicKey(ecPoint); + } else { + ecPoint.delete(); + throw new IllegalArgumentException("Invalid PublicKey bytes: " + uncompressed); + } + } + + public static BlstPublicKey fromBytes(Bytes48 compressed) { + if (compressed.equals(INFINITY_COMPRESSED_BYTES)) { + return INFINITY; + } + p1_affine ecPoint = new p1_affine(); + if (blst.p1_uncompress(ecPoint, compressed.toArrayUnsafe()) == BLST_ERROR.BLST_SUCCESS) { + return new BlstPublicKey(ecPoint); + } else { + ecPoint.delete(); + throw new IllegalArgumentException("Invalid PublicKey bytes: " + compressed); + } + } + + public static BlstPublicKey aggregate(List publicKeys) { + checkArgument(publicKeys.size() > 0); + + List finitePublicKeys = + publicKeys.stream().filter(pk -> !pk.isInfinity()).collect(Collectors.toList()); + if (finitePublicKeys.isEmpty()) { + return BlstPublicKey.INFINITY; + } + + p1 sum = new p1(); + try { + blst.p1_from_affine(sum, finitePublicKeys.get(0).ecPoint); + for (int i = 1; i < finitePublicKeys.size(); i++) { + blst.p1_add_or_double_affine(sum, sum, finitePublicKeys.get(i).ecPoint); + } + p1_affine res = new p1_affine(); + blst.p1_to_affine(res, sum); + + return new BlstPublicKey(res); + } finally { + sum.delete(); + } + } + + final p1_affine ecPoint; + + public BlstPublicKey(p1_affine ecPoint) { + this.ecPoint = ecPoint; + } + + @Override + public void forceValidation() throws IllegalArgumentException { + if (blst.p1_affine_in_g1(ecPoint) == 0) { + throw new IllegalArgumentException("Invalid PublicKey: " + toBytesCompressed()); + } + } + + @Override + public Bytes48 toBytesCompressed() { + byte[] res = new byte[COMPRESSED_PK_SIZE]; + blst.p1_affine_compress(res, ecPoint); + return Bytes48.wrap(res); + } + + public Bytes toBytesUncompressed() { + byte[] res = new byte[UNCOMPRESSED_PK_LENGTH]; + blst.p1_affine_serialize(res, ecPoint); + return Bytes.wrap(res); + } + + @SuppressWarnings("ReferenceEquality") + boolean isInfinity() { + return this == INFINITY; + } + + @Override + public int hashCode() { + return toBytesCompressed().hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + BlstPublicKey that = (BlstPublicKey) o; + return Objects.equals(toBytesCompressed(), that.toBytesCompressed()); + } + + @Override + public String toString() { + return toBytesCompressed().toHexString(); + } +} diff --git a/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/BlstSecretKey.java b/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/BlstSecretKey.java new file mode 100644 index 00000000000..69122ab41a0 --- /dev/null +++ b/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/BlstSecretKey.java @@ -0,0 +1,124 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * 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.bls.impl.blst; + +import java.util.Objects; +import java.util.Random; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import tech.pegasys.teku.bls.impl.SecretKey; +import tech.pegasys.teku.bls.impl.Signature; +import tech.pegasys.teku.bls.impl.blst.swig.blst; +import tech.pegasys.teku.bls.impl.blst.swig.p1; +import tech.pegasys.teku.bls.impl.blst.swig.p1_affine; +import tech.pegasys.teku.bls.impl.blst.swig.scalar; + +public class BlstSecretKey implements SecretKey { + static final BlstSecretKey ZERO_SK = BlstSecretKey.fromBytesRaw(Bytes32.ZERO); + + public static BlstSecretKey fromBytes(Bytes32 bytes) { + if (bytes.isZero()) { + return ZERO_SK; + } else { + return fromBytesRaw(bytes); + } + } + + private static BlstSecretKey fromBytesRaw(Bytes32 bytes) { + scalar scalarVal = new scalar(); + blst.scalar_from_bendian(scalarVal, bytes.toArrayUnsafe()); + return new BlstSecretKey(scalarVal); + } + + public static BlstSecretKey generateNew(Random random) { + byte[] ikm = new byte[128]; + random.nextBytes(ikm); + scalar sk = new scalar(); + blst.keygen(sk, ikm, null); + return new BlstSecretKey(sk); + } + + private final scalar scalarVal; + private boolean destroyed = false; + + public BlstSecretKey(scalar scalarVal) { + this.scalarVal = scalarVal; + } + + @Override + public Bytes32 toBytes() { + byte[] res = new byte[32]; + blst.bendian_from_scalar(res, getScalarVal()); + return Bytes32.wrap(res); + } + + @Override + public Signature sign(Bytes message) { + if (isZero()) { + return BlstSignature.INFINITY; + } + return BlstBLS12381.sign(this, message); + } + + @Override + public void destroy() { + blst.scalar_from_bendian(getScalarVal(), Bytes32.ZERO.toArrayUnsafe()); + destroyed = true; + } + + @Override + public BlstPublicKey derivePublicKey() { + if (isZero()) { + return BlstPublicKey.INFINITY; + } + p1 pk = new p1(); + try { + blst.sk_to_pk_in_g1(pk, getScalarVal()); + p1_affine pkAffine = new p1_affine(); + blst.p1_to_affine(pkAffine, pk); + + return new BlstPublicKey(pkAffine); + } finally { + pk.delete(); + } + } + + scalar getScalarVal() { + if (destroyed) throw new IllegalStateException("Private key was destroyed"); + return scalarVal; + } + + @SuppressWarnings("ReferenceEquality") + boolean isZero() { + return this == ZERO_SK; + } + + @Override + public int hashCode() { + return toBytes().hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + BlstSecretKey that = (BlstSecretKey) o; + return Objects.equals(toBytes(), that.toBytes()); + } +} diff --git a/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/BlstSignature.java b/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/BlstSignature.java new file mode 100644 index 00000000000..e93cd6f7dcc --- /dev/null +++ b/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/BlstSignature.java @@ -0,0 +1,201 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * 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.bls.impl.blst; + +import static com.google.common.base.Preconditions.checkArgument; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import org.apache.tuweni.bytes.Bytes; +import tech.pegasys.teku.bls.impl.PublicKey; +import tech.pegasys.teku.bls.impl.PublicKeyMessagePair; +import tech.pegasys.teku.bls.impl.Signature; +import tech.pegasys.teku.bls.impl.blst.swig.BLST_ERROR; +import tech.pegasys.teku.bls.impl.blst.swig.blst; +import tech.pegasys.teku.bls.impl.blst.swig.p2; +import tech.pegasys.teku.bls.impl.blst.swig.p2_affine; +import tech.pegasys.teku.bls.impl.blst.swig.pairing; + +public class BlstSignature implements Signature { + private static final int COMPRESSED_SIG_SIZE = 96; + + private static final Bytes INFINITY_BYTES = + Bytes.fromHexString( + "0x" + + "c000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000"); + static final BlstSignature INFINITY; + + static { + p2_affine ec2Point = new p2_affine(); + blst.p2_uncompress(ec2Point, INFINITY_BYTES.toArrayUnsafe()); + INFINITY = new BlstSignature(ec2Point, true); + } + + public static BlstSignature fromBytes(Bytes compressed) { + if (compressed.equals(INFINITY_BYTES)) { + return INFINITY; + } + checkArgument( + compressed.size() == COMPRESSED_SIG_SIZE, + "Expected " + COMPRESSED_SIG_SIZE + " bytes of input but got %s", + compressed.size()); + p2_affine ec2Point = new p2_affine(); + try { + BLST_ERROR rc = blst.p2_uncompress(ec2Point, compressed.toArrayUnsafe()); + return new BlstSignature(ec2Point, rc == BLST_ERROR.BLST_SUCCESS); + } catch (Exception e) { + ec2Point.delete(); + throw e; + } + } + + public static BlstSignature aggregate(List signatures) { + List finiteSignatures = + signatures.stream().filter(sig -> !sig.isInfinity()).collect(Collectors.toList()); + + Optional invalidSignature = + finiteSignatures.stream().filter(s -> !s.isValid).findFirst(); + if (invalidSignature.isPresent()) { + throw new IllegalArgumentException( + "Can't aggregate invalid signature: " + invalidSignature.get()); + } + + p2 sum = new p2(); + try { + blst.p2_from_affine(sum, finiteSignatures.get(0).ec2Point); + for (int i = 1; i < finiteSignatures.size(); i++) { + blst.p2_add_affine(sum, sum, finiteSignatures.get(i).ec2Point); + } + p2_affine res = new p2_affine(); + blst.p2_to_affine(res, sum); + + return new BlstSignature(res, true); + } finally { + sum.delete(); + } + } + + private static void blstPrepareVerifyAggregated( + BlstPublicKey pubKey, Bytes message, pairing ctx, BlstSignature blstSignature) { + + p2 g2Hash = HashToCurve.hashToG2(message); + p2_affine p2Affine = new p2_affine(); + + try { + blst.p2_to_affine(p2Affine, g2Hash); + + BLST_ERROR ret = + blst.pairing_aggregate_pk_in_g1( + ctx, + pubKey.ecPoint, + blstSignature == null ? null : blstSignature.ec2Point, + 1, + message.toArrayUnsafe(), + HashToCurve.ETH2_DST.toArrayUnsafe(), + null); + if (ret != BLST_ERROR.BLST_SUCCESS) throw new IllegalArgumentException("Error: " + ret); + } finally { + g2Hash.delete(); + p2Affine.delete(); + } + } + + private static boolean blstCompleteVerifyAggregated(pairing ctx) { + try { + blst.pairing_commit(ctx); + return blst.pairing_finalverify(ctx, null) > 0; + } finally { + ctx.delete(); + } + } + + final p2_affine ec2Point; + private final boolean isValid; + + public BlstSignature(p2_affine ec2Point, boolean isValid) { + this.ec2Point = ec2Point; + this.isValid = isValid; + } + + @Override + public Bytes toBytesCompressed() { + byte[] res = new byte[96]; + blst.p2_affine_compress(res, ec2Point); + return Bytes.wrap(res); + } + + @Override + public Bytes toBytesUncompressed() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean verify(List keysToMessages) { + + pairing ctx = new pairing(); + + try { + blst.pairing_init(ctx); + for (int i = 0; i < keysToMessages.size(); i++) { + BlstPublicKey publicKey = (BlstPublicKey) keysToMessages.get(i).getPublicKey(); + Bytes message = keysToMessages.get(i).getMessage(); + BlstSignature signature = i == 0 ? this : null; + blstPrepareVerifyAggregated(publicKey, message, ctx, signature); + } + return blstCompleteVerifyAggregated(ctx); + } finally { + ctx.delete(); + } + } + + @Override + public boolean verify(List publicKeys, Bytes message) { + return verify( + BlstPublicKey.aggregate( + publicKeys.stream().map(k -> (BlstPublicKey) k).collect(Collectors.toList())), + message); + } + + @Override + public boolean verify(PublicKey publicKey, Bytes message) { + return BlstBLS12381.verify((BlstPublicKey) publicKey, message, this); + } + + @SuppressWarnings("ReferenceEquality") + boolean isInfinity() { + return this == INFINITY; + } + + @Override + public int hashCode() { + return toBytesCompressed().hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + BlstSignature that = (BlstSignature) o; + return Objects.equals(toBytesCompressed(), that.toBytesCompressed()); + } +} diff --git a/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/HashToCurve.java b/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/HashToCurve.java new file mode 100644 index 00000000000..23b69e85feb --- /dev/null +++ b/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/HashToCurve.java @@ -0,0 +1,41 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * 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.bls.impl.blst; + +import java.nio.charset.StandardCharsets; +import org.apache.tuweni.bytes.Bytes; +import tech.pegasys.teku.bls.impl.blst.swig.blst; +import tech.pegasys.teku.bls.impl.blst.swig.p2; + +class HashToCurve { + // The ciphersuite defined in the Eth2 specification which also serves as domain separation tag + // https://github.com/ethereum/eth2.0-specs/blob/v0.12.0/specs/phase0/beacon-chain.md#bls-signatures + static final Bytes ETH2_DST = + Bytes.wrap("BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_".getBytes(StandardCharsets.US_ASCII)); + + static p2 hashToG2(Bytes message) { + return hashToG2(message, ETH2_DST); + } + + static p2 hashToG2(Bytes message, Bytes dst) { + p2 p2Hash = new p2(); + try { + blst.hash_to_g2(p2Hash, message.toArray(), dst.toArray(), new byte[0]); + return p2Hash; + } catch (Exception e) { + p2Hash.delete(); + throw e; + } + } +} diff --git a/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/NativeUtils.java b/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/NativeUtils.java new file mode 100644 index 00000000000..4fa11230fc7 --- /dev/null +++ b/bls/src/main/java/tech/pegasys/teku/bls/impl/blst/NativeUtils.java @@ -0,0 +1,131 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * 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.bls.impl.blst; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.FileSystemNotFoundException; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.ProviderNotFoundException; +import java.nio.file.StandardCopyOption; + +/** + * A simple library class which helps with loading dynamic libraries stored in the JAR archive. + * These libraries usually contain implementation of some methods in native code (using JNI - Java + * Native Interface). + * + * @see http://adamheinrich.com/blog/2012/how-to-load-native-jni-library-from-jar + * @see https://github.com/adamheinrich/native-utils + */ +public class NativeUtils { + + /** + * The minimum length a prefix for a file has to have according to {@link + * File#createTempFile(String, String)}}. + */ + private static final int MIN_PREFIX_LENGTH = 3; + + public static final String NATIVE_FOLDER_PATH_PREFIX = "nativeutils"; + + /** Temporary directory which will contain the DLLs. */ + private static File temporaryDir; + + /** Private constructor - this class will never be instanced */ + private NativeUtils() {} + + /** + * Loads library from current JAR archive + * + *

The file from JAR is copied into system temporary directory and then loaded. The temporary + * file is deleted after exiting. Method uses String as filename because the pathname is + * "abstract", not system-dependent. + * + * @param path The path of file inside JAR as absolute path (beginning with '/'), e.g. + * /package/File.ext + * @throws IOException If temporary file creation or read/write operation fails + * @throws IllegalArgumentException If source file (param path) does not exist + * @throws IllegalArgumentException If the path is not absolute or if the filename is shorter than + * three characters (restriction of {@link File#createTempFile(java.lang.String, + * java.lang.String)}). + * @throws FileNotFoundException If the file could not be found inside the JAR. + */ + public static void loadLibraryFromJar(String path) throws IOException { + + if (null == path || !path.startsWith("/")) { + throw new IllegalArgumentException("The path has to be absolute (start with '/')."); + } + + // Obtain filename from path + String[] parts = path.split("/", -1); + String filename = (parts.length > 1) ? parts[parts.length - 1] : null; + + // Check if the filename is okay + if (filename == null || filename.length() < MIN_PREFIX_LENGTH) { + throw new IllegalArgumentException("The filename has to be at least 3 characters long."); + } + + // Prepare temporary file + if (temporaryDir == null) { + temporaryDir = createTempDirectory(NATIVE_FOLDER_PATH_PREFIX); + temporaryDir.deleteOnExit(); + } + + File temp = new File(temporaryDir, filename); + + try (InputStream is = NativeUtils.class.getResourceAsStream(path)) { + Files.copy(is, temp.toPath(), StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + temp.delete(); + throw e; + } catch (NullPointerException e) { + temp.delete(); + throw new FileNotFoundException("File " + path + " was not found inside JAR."); + } + + try { + System.load(temp.getAbsolutePath()); + } finally { + if (isPosixCompliant()) { + // Assume POSIX compliant file system, can be deleted after loading + temp.delete(); + } else { + // Assume non-POSIX, and don't delete until last file descriptor closed + temp.deleteOnExit(); + } + } + } + + private static boolean isPosixCompliant() { + try { + return FileSystems.getDefault().supportedFileAttributeViews().contains("posix"); + } catch (FileSystemNotFoundException | ProviderNotFoundException | SecurityException e) { + return false; + } + } + + private static File createTempDirectory(String prefix) throws IOException { + String tempDir = System.getProperty("java.io.tmpdir"); + File generatedDir = new File(tempDir, prefix + System.nanoTime()); + + if (!generatedDir.mkdir()) + throw new IOException("Failed to create temp directory " + generatedDir.getName()); + + return generatedDir; + } +} diff --git a/bls/src/main/java/tech/pegasys/teku/bls/impl/mikuli/MikuliBLS12381.java b/bls/src/main/java/tech/pegasys/teku/bls/impl/mikuli/MikuliBLS12381.java index 9aeaae3b624..e896267c294 100644 --- a/bls/src/main/java/tech/pegasys/teku/bls/impl/mikuli/MikuliBLS12381.java +++ b/bls/src/main/java/tech/pegasys/teku/bls/impl/mikuli/MikuliBLS12381.java @@ -78,7 +78,7 @@ public KeyPair generateKeyPair(Random random) { } @Override - public PublicKey publicKeyFromCompressed(Bytes compressedPublicKeyBytes) { + public PublicKey publicKeyFromCompressed(Bytes48 compressedPublicKeyBytes) { return MikuliPublicKey.fromBytesCompressed(compressedPublicKeyBytes); } diff --git a/bls/src/main/java/tech/pegasys/teku/bls/impl/mikuli/MikuliPublicKey.java b/bls/src/main/java/tech/pegasys/teku/bls/impl/mikuli/MikuliPublicKey.java index ac99cff0642..a1e03104bf0 100644 --- a/bls/src/main/java/tech/pegasys/teku/bls/impl/mikuli/MikuliPublicKey.java +++ b/bls/src/main/java/tech/pegasys/teku/bls/impl/mikuli/MikuliPublicKey.java @@ -19,6 +19,7 @@ import java.util.Objects; import java.util.function.Supplier; import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes48; import tech.pegasys.teku.bls.impl.PublicKey; /** This class represents a BLS12-381 public key. */ @@ -137,9 +138,9 @@ public MikuliPublicKey combine(MikuliPublicKey pk) { * @return byte array representation of the public key */ @Override - public Bytes toBytesCompressed() { + public Bytes48 toBytesCompressed() { Bytes data = rawData.get(); - return data.size() == COMPRESSED_PK_SIZE ? data : point.get().toBytesCompressed(); + return Bytes48.wrap(data.size() == COMPRESSED_PK_SIZE ? data : point.get().toBytesCompressed()); } public G1Point g1Point() { diff --git a/bls/src/main/java/tech/pegasys/teku/bls/impl/mikuli/MikuliSecretKey.java b/bls/src/main/java/tech/pegasys/teku/bls/impl/mikuli/MikuliSecretKey.java index bc7e617c068..1db24da6ff5 100644 --- a/bls/src/main/java/tech/pegasys/teku/bls/impl/mikuli/MikuliSecretKey.java +++ b/bls/src/main/java/tech/pegasys/teku/bls/impl/mikuli/MikuliSecretKey.java @@ -18,8 +18,8 @@ import java.util.Objects; import org.apache.milagro.amcl.BLS381.BIG; import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.bytes.Bytes48; -import tech.pegasys.teku.bls.impl.PublicKey; import tech.pegasys.teku.bls.impl.SecretKey; import tech.pegasys.teku.bls.impl.Signature; @@ -57,14 +57,14 @@ public G2Point sign(G2Point message) { } @Override - public Bytes toBytes() { + public Bytes32 toBytes() { byte[] bytea = new byte[MODBYTES]; scalarValue.value().toBytes(bytea); - return Bytes.wrap(bytea); + return Bytes32.wrap(bytea, 16); } @Override - public PublicKey derivePublicKey() { + public MikuliPublicKey derivePublicKey() { return new MikuliPublicKey(this); } diff --git a/bls/src/test/java/tech/pegasys/teku/bls/BLSPerformanceRunner.java b/bls/src/test/java/tech/pegasys/teku/bls/BLSPerformanceRunner.java index 3a536b826b2..a1c2ccd8695 100644 --- a/bls/src/test/java/tech/pegasys/teku/bls/BLSPerformanceRunner.java +++ b/bls/src/test/java/tech/pegasys/teku/bls/BLSPerformanceRunner.java @@ -141,7 +141,7 @@ void testAggregationTime(Integer i) { void testBLSPubKeyDeserialize(Integer i) { Bytes emptyBytesSsz = SSZ.encode(writer -> writer.writeFixedBytes(Bytes.wrap(new byte[48]))); - Long time = executeRun(() -> BLSPublicKey.fromBytes(emptyBytesSsz), i); + Long time = executeRun(() -> BLSPublicKey.fromSSZBytes(emptyBytesSsz), i); LOG.info("Time for i: {}, time: {}", i, time); } @@ -150,7 +150,7 @@ void testBLSPubKeyDeserialize(Integer i) { void testBLSPubKeySerialize(Integer i) { BLSPublicKey emptyPublicKey = BLSPublicKey.empty(); - Long time = executeRun(() -> emptyPublicKey.toBytes().toHexString(), i); + Long time = executeRun(() -> emptyPublicKey.toSSZBytes().toHexString(), i); LOG.info("Time for i: {}, time: {}", i, time); } @@ -159,7 +159,7 @@ void testBLSPubKeySerialize(Integer i) { void testSignatureSerialize(Integer i) { BLSSignature signature1 = BLSSignature.random(); - Long time = executeRun(signature1::toBytes, i); + Long time = executeRun(signature1::toSSZBytes, i); LOG.info("Time for i: {}, time: {}", i, time); } @@ -167,9 +167,9 @@ void testSignatureSerialize(Integer i) { @MethodSource("singleAggregationCountOrder4") void testSignatureDeserialize(Integer i) { BLSSignature signature1 = BLSSignature.random(); - Bytes bytes = signature1.toBytes(); + Bytes bytes = signature1.toSSZBytes(); - Long time = executeRun(() -> BLSSignature.fromBytes(bytes), i); + Long time = executeRun(() -> BLSSignature.fromSSZBytes(bytes), i); LOG.info("Time for i: {}, time: {}", i, time); } diff --git a/bls/src/test/java/tech/pegasys/teku/bls/BLSPublicKeyTest.java b/bls/src/test/java/tech/pegasys/teku/bls/BLSPublicKeyTest.java index 55b3516fd1a..9fe89d1632c 100644 --- a/bls/src/test/java/tech/pegasys/teku/bls/BLSPublicKeyTest.java +++ b/bls/src/test/java/tech/pegasys/teku/bls/BLSPublicKeyTest.java @@ -13,13 +13,16 @@ package tech.pegasys.teku.bls; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.List; import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes48; import org.apache.tuweni.ssz.SSZ; import org.junit.jupiter.api.Test; @@ -29,16 +32,22 @@ class BLSPublicKeyTest { "0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); @Test - void isValidReturnsTrueForValidKey() { - BLSPublicKey publicKey = BLSPublicKey.random(1); - assertTrue(publicKey.isValid()); + void fromBytesCompressedValidate_okWhenValidBytes() { + assertThatCode( + () -> + BLSPublicKey.fromBytesCompressedValidate( + BLSPublicKey.random(1).toBytesCompressed())) + .doesNotThrowAnyException(); } @Test - void isValidReturnsFalseForInvalidKey() { + void fromBytesCompressedValidate_throwsOnInvalidData() { BLSPublicKey publicKey = BLSPublicKey.random(1); - BLSPublicKey invalidPublicKey = BLSPublicKey.fromBytes(publicKey.toBytes().shiftLeft(1)); - assertFalse(invalidPublicKey.isValid()); + assertThatThrownBy( + () -> + BLSPublicKey.fromBytesCompressedValidate( + Bytes48.wrap(publicKey.toBytesCompressed().shiftLeft(1)))) + .isInstanceOf(IllegalArgumentException.class); } @Test @@ -50,32 +59,30 @@ void succeedsWhenEqualsReturnsTrueForTheSameEmptyPublicKey() { @Test void succeedsWhenTwoInfinityPublicKeysAreEqual() { // Infinity keys are valid G1 points, so pass the equality test - BLSPublicKey publicKey1 = BLSPublicKey.fromBytes(InfinityPublicKey); - BLSPublicKey publicKey2 = BLSPublicKey.fromBytes(InfinityPublicKey); + BLSPublicKey publicKey1 = BLSPublicKey.fromSSZBytes(InfinityPublicKey); + BLSPublicKey publicKey2 = BLSPublicKey.fromSSZBytes(InfinityPublicKey); assertEquals(publicKey1, publicKey2); } @Test - void succeedsWhenInvalidPublicKeyIsInvalid() { - BLSPublicKey invalidPublicKey = - BLSPublicKey.fromBytesCompressed( - Bytes.fromHexString( - "0x9378a6e3984e96d2cd50450c76ca14732f1300efa04aecdb805b22e6d6926a85ef409e8f3acf494a1481090bf32ce3bd")); - assertFalse(invalidPublicKey.isValid()); + void fromBytesCompressedValidate_throwsOnInvalidPubKey() { + Bytes48 invalidPublicKeyBytes = + Bytes48.fromHexString( + "0x9378a6e3984e96d2cd50450c76ca14732f1300efa04aecdb805b22e6d6926a85ef409e8f3acf494a1481090bf32ce3bd"); + assertThatThrownBy(() -> BLSPublicKey.fromBytesCompressedValidate(invalidPublicKeyBytes)) + .isInstanceOf(IllegalArgumentException.class); } @Test void succeedsWhenComparingInvalidAndValidPublicKeyFails() { BLSPublicKey invalidPublicKey = BLSPublicKey.fromBytesCompressed( - Bytes.fromHexString( + Bytes48.fromHexString( "0x9378a6e3984e96d2cd50450c76ca14732f1300efa04aecdb805b22e6d6926a85ef409e8f3acf494a1481090bf32ce3bd")); BLSPublicKey validPublicKey = BLSPublicKey.fromBytesCompressed( - Bytes.fromHexString( + Bytes48.fromHexString( "0xb51aa9cdb40ed3e7e5a9b3323550fe323ecd5c7f5cb3d8b47af55a061811bc7da0397986cad0d565c0bdbbe99af24355")); - assertFalse(invalidPublicKey.isValid()); - assertTrue(validPublicKey.isValid()); assertNotEquals(validPublicKey, invalidPublicKey); } @@ -83,11 +90,11 @@ void succeedsWhenComparingInvalidAndValidPublicKeyFails() { void succeedsWhenInvalidPublicReturnsHashCode() { BLSPublicKey invalidPublicKey = BLSPublicKey.fromBytesCompressed( - Bytes.fromHexString( + Bytes48.fromHexString( "0x9378a6e3984e96d2cd50450c76ca14732f1300efa04aecdb805b22e6d6926a85ef409e8f3acf494a1481090bf32ce3bd")); BLSPublicKey validPublicKey = BLSPublicKey.fromBytesCompressed( - Bytes.fromHexString( + Bytes48.fromHexString( "0xb51aa9cdb40ed3e7e5a9b3323550fe323ecd5c7f5cb3d8b47af55a061811bc7da0397986cad0d565c0bdbbe99af24355")); assertNotEquals(invalidPublicKey.hashCode(), validPublicKey.hashCode()); assertEquals(invalidPublicKey.hashCode(), invalidPublicKey.hashCode()); @@ -99,12 +106,12 @@ void succeedsIfSerializationOfEmptyPublicKeyIsCorrect() { assertEquals( "0x000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000", - emptyPublicKey.toBytes().toHexString()); + emptyPublicKey.toSSZBytes().toHexString()); } @Test void succeedsIfDeserializationOfInfinityPublicKeyIsCorrect() { - BLSPublicKey infinityPublicKey = BLSPublicKey.fromBytes(InfinityPublicKey); + BLSPublicKey infinityPublicKey = BLSPublicKey.fromSSZBytes(InfinityPublicKey); byte[] pointBytes = new byte[48]; pointBytes[0] = (byte) 0xc0; Bytes infinityBytesSsz = @@ -112,14 +119,14 @@ void succeedsIfDeserializationOfInfinityPublicKeyIsCorrect() { writer -> { writer.writeFixedBytes(Bytes.wrap(pointBytes)); }); - BLSPublicKey deserializedPublicKey = BLSPublicKey.fromBytes(infinityBytesSsz); + BLSPublicKey deserializedPublicKey = BLSPublicKey.fromSSZBytes(infinityBytesSsz); assertEquals(infinityPublicKey, deserializedPublicKey); } @Test void succeedsIfDeserializationThrowsWithTooFewBytes() { Bytes tooFewBytes = Bytes.wrap(new byte[51]); - assertThrows(IllegalArgumentException.class, () -> BLSPublicKey.fromBytes(tooFewBytes)); + assertThrows(IllegalArgumentException.class, () -> BLSPublicKey.fromSSZBytes(tooFewBytes)); } @Test @@ -128,13 +135,6 @@ void succeedsWhenEqualsReturnsTrueForTheSamePublicKey() { assertEquals(publicKey, publicKey); } - @Test - void succeedsWhenEqualsReturnsTrueForIdenticalPublicKeys() { - BLSPublicKey publicKey = BLSPublicKey.random(42); - BLSPublicKey copyOfPublicKey = new BLSPublicKey(publicKey); - assertEquals(publicKey, copyOfPublicKey); - } - @Test void succeedsWhenEqualsReturnsFalseForDifferentPublicKeys() { BLSPublicKey publicKey1 = BLSPublicKey.random(1); @@ -147,8 +147,8 @@ public void succeedsWhenEqualsReturnsTrueForEquivalentPublicKeysCreatedFromDiffe BLSPublicKey publicKey1 = BLSPublicKey.random(1); Bytes compressedBytes = publicKey1.toBytesCompressed(); - BLSPublicKey publicKey2 = BLSPublicKey.fromBytes(compressedBytes); - BLSPublicKey publicKey3 = BLSPublicKey.fromBytes(compressedBytes); + BLSPublicKey publicKey2 = BLSPublicKey.fromSSZBytes(compressedBytes); + BLSPublicKey publicKey3 = BLSPublicKey.fromSSZBytes(compressedBytes); assertEquals(publicKey1, publicKey2); assertEquals(publicKey2, publicKey3); } @@ -156,14 +156,30 @@ public void succeedsWhenEqualsReturnsTrueForEquivalentPublicKeysCreatedFromDiffe @Test void succeedsWhenRoundtripSSZReturnsTheSamePublicKey() { BLSPublicKey publicKey1 = BLSPublicKey.random(42); - BLSPublicKey publicKey2 = BLSPublicKey.fromBytes(publicKey1.toBytes()); + BLSPublicKey publicKey2 = BLSPublicKey.fromSSZBytes(publicKey1.toSSZBytes()); assertEquals(publicKey1, publicKey2); } @Test void succeedsWhenRoundtripSSZReturnsTheInfinityPublicKey() { - BLSPublicKey publicKey1 = BLSPublicKey.fromBytes(InfinityPublicKey); - BLSPublicKey publicKey2 = BLSPublicKey.fromBytes(publicKey1.toBytes()); + BLSPublicKey publicKey1 = BLSPublicKey.fromSSZBytes(InfinityPublicKey); + BLSPublicKey publicKey2 = BLSPublicKey.fromSSZBytes(publicKey1.toSSZBytes()); assertEquals(publicKey1, publicKey2); } + + @Test + void aggregateSamePubKeys() { + BLSPublicKey pk = + BLSPublicKey.fromBytesCompressedValidate( + Bytes48.fromHexString( + "0x89ece308f9d1f0131765212deca99697b112d61f9be9a5f1f3780a51335b3ff981747a0b2ca2179b96d2c0c9024e5224")); + + BLSPublicKey aggrPk = BLSPublicKey.aggregate(List.of(pk, pk)); + + BLSPublicKey aggrPkGolden = + BLSPublicKey.fromBytesCompressedValidate( + Bytes48.fromHexString( + "0xa6e82f6da4520f85c5d27d8f329eccfa05944fd1096b20734c894966d12a9e2a9a9744529d7212d33883113a0cadb909")); + assertThat(aggrPk).isEqualTo(aggrPkGolden); + } } diff --git a/bls/src/test/java/tech/pegasys/teku/bls/BLSSecretKeyTest.java b/bls/src/test/java/tech/pegasys/teku/bls/BLSSecretKeyTest.java new file mode 100644 index 00000000000..65490bc2e10 --- /dev/null +++ b/bls/src/test/java/tech/pegasys/teku/bls/BLSSecretKeyTest.java @@ -0,0 +1,77 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * 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.bls; + +import java.util.stream.Stream; +import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.bytes.Bytes48; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + +public class BLSSecretKeyTest { + + @ParameterizedTest + @ValueSource( + strings = { + "0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001", // r + "0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000002", + "0x74eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00010000", + "0x79eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00010000", + "0x80eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00010000", + "0x83eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00010000", + "0xe7db4ea6533afa906673b0101343b00aa77b4805fffcb7fdfffffffe00000001", + "0xe7db4ea6533afa906673b0101343b00aa77b4805fffcb7fdfffffffe00000002", // r * 2 + "0xe7db4ea6533afa906673b0101343b00aa77b4805fffcb7fdfffffffe00000003", + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + }) + void secretKeyFromBytes_shouldThrowWhenInvaidBytes(String skString) { + Bytes32 sk1 = Bytes32.fromHexString(skString); + Assertions.assertThatThrownBy(() -> BLSSecretKey.fromBytes(sk1)) + .isInstanceOf(IllegalArgumentException.class); + } + + @ParameterizedTest + @MethodSource("getSecretKeysToPubKeys") + void secretKeyFromBytes_shouldYielCorrectPublicKey(String skString, String compressedPubKey) { + BLSSecretKey sk = BLSSecretKey.fromBytes(Bytes32.fromHexString(skString)); + Assertions.assertThat(sk.toPublicKey().toBytesCompressed()) + .isEqualTo(Bytes48.fromHexString(compressedPubKey)); + } + + public static Stream getSecretKeysToPubKeys() { + Stream.Builder builder = Stream.builder(); + + builder.add( + Arguments.of( + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")); + builder.add( + Arguments.of( + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb")); + builder.add( + Arguments.of( + "0x72ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "0xb5d2c2f45a9d8429e2fc28ffe844601b3d87490682f5dab702ac090fd3d1ec3fe3cc3e5ffb63ca36bc640a2b9f73cc3f")); + builder.add( + Arguments.of( + "0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000", + "0xb7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb")); + + return builder.build(); + } +} diff --git a/bls/src/test/java/tech/pegasys/teku/bls/BLSSignatureTest.java b/bls/src/test/java/tech/pegasys/teku/bls/BLSSignatureTest.java index 3ab73695597..61479432ff4 100644 --- a/bls/src/test/java/tech/pegasys/teku/bls/BLSSignatureTest.java +++ b/bls/src/test/java/tech/pegasys/teku/bls/BLSSignatureTest.java @@ -43,7 +43,7 @@ void succeedsIfSerializationOfEmptySignatureIsCorrect() { "0x0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000", - emptySignature.toBytes().toHexString()); + emptySignature.toSSZBytes().toHexString()); } @Test @@ -51,27 +51,28 @@ void succeedsIfDeserializationOfEmptySignatureIsCorrect() { BLSSignature emptySignature = BLSSignature.empty(); Bytes zeroBytes = Bytes.wrap(new byte[96]); Bytes emptyBytesSsz = SSZ.encode(writer -> writer.writeFixedBytes(zeroBytes)); - BLSSignature deserializedSignature = BLSSignature.fromBytes(emptyBytesSsz); + BLSSignature deserializedSignature = BLSSignature.fromSSZBytes(emptyBytesSsz); assertEquals(emptySignature, deserializedSignature); } @Test void succeedsIfDeserializationThrowsWithTooFewBytes() { Bytes tooFewBytes = Bytes.wrap(new byte[95]); - assertThrows(IllegalArgumentException.class, () -> BLSSignature.fromBytes(tooFewBytes)); + assertThrows( + IllegalArgumentException.class, () -> BLSSignature.fromBytesCompressed(tooFewBytes)); } @Test - void succeedsWhenEqualsReturnsTrueForTheSameSignature() { - BLSSignature signature = BLSSignature.random(9); - assertEquals(signature, signature); + void succeedsIfSSZDeserializationThrowsWithTooFewBytes() { + Bytes tooFewBytes = Bytes.wrap(new byte[95]); + assertThrows(IllegalArgumentException.class, () -> BLSSignature.fromSSZBytes(tooFewBytes)); } @Test - void succeedsWhenEqualsReturnsTrueForIdenticalSignatures() { - BLSSignature signature = BLSSignature.random(17); - BLSSignature copyOfSignature = new BLSSignature(signature); - assertEquals(signature, copyOfSignature); + void succeedsWhenEqualsReturnsTrueForTheSameSignature() { + BLSSignature signature = BLSSignature.random(42); + assertEquals(signature, signature); + assertEquals(signature.hashCode(), signature.hashCode()); } @Test @@ -84,14 +85,28 @@ void succeedsWhenEqualsReturnsFalseForDifferentSignatures() { @Test void succeedsWhenRoundtripSSZReturnsTheSameSignature() { BLSSignature signature1 = BLSSignature.random(65); - BLSSignature signature2 = BLSSignature.fromBytes(signature1.toBytes()); + BLSSignature signature2 = BLSSignature.fromSSZBytes(signature1.toSSZBytes()); assertEquals(signature1, signature2); } @Test void succeedsWhenRoundtripSSZReturnsTheEmptySignature() { BLSSignature signature1 = BLSSignature.empty(); - BLSSignature signature2 = BLSSignature.fromBytes(signature1.toBytes()); + BLSSignature signature2 = BLSSignature.fromSSZBytes(signature1.toSSZBytes()); assertEquals(signature1, signature2); } + + @Test + void succeedsWhenEqualsReturnsTrueForEmptySignatures() { + assertEquals(BLSSignature.empty(), BLSSignature.empty()); + assertEquals(BLSSignature.empty().hashCode(), BLSSignature.empty().hashCode()); + } + + @Test + void roundtripEncodeDecodeCompressed() { + BLSSignature signature = BLSSignature.random(513); + final BLSSignature result = BLSSignature.fromBytesCompressed(signature.toBytesCompressed()); + assertEquals(signature, result); + assertEquals(signature.hashCode(), result.hashCode()); + } } diff --git a/bls/src/test/java/tech/pegasys/teku/bls/BLSTest.java b/bls/src/test/java/tech/pegasys/teku/bls/BLSTest.java index 8e234ce5183..50361800db1 100644 --- a/bls/src/test/java/tech/pegasys/teku/bls/BLSTest.java +++ b/bls/src/test/java/tech/pegasys/teku/bls/BLSTest.java @@ -14,6 +14,7 @@ package tech.pegasys.teku.bls; import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -21,10 +22,12 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.bytes.Bytes48; import org.junit.jupiter.api.Test; class BLSTest { @@ -61,7 +64,7 @@ void returnsTrueEvenIfBLSSignatureIsWrongWhenBLSVerificationIsDisabled() { @Test void succeedsWhenAggregatingASingleSignatureReturnsTheSameSignature() { BLSSignature signature = BLSSignature.random(1); - assertEquals(signature, BLS.aggregate(Collections.singletonList(signature))); + assertEquals(signature, BLS.aggregate(singletonList(signature))); } @Test @@ -94,6 +97,28 @@ void succeedsWhenCorrectlySigningAndVerifyingAggregateSignaturesReturnsTrue() { assertTrue(BLS.fastAggregateVerify(publicKeys, message, aggregatedSignature)); } + @Test + void fastAggregateVerify_verify4Signers() { + Bytes message = + Bytes.fromHexString("0x999bb85f3690c2ccb1607dd3e11a7e114038eb4044bdbdd340bc81aa3e5e0c9e"); + + List publicKeys = + Stream.of( + "0x97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "0xa572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e", + "0x89ece308f9d1f0131765212deca99697b112d61f9be9a5f1f3780a51335b3ff981747a0b2ca2179b96d2c0c9024e5224", + "0xac9b60d5afcbd5663a8a44b7c5a02f19e9a77ab0a35bd65809bb5c67ec582c897feb04decc694b13e08587f3ff9b5b60") + .map(pk -> BLSPublicKey.fromBytesCompressedValidate(Bytes48.fromHexString(pk))) + .collect(Collectors.toList()); + + BLSSignature aggregatedSignature = + BLSSignature.fromBytesCompressed( + Bytes.fromHexString( + "0xb2550663aa862b2741c9abc94f7b0b8a725b6f12b8f214d833e214e87c64235e4b1fb1e1ee64e5ae942cb3e0392699fc0524ae6f35072d1f243668de730be8745ab5be3314f90c107e246cefd1f1b97cd7241cfe97f4c80aeb354e8fac2ea720")); + + assertTrue(BLS.fastAggregateVerify(publicKeys, message, aggregatedSignature)); + } + @Test void succeedsWhenAggregateVerifyWithRepeatedMessagesReturnsFalse() { Bytes message1 = Bytes.wrap("Hello, world 1!".getBytes(UTF_8)); @@ -115,6 +140,28 @@ void succeedsWhenAggregateVerifyWithRepeatedMessagesReturnsFalse() { assertFalse(BLS.aggregateVerify(publicKeys, messages, aggregatedSignature)); } + @Test + void succeedsWhenAggregateVerifyWithDistinctMessagesReturnsTrue() { + Bytes message1 = Bytes.wrap("Hello, world 1!".getBytes(UTF_8)); + Bytes message2 = Bytes.wrap("Hello, world 2!".getBytes(UTF_8)); + Bytes message3 = Bytes.wrap("Hello, world 3!".getBytes(UTF_8)); + BLSKeyPair keyPair1 = BLSKeyPair.random(1); + BLSKeyPair keyPair2 = BLSKeyPair.random(2); + BLSKeyPair keyPair3 = BLSKeyPair.random(3); + + List publicKeys = + Arrays.asList(keyPair1.getPublicKey(), keyPair2.getPublicKey(), keyPair3.getPublicKey()); + List messages = Arrays.asList(message1, message2, message3); + List signatures = + Arrays.asList( + BLS.sign(keyPair1.getSecretKey(), message1), + BLS.sign(keyPair2.getSecretKey(), message2), + BLS.sign(keyPair3.getSecretKey(), message3)); + BLSSignature aggregatedSignature = BLS.aggregate(signatures); + + assertTrue(BLS.aggregateVerify(publicKeys, messages, aggregatedSignature)); + } + @Test // The standard says that this is INVALID void aggregateThrowsExceptionForEmptySignatureList() { @@ -135,13 +182,13 @@ void fastAggregateVerifyReturnsFalseForEmptyPubkeysList() { static final BLSPublicKey infinityG1 = BLSPublicKey.fromBytesCompressed( - Bytes.fromHexString( + Bytes48.fromHexString( "0x" + "c0000000000000000000000000000000" + "00000000000000000000000000000000" + "00000000000000000000000000000000")); static final BLSSignature infinityG2 = - BLSSignature.fromBytes( + BLSSignature.fromBytesCompressed( Bytes.fromHexString( "0x" + "c000000000000000000000000000000000000000000000000000000000000000" @@ -167,4 +214,85 @@ void succeedsWhenInfinitePublicKeyGivesInfiniteSignature() { Bytes message = Bytes.wrap("Hello, world!".getBytes(UTF_8)); assertEquals(infinityG2, BLS.sign(zeroSK, message)); } + + @Test + void aggregateInfinitePublicKeyAndSignature() { + BLSKeyPair keyPair1 = BLSKeyPair.random(1); + BLSKeyPair keyPairInf = new BLSKeyPair(zeroSK); + + Bytes message = Bytes.wrap("Hello, world!".getBytes(UTF_8)); + BLSSignature sig1 = BLS.sign(keyPair1.getSecretKey(), message); + BLSSignature sigInf = BLS.sign(keyPairInf.getSecretKey(), message); + + BLSPublicKey pubKeyAggr = + BLSPublicKey.aggregate(List.of(keyPair1.getPublicKey(), keyPairInf.getPublicKey())); + BLSSignature sigAggr = BLS.aggregate(List.of(sig1, sigInf)); + boolean res1 = BLS.verify(pubKeyAggr, message, sigAggr); + assertTrue(res1); + } + + @Test + void batchVerify2InfinitePublicKeyAndSignature() { + BLSKeyPair keyPairInf = new BLSKeyPair(zeroSK); + + Bytes message = Bytes.wrap("Hello, world!".getBytes(UTF_8)); + BLSSignature sigInf = BLS.sign(keyPairInf.getSecretKey(), message); + + BatchSemiAggregate semiAggregate1 = + BLS.prepareBatchVerify(0, List.of(keyPairInf.getPublicKey()), message, sigInf); + BatchSemiAggregate semiAggregateInf = + BLS.prepareBatchVerify(1, List.of(keyPairInf.getPublicKey()), message, sigInf); + + boolean res1 = BLS.completeBatchVerify(List.of(semiAggregate1, semiAggregateInf)); + assertTrue(res1); + } + + @Test + void batchVerifyInfinitePublicKeyAndSignature() { + BLSKeyPair keyPair1 = BLSKeyPair.random(1); + BLSKeyPair keyPairInf = new BLSKeyPair(zeroSK); + + Bytes message = Bytes.wrap("Hello, world!".getBytes(UTF_8)); + BLSSignature sig1 = BLS.sign(keyPair1.getSecretKey(), message); + BLSSignature sigInf = BLS.sign(keyPairInf.getSecretKey(), message); + + BatchSemiAggregate semiAggregate1 = + BLS.prepareBatchVerify(0, List.of(keyPair1.getPublicKey()), message, sig1); + BatchSemiAggregate semiAggregateInf = + BLS.prepareBatchVerify(1, List.of(keyPairInf.getPublicKey()), message, sigInf); + + boolean res1 = BLS.completeBatchVerify(List.of(semiAggregate1, semiAggregateInf)); + assertTrue(res1); + } + + @Test + void batchVerifyInvalidInfiniteSignature() { + BLSKeyPair keyPair1 = BLSKeyPair.random(1); + BLSKeyPair keyPairInf = new BLSKeyPair(zeroSK); + + Bytes message = Bytes.wrap("Hello, world!".getBytes(UTF_8)); + BLSSignature sigInf = BLS.sign(keyPairInf.getSecretKey(), message); + + BatchSemiAggregate semiAggregate = + BLS.prepareBatchVerify(0, List.of(keyPair1.getPublicKey()), message, sigInf); + + boolean res1 = BLS.completeBatchVerify(List.of(semiAggregate)); + assertFalse(res1); + } + + @Test + void testSignatureVerifyForSomeRealValues() { + String signingRoot = "0x95b8e2ba063ab62f68ebe7db0a9669ab9e7906aa4e060e1cc0b67b294ce8c5e4"; + String sig = + "0xab51f352e90509ca5085ec43af9ad3ea4ae42bf30c91af7dcdc113ef79cfc8601b756f18d8cf634436d8b6b0095fc5680066f382eb3728a7090c55c9afb66e8f94b44d2682db8ef5de4b89928d1744824df174e0c800b9e934b0ad14e6388163"; + String pk = + "0xb5e8f551c28abd6ef8253581ffad0834bfd8fafa9948d09b337c9c5f21d6e7fd6065a1ee35ac5146ac17344f97490301"; + + Bytes msg = Bytes.fromHexString(signingRoot); + BLSSignature signature = BLSSignature.fromBytesCompressed(Bytes.fromHexString(sig)); + BLSPublicKey publicKey = BLSPublicKey.fromBytesCompressed(Bytes48.fromHexString(pk)); + + boolean res = BLS.verify(publicKey, msg, signature); + assertTrue(res); + } } diff --git a/bls/src/test/java/tech/pegasys/teku/bls/impl/BLS12381Test.java b/bls/src/test/java/tech/pegasys/teku/bls/impl/BLS12381Test.java index 77c7bad4a5c..238f7d104af 100644 --- a/bls/src/test/java/tech/pegasys/teku/bls/impl/BLS12381Test.java +++ b/bls/src/test/java/tech/pegasys/teku/bls/impl/BLS12381Test.java @@ -116,28 +116,6 @@ void fastAggregateVerify() { assertTrue(aggregatedSignature.verify(publicKeys, message)); } - @Test - void aggregateVerifyDistinctMessages() { - Bytes message1 = Bytes.wrap("Hello, world 1!".getBytes(UTF_8)); - Bytes message2 = Bytes.wrap("Hello, world 2!".getBytes(UTF_8)); - Bytes message3 = Bytes.wrap("Hello, world 3!".getBytes(UTF_8)); - KeyPair keyPair1 = getBls().generateKeyPair(1); - KeyPair keyPair2 = getBls().generateKeyPair(2); - KeyPair keyPair3 = getBls().generateKeyPair(3); - - List publicKeys = - Arrays.asList(keyPair1.getPublicKey(), keyPair2.getPublicKey(), keyPair3.getPublicKey()); - List messages = Arrays.asList(message1, message2, message3); - List signatures = - Arrays.asList( - keyPair1.getSecretKey().sign(message1), - keyPair2.getSecretKey().sign(message2), - keyPair3.getSecretKey().sign(message3)); - Signature aggregatedSignature = getBls().aggregateSignatures(signatures); - - assertTrue(aggregatedSignature.verify(PublicKeyMessagePair.fromLists(publicKeys, messages))); - } - @Test void aggregateVerifyDuplicateMessages() { Bytes message1 = Bytes.wrap("Hello, world 1!".getBytes(UTF_8)); diff --git a/bls/src/test/java/tech/pegasys/teku/bls/impl/PublicKeyTest.java b/bls/src/test/java/tech/pegasys/teku/bls/impl/PublicKeyTest.java index b688d94b58e..a2740eb608f 100644 --- a/bls/src/test/java/tech/pegasys/teku/bls/impl/PublicKeyTest.java +++ b/bls/src/test/java/tech/pegasys/teku/bls/impl/PublicKeyTest.java @@ -17,7 +17,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Collections; -import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes48; import org.junit.jupiter.api.Test; public abstract class PublicKeyTest { @@ -34,7 +34,7 @@ public void shouldHaveConsistentHashCodeAndEquals() { final PublicKey key = getBls() .publicKeyFromCompressed( - Bytes.fromHexString( + Bytes48.fromHexString( "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e")); final PublicKey same = getBls().publicKeyFromCompressed(key.toBytesCompressed()); diff --git a/bls/src/test/java/tech/pegasys/teku/bls/impl/SignatureTest.java b/bls/src/test/java/tech/pegasys/teku/bls/impl/SignatureTest.java index 3c658592ae8..5117214d914 100644 --- a/bls/src/test/java/tech/pegasys/teku/bls/impl/SignatureTest.java +++ b/bls/src/test/java/tech/pegasys/teku/bls/impl/SignatureTest.java @@ -19,7 +19,6 @@ import java.util.Collections; import org.junit.jupiter.api.Test; -import tech.pegasys.teku.bls.BLSSignature; public abstract class SignatureTest { @@ -47,14 +46,6 @@ void succeedsWhenEqualsReturnsFalseForDifferentSignatures() { assertNotEquals(signature1, signature2); } - @Test - void succeedsWhenEqualsReturnsTrueForEmptySignatures() { - assertEquals(BLSSignature.empty().getSignature(), BLSSignature.empty().getSignature()); - assertEquals( - BLSSignature.empty().getSignature().hashCode(), - BLSSignature.empty().getSignature().hashCode()); - } - @Test void succeedsWhenPassingEmptyListToAggregateSignaturesDoesNotThrowException() { assertDoesNotThrow(() -> getBls().aggregateSignatures(Collections.emptyList())); diff --git a/bls/src/test/java/tech/pegasys/teku/bls/impl/blst/BlstPublicKeyTest.java b/bls/src/test/java/tech/pegasys/teku/bls/impl/blst/BlstPublicKeyTest.java new file mode 100644 index 00000000000..96967de018a --- /dev/null +++ b/bls/src/test/java/tech/pegasys/teku/bls/impl/blst/BlstPublicKeyTest.java @@ -0,0 +1,60 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * 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.bls.impl.blst; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes48; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +public class BlstPublicKeyTest { + + @BeforeAll + static void setup() { + assertThat(BlstBLS12381.INSTANCE).isNotEmpty(); + } + + // Blst library doesn't handle infinity pubkeys at the moment. + // Could enable the test when the issue https://github.com/supranational/blst/issues/11 is + // addressed + @Disabled + @Test + void infinityPublicKey() { + BlstPublicKey inf1 = + BlstPublicKey.fromBytes( + Bytes48.fromHexString( + "0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")); + Bytes bytes = inf1.toBytesUncompressed(); + + BlstPublicKey publicKey = BlstPublicKey.fromBytesUncompressed(bytes); + publicKey.forceValidation(); + } + + @Test + void succeedsWhenInvalidPublicKeyIsInvalid() { + Bytes48 invalidPublicKeyBytes = + Bytes48.fromHexString( + "0x9378a6e3984e96d2cd50450c76ca14732f1300efa04aecdb805b22e6d6926a85ef409e8f3acf494a1481090bf32ce3bd"); + assertThatThrownBy( + () -> { + BlstPublicKey publicKey = BlstPublicKey.fromBytes(invalidPublicKeyBytes); + publicKey.forceValidation(); + }) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/bls/src/test/java/tech/pegasys/teku/bls/impl/blst/BlstTest.java b/bls/src/test/java/tech/pegasys/teku/bls/impl/blst/BlstTest.java new file mode 100644 index 00000000000..b07f6c2df32 --- /dev/null +++ b/bls/src/test/java/tech/pegasys/teku/bls/impl/blst/BlstTest.java @@ -0,0 +1,74 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * 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.bls.impl.blst; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import java.util.Random; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import tech.pegasys.teku.bls.BatchSemiAggregate; + +public class BlstTest { + private static final Random random = new Random(1); + + private static BlstBLS12381 BLS; + + @BeforeAll + static void setup() { + BLS = BlstBLS12381.INSTANCE.orElseThrow(); + } + + @Test + void testBatchVerifySingleSig() { + Bytes msg = Bytes32.ZERO; + + BlstSecretKey blstSK = BlstSecretKey.generateNew(random); + BlstPublicKey blstPK = blstSK.derivePublicKey(); + + BlstSignature blstSignature = BlstBLS12381.sign(blstSK, msg); + + BatchSemiAggregate semiAggregate = + BLS.prepareBatchVerify(0, List.of(blstPK), msg, blstSignature); + + boolean blstRes = BLS.completeBatchVerify(List.of(semiAggregate)); + assertThat(blstRes).isTrue(); + } + + @Test + void testBatchVerifyCoupleSigs() { + Bytes msg1 = Bytes32.fromHexString("123456"); + + BlstSecretKey blstSK1 = BlstSecretKey.generateNew(random); + BlstPublicKey blstPK1 = blstSK1.derivePublicKey(); + BlstSignature blstSignature1 = BlstBLS12381.sign(blstSK1, msg1); + + Bytes msg2 = Bytes32.fromHexString("654321"); + + BlstSecretKey blstSK2 = BlstSecretKey.generateNew(random); + BlstPublicKey blstPK2 = blstSK2.derivePublicKey(); + BlstSignature blstSignature2 = BlstBLS12381.sign(blstSK2, msg2); + + BatchSemiAggregate semiAggregate1 = + BLS.prepareBatchVerify(0, List.of(blstPK1), msg1, blstSignature1); + BatchSemiAggregate semiAggregate2 = + BLS.prepareBatchVerify(1, List.of(blstPK2), msg2, blstSignature2); + + boolean blstRes = BLS.completeBatchVerify(List.of(semiAggregate1, semiAggregate2)); + assertThat(blstRes).isTrue(); + } +} diff --git a/bls/src/test/java/tech/pegasys/teku/bls/impl/mikuli/MikuliPublicKeyTest.java b/bls/src/test/java/tech/pegasys/teku/bls/impl/mikuli/MikuliPublicKeyTest.java index cdca30c44f0..788c5726c23 100644 --- a/bls/src/test/java/tech/pegasys/teku/bls/impl/mikuli/MikuliPublicKeyTest.java +++ b/bls/src/test/java/tech/pegasys/teku/bls/impl/mikuli/MikuliPublicKeyTest.java @@ -13,14 +13,15 @@ package tech.pegasys.teku.bls.impl.mikuli; +import static org.assertj.core.api.Assertions.assertThatCode; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.milagro.amcl.BLS381.BIG; import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes48; import org.apache.tuweni.ssz.SSZ; import org.junit.jupiter.api.Test; -import tech.pegasys.teku.bls.BLSPublicKey; import tech.pegasys.teku.bls.impl.BLS12381; import tech.pegasys.teku.bls.impl.PublicKeyTest; @@ -47,7 +48,7 @@ void succeedsWhenTwoInfinityPublicKeysAreEqual() { @Test void succeedsIfDeserializationOfInfinityPublicKeyIsCorrect() { - BLSPublicKey infinityPublicKey = new BLSPublicKey(new MikuliPublicKey(new G1Point())); + MikuliPublicKey infinityPublicKey = new MikuliPublicKey(new G1Point()); byte[] pointBytes = new byte[48]; pointBytes[0] = (byte) 0xc0; Bytes infinityBytesSsz = @@ -55,14 +56,28 @@ void succeedsIfDeserializationOfInfinityPublicKeyIsCorrect() { writer -> { writer.writeFixedBytes(Bytes.wrap(pointBytes)); }); - BLSPublicKey deserializedPublicKey = BLSPublicKey.fromBytes(infinityBytesSsz); + MikuliPublicKey deserializedPublicKey = MikuliPublicKey.fromBytesCompressed(infinityBytesSsz); assertEquals(infinityPublicKey, deserializedPublicKey); } @Test void succeedsWhenRoundtripSSZReturnsTheInfinityPublicKey() { - BLSPublicKey publicKey1 = new BLSPublicKey(new MikuliPublicKey(new G1Point())); - BLSPublicKey publicKey2 = BLSPublicKey.fromBytes(publicKey1.toBytes()); + MikuliPublicKey publicKey1 = new MikuliPublicKey(new G1Point()); + MikuliPublicKey publicKey2 = + MikuliPublicKey.fromBytesCompressed(publicKey1.toBytesCompressed()); assertEquals(publicKey1, publicKey2); } + + @Test + void infinityPublicKeyIsValid() { + MikuliPublicKey infinityG1 = + MikuliPublicKey.fromBytesCompressed( + Bytes48.fromHexString( + "0x" + + "c0000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000")); + + assertThatCode(infinityG1::forceValidation).doesNotThrowAnyException(); + } } diff --git a/bls/src/test/java/tech/pegasys/teku/bls/impl/mikuli/MikuliSecretKeyTest.java b/bls/src/test/java/tech/pegasys/teku/bls/impl/mikuli/MikuliSecretKeyTest.java deleted file mode 100644 index 3741e43f249..00000000000 --- a/bls/src/test/java/tech/pegasys/teku/bls/impl/mikuli/MikuliSecretKeyTest.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2020 ConsenSys AG. - * - * 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.bls.impl.mikuli; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.apache.tuweni.bytes.Bytes32; -import org.apache.tuweni.bytes.Bytes48; -import org.junit.jupiter.api.Test; -import tech.pegasys.teku.bls.BLSSecretKey; - -class MikuliSecretKeyTest { - private static final Bytes32 PRIVATE_KEY_32_BYTES = - Bytes32.fromHexString("0x2CF622DE0FD92C7D4E59539CBDA63100E02CF59349595356CD97FFE6CB486460"); - - @Test - void keyCanBeCreatedWith32ByteValue() { - assertThat(PRIVATE_KEY_32_BYTES.size()).isEqualTo(32); - - final BLSSecretKey secretKey = BLSSecretKey.fromBytes(PRIVATE_KEY_32_BYTES); - // mikuli always represents the key as 48 bytes so compare against the key left padded with 0s - assertThat(secretKey.getSecretKey().toBytes()).isEqualTo(Bytes48.leftPad(PRIVATE_KEY_32_BYTES)); - } -} diff --git a/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/beacon/PostDutiesIntegrationTest.java b/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/beacon/PostDutiesIntegrationTest.java index bc4df0ed7e8..af7e4dadcaa 100644 --- a/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/beacon/PostDutiesIntegrationTest.java +++ b/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/beacon/PostDutiesIntegrationTest.java @@ -71,7 +71,7 @@ public void shouldReturnEmptyListWhenNoPubKeysSupplied() throws Exception { private Response post(final int epoch, final List publicKeys) throws IOException { final List publicKeyStrings = publicKeys.stream() - .map(k -> k.getPublicKey().toBytes().toHexString()) + .map(k -> k.getPublicKey().toSSZBytes().toHexString()) .collect(Collectors.toList()); final Map params = diff --git a/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/beacon/PostValidatorsIntegrationTest.java b/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/beacon/PostValidatorsIntegrationTest.java index 3668c11df14..a97bf9c0a07 100644 --- a/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/beacon/PostValidatorsIntegrationTest.java +++ b/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/beacon/PostValidatorsIntegrationTest.java @@ -80,7 +80,7 @@ public void shouldReturnEmptyListIfWhenPubKeysIsEmpty() throws Exception { private Response post(final int epoch, final List publicKeys) throws IOException { final List publicKeyStrings = publicKeys.stream() - .map(k -> k.getPublicKey().toBytes().toHexString()) + .map(k -> k.getPublicKey().toSSZBytes().toHexString()) .collect(Collectors.toList()); final Map params = diff --git a/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/beacon/PostValidatorsWithDataIntegrationTest.java b/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/beacon/PostValidatorsWithDataIntegrationTest.java index 1e98ddf3985..83e29fe2707 100644 --- a/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/beacon/PostValidatorsWithDataIntegrationTest.java +++ b/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/beacon/PostValidatorsWithDataIntegrationTest.java @@ -70,7 +70,7 @@ private Response post(final Optional epoch, final List publ throws IOException { final List publicKeyStrings = publicKeys.stream() - .map(k -> k.getPublicKey().toBytes().toHexString()) + .map(k -> k.getPublicKey().toSSZBytes().toHexString()) .collect(Collectors.toList()); final Map params = diff --git a/data/beaconrestapi/src/test/java/tech/pegasys/teku/beaconrestapi/handlers/validator/PostDutiesTest.java b/data/beaconrestapi/src/test/java/tech/pegasys/teku/beaconrestapi/handlers/validator/PostDutiesTest.java index cefde8bea4e..5eefa79c39f 100644 --- a/data/beaconrestapi/src/test/java/tech/pegasys/teku/beaconrestapi/handlers/validator/PostDutiesTest.java +++ b/data/beaconrestapi/src/test/java/tech/pegasys/teku/beaconrestapi/handlers/validator/PostDutiesTest.java @@ -45,7 +45,7 @@ public class PostDutiesTest { private static final List pubKeys = keyPairs.stream() .map(BLSKeyPair::getPublicKey) - .map(BLSPublicKey::toBytes) + .map(BLSPublicKey::toSSZBytes) .map(Bytes::toHexString) .collect(Collectors.toList()); private Context context = mock(Context.class); diff --git a/data/provider/src/main/java/tech/pegasys/teku/api/ValidatorDataProvider.java b/data/provider/src/main/java/tech/pegasys/teku/api/ValidatorDataProvider.java index d840a0ec755..dd1afdf7856 100644 --- a/data/provider/src/main/java/tech/pegasys/teku/api/ValidatorDataProvider.java +++ b/data/provider/src/main/java/tech/pegasys/teku/api/ValidatorDataProvider.java @@ -87,7 +87,9 @@ public SafeFuture> getUnsignedBeaconBlockAtSlot( return validatorApiChannel .createUnsignedBlock( - slot, tech.pegasys.teku.bls.BLSSignature.fromBytes(randao.getBytes()), graffiti) + slot, + tech.pegasys.teku.bls.BLSSignature.fromBytesCompressed(randao.getBytes()), + graffiti) .thenApply(maybeBlock -> maybeBlock.map(BeaconBlock::new)); } @@ -123,7 +125,7 @@ public SafeFuture>> getValidatorDutiesByRequest( () -> { final List publicKeys = validatorDutiesRequest.pubkeys.stream() - .map(key -> BLSPublicKey.fromBytes(key.toBytes())) + .map(key -> BLSPublicKey.fromSSZBytes(key.toBytes())) .collect(toList()); return validatorApiChannel.getDuties(validatorDutiesRequest.epoch, publicKeys); }) @@ -151,7 +153,7 @@ private ValidatorDuties mapToSchemaDuties( public void submitAttestation(Attestation attestation) { // TODO (#2410): extra validation for the attestation we're posting? - if (attestation.signature.asInternalBLSSignature().toBytes().isZero()) { + if (attestation.signature.asInternalBLSSignature().toSSZBytes().isZero()) { throw new IllegalArgumentException("Signed attestations must have a non zero signature"); } validatorApiChannel.sendSignedAttestation(attestation.asInternalAttestation()); diff --git a/data/provider/src/test/java/tech/pegasys/teku/api/ValidatorDataProviderTest.java b/data/provider/src/test/java/tech/pegasys/teku/api/ValidatorDataProviderTest.java index a00264969b0..7b7813bba63 100644 --- a/data/provider/src/test/java/tech/pegasys/teku/api/ValidatorDataProviderTest.java +++ b/data/provider/src/test/java/tech/pegasys/teku/api/ValidatorDataProviderTest.java @@ -160,7 +160,7 @@ void getUnsignedAttestationAtSlot_shouldReturnAttestation() throws Exception { Attestation attestation = result.join().orElseThrow(); assertThat(attestation.data.index).isEqualTo(internalAttestation.getData().getIndex()); assertThat(attestation.signature.toHexString()) - .isEqualTo(internalAttestation.getAggregate_signature().toBytes().toHexString()); + .isEqualTo(internalAttestation.getAggregate_signature().toSSZBytes().toHexString()); assertThat(attestation.data.slot).isEqualTo(internalAttestation.getData().getSlot()); assertThat(attestation.data.beacon_block_root) .isEqualTo(internalAttestation.getData().getBeacon_block_root()); diff --git a/data/serializer/src/main/java/tech/pegasys/teku/api/schema/BLSPubKey.java b/data/serializer/src/main/java/tech/pegasys/teku/api/schema/BLSPubKey.java index 5f0ddd4c971..ac18c47cd55 100644 --- a/data/serializer/src/main/java/tech/pegasys/teku/api/schema/BLSPubKey.java +++ b/data/serializer/src/main/java/tech/pegasys/teku/api/schema/BLSPubKey.java @@ -17,6 +17,7 @@ import java.util.Objects; import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes48; import tech.pegasys.teku.bls.BLSPublicKey; public class BLSPubKey { @@ -36,7 +37,7 @@ public BLSPubKey(Bytes bytes) { } public BLSPubKey(BLSPublicKey publicKey) { - this(publicKey.toBytes()); + this(publicKey.toSSZBytes()); } @Override @@ -62,11 +63,10 @@ public String toString() { } public static BLSPubKey fromHexString(String value) { - BLSPublicKey blsPublicKey = BLSPublicKey.fromBytes(Bytes.fromHexString(value)); - if (blsPublicKey.isValid()) { - return new BLSPubKey(blsPublicKey); - } else { - throw new PublicKeyException("Public key is invalid."); + try { + return new BLSPubKey(BLSPublicKey.fromBytesCompressedValidate(Bytes48.fromHexString(value))); + } catch (IllegalArgumentException e) { + throw new PublicKeyException("Public key is invalid.", e); } } @@ -83,6 +83,6 @@ public static BLSPubKey empty() { } public BLSPublicKey asBLSPublicKey() { - return BLSPublicKey.fromBytes(bytes); + return BLSPublicKey.fromSSZBytes(bytes); } } diff --git a/data/serializer/src/main/java/tech/pegasys/teku/api/schema/BLSSignature.java b/data/serializer/src/main/java/tech/pegasys/teku/api/schema/BLSSignature.java index 00249353554..f629d78faeb 100644 --- a/data/serializer/src/main/java/tech/pegasys/teku/api/schema/BLSSignature.java +++ b/data/serializer/src/main/java/tech/pegasys/teku/api/schema/BLSSignature.java @@ -17,10 +17,11 @@ import java.util.Objects; import org.apache.tuweni.bytes.Bytes; +import tech.pegasys.teku.bls.BLSConstants; public class BLSSignature { /** The number of bytes in this value - i.e. 96 */ - private static final int SIZE = 96; + private static final int SIZE = BLSConstants.BLS_SIGNATURE_SIZE; private final Bytes bytes; @@ -35,7 +36,7 @@ public BLSSignature(Bytes bytes) { } public BLSSignature(tech.pegasys.teku.bls.BLSSignature signature) { - this(signature.toBytes()); + this(signature.toBytesCompressed()); } @Override @@ -69,7 +70,7 @@ public String toHexString() { } public static BLSSignature empty() { - return new BLSSignature(Bytes.wrap(new byte[SIZE])); + return new BLSSignature(tech.pegasys.teku.bls.BLSSignature.empty()); } public final Bytes getBytes() { @@ -77,6 +78,6 @@ public final Bytes getBytes() { } public tech.pegasys.teku.bls.BLSSignature asInternalBLSSignature() { - return tech.pegasys.teku.bls.BLSSignature.fromBytes(bytes); + return tech.pegasys.teku.bls.BLSSignature.fromBytesCompressed(bytes); } } diff --git a/data/serializer/src/main/java/tech/pegasys/teku/api/schema/BeaconBlockBody.java b/data/serializer/src/main/java/tech/pegasys/teku/api/schema/BeaconBlockBody.java index 1855cfc8520..0857541cfca 100644 --- a/data/serializer/src/main/java/tech/pegasys/teku/api/schema/BeaconBlockBody.java +++ b/data/serializer/src/main/java/tech/pegasys/teku/api/schema/BeaconBlockBody.java @@ -65,7 +65,7 @@ public BeaconBlockBody( } public BeaconBlockBody(tech.pegasys.teku.datastructures.blocks.BeaconBlockBody body) { - this.randao_reveal = new BLSSignature(body.getRandao_reveal().toBytes()); + this.randao_reveal = new BLSSignature(body.getRandao_reveal().toSSZBytes()); this.eth1_data = new Eth1Data(body.getEth1_data()); this.graffiti = body.getGraffiti(); this.proposer_slashings = diff --git a/data/serializer/src/main/java/tech/pegasys/teku/api/schema/DepositData.java b/data/serializer/src/main/java/tech/pegasys/teku/api/schema/DepositData.java index 9b506cde2db..af1c70df53c 100644 --- a/data/serializer/src/main/java/tech/pegasys/teku/api/schema/DepositData.java +++ b/data/serializer/src/main/java/tech/pegasys/teku/api/schema/DepositData.java @@ -37,7 +37,7 @@ public class DepositData { public final BLSSignature signature; public DepositData(tech.pegasys.teku.datastructures.operations.DepositData depositData) { - this.pubkey = new BLSPubKey(depositData.getPubkey().toBytes()); + this.pubkey = new BLSPubKey(depositData.getPubkey().toSSZBytes()); this.withdrawal_credentials = depositData.getWithdrawal_credentials(); this.amount = depositData.getAmount(); this.signature = new BLSSignature(depositData.getSignature()); @@ -56,7 +56,7 @@ public DepositData( public tech.pegasys.teku.datastructures.operations.DepositData asInternalDepositData() { return new tech.pegasys.teku.datastructures.operations.DepositData( - BLSPublicKey.fromBytes(pubkey.toBytes()), + BLSPublicKey.fromSSZBytes(pubkey.toBytes()), withdrawal_credentials, amount, signature.asInternalBLSSignature()); diff --git a/data/serializer/src/main/java/tech/pegasys/teku/api/schema/Validator.java b/data/serializer/src/main/java/tech/pegasys/teku/api/schema/Validator.java index b0ddf4bb442..f40ebca9f75 100644 --- a/data/serializer/src/main/java/tech/pegasys/teku/api/schema/Validator.java +++ b/data/serializer/src/main/java/tech/pegasys/teku/api/schema/Validator.java @@ -67,7 +67,7 @@ public Validator( } public Validator(final tech.pegasys.teku.datastructures.state.Validator validator) { - this.pubkey = new BLSPubKey(validator.getPubkey().toBytes()); + this.pubkey = new BLSPubKey(validator.getPubkey().toSSZBytes()); this.withdrawal_credentials = validator.getWithdrawal_credentials(); this.effective_balance = validator.getEffective_balance(); this.slashed = validator.isSlashed(); diff --git a/eth-benchmark-tests/src/jmh/java/tech/pegasys/teku/benchmarks/gen/BlsKeyPairIO.java b/eth-benchmark-tests/src/jmh/java/tech/pegasys/teku/benchmarks/gen/BlsKeyPairIO.java index 691cffe2da9..2c4f115123c 100644 --- a/eth-benchmark-tests/src/jmh/java/tech/pegasys/teku/benchmarks/gen/BlsKeyPairIO.java +++ b/eth-benchmark-tests/src/jmh/java/tech/pegasys/teku/benchmarks/gen/BlsKeyPairIO.java @@ -29,8 +29,8 @@ import java.util.stream.Collectors; import java.util.stream.StreamSupport; import java.util.zip.GZIPInputStream; -import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.bytes.Bytes48; import org.jetbrains.annotations.NotNull; import tech.pegasys.teku.bls.BLSKeyPair; import tech.pegasys.teku.bls.BLSPublicKey; @@ -103,7 +103,7 @@ public BLSKeyPair get() { BLSPublicKey blsPublicKey = BLSPublicKey.fromBytesCompressed( - Bytes.fromHexString(parts.substring(delimiterPos + 1))); + Bytes48.fromHexString(parts.substring(delimiterPos + 1))); Bytes32 privateBytes = Bytes32.fromHexString(parts.substring(0, delimiterPos)); BLSSecretKey blsSecretKey = BLSSecretKey.fromBytes(privateBytes); @@ -128,9 +128,9 @@ public void write(int count) throws IOException { for (int i = 0; i < count; i++) { BLSKeyPair blsKeyPair = generator.get(); writer - .append(blsKeyPair.getSecretKey().getSecretKey().toBytes().toHexString()) + .append(blsKeyPair.getSecretKey().toBytes().toHexString()) .append(':') - .append(blsKeyPair.getPublicKey().getPublicKey().toBytesCompressed().toHexString()) + .append(blsKeyPair.getPublicKey().toBytesCompressed().toHexString()) .append('\n'); } } diff --git a/eth-benchmark-tests/src/jmh/resources/bls-key-pairs/bls-key-pairs-200k-seed-0.txt.gz b/eth-benchmark-tests/src/jmh/resources/bls-key-pairs/bls-key-pairs-200k-seed-0.txt.gz index 37facf09522..c0d8816a76c 100644 Binary files a/eth-benchmark-tests/src/jmh/resources/bls-key-pairs/bls-key-pairs-200k-seed-0.txt.gz and b/eth-benchmark-tests/src/jmh/resources/bls-key-pairs/bls-key-pairs-200k-seed-0.txt.gz differ diff --git a/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/bls/BlsAggregateVerifyTestExecutor.java b/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/bls/BlsAggregateVerifyTestExecutor.java index 31e32d59800..acfc9b56409 100644 --- a/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/bls/BlsAggregateVerifyTestExecutor.java +++ b/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/bls/BlsAggregateVerifyTestExecutor.java @@ -65,7 +65,7 @@ private static class Input { public List getPublicKeys() { return publicKeys.stream() .map(Bytes::fromHexString) - .map(BLSPublicKey::fromBytes) + .map(BLSPublicKey::fromSSZBytes) .collect(toList()); } diff --git a/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/bls/BlsFastAggregateVerifyTestExecutor.java b/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/bls/BlsFastAggregateVerifyTestExecutor.java index 63e7cc2d684..27d928c4ccc 100644 --- a/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/bls/BlsFastAggregateVerifyTestExecutor.java +++ b/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/bls/BlsFastAggregateVerifyTestExecutor.java @@ -64,7 +64,7 @@ private static class Input { public List getPublicKeys() { return publicKeys.stream() .map(Bytes::fromHexString) - .map(BLSPublicKey::fromBytes) + .map(BLSPublicKey::fromSSZBytes) .collect(toList()); } diff --git a/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/bls/BlsTests.java b/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/bls/BlsTests.java index 2924ec011f8..f74b1512fbc 100644 --- a/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/bls/BlsTests.java +++ b/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/bls/BlsTests.java @@ -30,6 +30,6 @@ public class BlsTests { .build(); public static BLSSignature parseSignature(final String value) { - return BLSSignature.fromBytes(Bytes.fromHexStringLenient(value, 96)); + return BLSSignature.fromSSZBytes(Bytes.fromHexStringLenient(value, 96)); } } diff --git a/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/bls/BlsVerifyTestExecutor.java b/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/bls/BlsVerifyTestExecutor.java index 3efd29cd68d..ae4b139a68c 100644 --- a/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/bls/BlsVerifyTestExecutor.java +++ b/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/bls/BlsVerifyTestExecutor.java @@ -61,7 +61,7 @@ private static class Input { private String signature; public BLSPublicKey getPublicKey() { - return BLSPublicKey.fromBytes(Bytes.fromHexString(publicKey)); + return BLSPublicKey.fromSSZBytes(Bytes.fromHexString(publicKey)); } public Bytes getMessage() { diff --git a/eth-tests/src/main/java/tech/pegasys/teku/ethtests/finder/TestDefinition.java b/eth-tests/src/main/java/tech/pegasys/teku/ethtests/finder/TestDefinition.java index 7f1edbeec5a..17c3e51f14d 100644 --- a/eth-tests/src/main/java/tech/pegasys/teku/ethtests/finder/TestDefinition.java +++ b/eth-tests/src/main/java/tech/pegasys/teku/ethtests/finder/TestDefinition.java @@ -27,8 +27,8 @@ public TestDefinition( final String testName, final Path pathFromPhaseTestDir) { this.spec = spec; - this.testType = testType; - this.testName = testName; + this.testType = testType.replace("\\", "/"); + this.testName = testName.replace("\\", "/"); this.pathFromPhaseTestDir = pathFromPhaseTestDir; } diff --git a/ethereum/core/src/main/java/tech/pegasys/teku/core/BlockProcessorUtil.java b/ethereum/core/src/main/java/tech/pegasys/teku/core/BlockProcessorUtil.java index b44e988871c..cd346e6502d 100644 --- a/ethereum/core/src/main/java/tech/pegasys/teku/core/BlockProcessorUtil.java +++ b/ethereum/core/src/main/java/tech/pegasys/teku/core/BlockProcessorUtil.java @@ -124,7 +124,7 @@ public static void process_randao_no_validation(MutableBeaconState state, Beacon UnsignedLong epoch = get_current_epoch(state); Bytes32 mix = - get_randao_mix(state, epoch).xor(Hash.sha2_256(body.getRandao_reveal().toBytes())); + get_randao_mix(state, epoch).xor(Hash.sha2_256(body.getRandao_reveal().toSSZBytes())); int index = epoch.mod(UnsignedLong.valueOf(EPOCHS_PER_HISTORICAL_VECTOR)).intValue(); state.getRandao_mixes().set(index, mix); } catch (IllegalArgumentException e) { diff --git a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/blocks/BeaconBlockBody.java b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/blocks/BeaconBlockBody.java index 84940777cc4..183d8434b9c 100644 --- a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/blocks/BeaconBlockBody.java +++ b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/blocks/BeaconBlockBody.java @@ -188,7 +188,7 @@ public SSZList getVoluntary_exits() { public Bytes32 hash_tree_root() { return HashTreeUtil.merkleize( Arrays.asList( - HashTreeUtil.hash_tree_root(SSZTypes.VECTOR_OF_BASIC, randao_reveal.toBytes()), + HashTreeUtil.hash_tree_root(SSZTypes.VECTOR_OF_BASIC, randao_reveal.toSSZBytes()), eth1_data.hash_tree_root(), HashTreeUtil.hash_tree_root(SSZTypes.VECTOR_OF_BASIC, graffiti), HashTreeUtil.hash_tree_root(SSZTypes.LIST_OF_COMPOSITE, proposer_slashings), diff --git a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/blocks/SignedBeaconBlock.java b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/blocks/SignedBeaconBlock.java index 276e8d63e84..6ddb32f936e 100644 --- a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/blocks/SignedBeaconBlock.java +++ b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/blocks/SignedBeaconBlock.java @@ -120,6 +120,6 @@ public Bytes32 hash_tree_root() { return HashTreeUtil.merkleize( List.of( message.hash_tree_root(), - HashTreeUtil.hash_tree_root(SSZTypes.VECTOR_OF_BASIC, signature.toBytes()))); + HashTreeUtil.hash_tree_root(SSZTypes.VECTOR_OF_BASIC, signature.toSSZBytes()))); } } diff --git a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/blocks/SignedBeaconBlockHeader.java b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/blocks/SignedBeaconBlockHeader.java index f4d71858929..3f5c81823f9 100644 --- a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/blocks/SignedBeaconBlockHeader.java +++ b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/blocks/SignedBeaconBlockHeader.java @@ -79,6 +79,6 @@ public Bytes32 hash_tree_root() { return HashTreeUtil.merkleize( Arrays.asList( message.hash_tree_root(), - HashTreeUtil.hash_tree_root(SSZTypes.VECTOR_OF_BASIC, signature.toBytes()))); + HashTreeUtil.hash_tree_root(SSZTypes.VECTOR_OF_BASIC, signature.toSSZBytes()))); } } diff --git a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/AggregateAndProof.java b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/AggregateAndProof.java index d09cb8fbd61..8c3f4587b4a 100644 --- a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/AggregateAndProof.java +++ b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/AggregateAndProof.java @@ -128,6 +128,6 @@ public Bytes32 hash_tree_root() { List.of( HashTreeUtil.hash_tree_root(SSZTypes.BASIC, SSZ.encodeUInt64(index.longValue())), aggregate.hash_tree_root(), - HashTreeUtil.hash_tree_root(SSZTypes.VECTOR_OF_BASIC, selection_proof.toBytes()))); + HashTreeUtil.hash_tree_root(SSZTypes.VECTOR_OF_BASIC, selection_proof.toSSZBytes()))); } } diff --git a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/DepositData.java b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/DepositData.java index 038a167719a..c2b6958d52a 100644 --- a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/DepositData.java +++ b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/DepositData.java @@ -133,9 +133,9 @@ public void setSignature(BLSSignature signature) { public Bytes32 hash_tree_root() { return HashTreeUtil.merkleize( Arrays.asList( - HashTreeUtil.hash_tree_root(SSZTypes.VECTOR_OF_BASIC, pubkey.toBytes()), + HashTreeUtil.hash_tree_root(SSZTypes.VECTOR_OF_BASIC, pubkey.toSSZBytes()), HashTreeUtil.hash_tree_root(SSZTypes.VECTOR_OF_BASIC, withdrawal_credentials), HashTreeUtil.hash_tree_root(SSZTypes.BASIC, SSZ.encodeUInt64(amount.longValue())), - HashTreeUtil.hash_tree_root(SSZTypes.VECTOR_OF_BASIC, signature.toBytes()))); + HashTreeUtil.hash_tree_root(SSZTypes.VECTOR_OF_BASIC, signature.toSSZBytes()))); } } diff --git a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/DepositMessage.java b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/DepositMessage.java index 6a7b454a5f8..0fb5fc711ce 100644 --- a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/DepositMessage.java +++ b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/DepositMessage.java @@ -95,7 +95,7 @@ public int hashCode() { public Bytes32 hash_tree_root() { return HashTreeUtil.merkleize( Arrays.asList( - HashTreeUtil.hash_tree_root(SSZTypes.VECTOR_OF_BASIC, pubkey.toBytes()), + HashTreeUtil.hash_tree_root(SSZTypes.VECTOR_OF_BASIC, pubkey.toSSZBytes()), HashTreeUtil.hash_tree_root(SSZTypes.VECTOR_OF_BASIC, withdrawal_credentials), HashTreeUtil.hash_tree_root(SSZTypes.BASIC, SSZ.encodeUInt64(amount.longValue())))); } diff --git a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/IndexedAttestation.java b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/IndexedAttestation.java index 911d077c288..34a2faab77c 100644 --- a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/IndexedAttestation.java +++ b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/IndexedAttestation.java @@ -60,7 +60,7 @@ public IndexedAttestation() { public IndexedAttestation(IndexedAttestation indexedAttestation) { this.attesting_indices = SSZList.createMutable(indexedAttestation.getAttesting_indices()); this.data = indexedAttestation.getData(); - this.signature = new BLSSignature(indexedAttestation.getSignature()); + this.signature = indexedAttestation.getSignature(); } @Override @@ -137,6 +137,6 @@ public Bytes32 hash_tree_root() { HashTreeUtil.hash_tree_root_list_ul( attesting_indices.map(Bytes.class, item -> SSZ.encodeUInt64(item.longValue()))), data.hash_tree_root(), - HashTreeUtil.hash_tree_root(SSZTypes.VECTOR_OF_BASIC, signature.toBytes()))); + HashTreeUtil.hash_tree_root(SSZTypes.VECTOR_OF_BASIC, signature.toSSZBytes()))); } } diff --git a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/SignedAggregateAndProof.java b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/SignedAggregateAndProof.java index 6a5836d915f..8d828935a10 100644 --- a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/SignedAggregateAndProof.java +++ b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/SignedAggregateAndProof.java @@ -93,6 +93,6 @@ public Bytes32 hash_tree_root() { return HashTreeUtil.merkleize( List.of( message.hash_tree_root(), - HashTreeUtil.hash_tree_root(SSZTypes.VECTOR_OF_BASIC, signature.toBytes()))); + HashTreeUtil.hash_tree_root(SSZTypes.VECTOR_OF_BASIC, signature.toSSZBytes()))); } } diff --git a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/SignedVoluntaryExit.java b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/SignedVoluntaryExit.java index f1adc120131..c33c4e01d29 100644 --- a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/SignedVoluntaryExit.java +++ b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/operations/SignedVoluntaryExit.java @@ -62,7 +62,7 @@ public Bytes32 hash_tree_root() { return HashTreeUtil.merkleize( Arrays.asList( message.hash_tree_root(), - HashTreeUtil.hash_tree_root(SSZTypes.VECTOR_OF_BASIC, signature.toBytes()))); + HashTreeUtil.hash_tree_root(SSZTypes.VECTOR_OF_BASIC, signature.toSSZBytes()))); } @Override diff --git a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/state/Validator.java b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/state/Validator.java index 12f2aa150c4..77d07e03aa1 100644 --- a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/state/Validator.java +++ b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/state/Validator.java @@ -101,7 +101,7 @@ public Validator( UnsignedLong withdrawable_epoch) { super( TYPE, - ViewUtils.createVectorFromBytes(pubkey.toBytes()), + ViewUtils.createVectorFromBytes(pubkey.toSSZBytes()), new Bytes32View(withdrawal_credentials), new UInt64View(effective_balance), new BitView(slashed), @@ -175,7 +175,7 @@ public static Validator create( } public BLSPublicKey getPubkey() { - return BLSPublicKey.fromBytes(ViewUtils.getAllBytes(getAny(0))); + return BLSPublicKey.fromSSZBytes(ViewUtils.getAllBytes(getAny(0))); } public Bytes32 getWithdrawal_credentials() { diff --git a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/util/CommitteeUtil.java b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/util/CommitteeUtil.java index 1d91834e784..aadc80bf892 100644 --- a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/util/CommitteeUtil.java +++ b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/util/CommitteeUtil.java @@ -278,7 +278,7 @@ public static int getAggregatorModulo(final int committeeSize) { } public static boolean isAggregator(final BLSSignature slot_signature, final int modulo) { - return (bytes_to_int(Hash.sha2_256(slot_signature.toBytes()).slice(0, 8)) % modulo) == 0; + return (bytes_to_int(Hash.sha2_256(slot_signature.toSSZBytes()).slice(0, 8)) % modulo) == 0; } /** diff --git a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/util/LengthBoundCalculator.java b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/util/LengthBoundCalculator.java index b14bff82b38..31827f09b6b 100644 --- a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/util/LengthBoundCalculator.java +++ b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/util/LengthBoundCalculator.java @@ -146,9 +146,9 @@ private static int getPrimitiveLength(final Class classInfo) { case "Bytes4": return Bytes4.SIZE; case "BLSSignature": - return BLSSignature.BLS_SIGNATURE_SIZE; + return BLSSignature.SSZ_BLS_SIGNATURE_SIZE; case "BLSPublicKey": - return BLSPublicKey.BLS_PUBKEY_SIZE; + return BLSPublicKey.SSZ_BLS_PUBKEY_SIZE; case "Boolean": case "boolean": return BOOLEAN_SIZE; diff --git a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/util/SimpleOffsetSerializer.java b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/util/SimpleOffsetSerializer.java index d79f486255f..69fa2e7ef9e 100644 --- a/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/util/SimpleOffsetSerializer.java +++ b/ethereum/datastructures/src/main/java/tech/pegasys/teku/datastructures/util/SimpleOffsetSerializer.java @@ -496,11 +496,12 @@ private static Object deserializePrimitive( bytePointer.add(Bytes4.SIZE); return new Bytes4(reader.readFixedBytes(Bytes4.SIZE)); case "BLSSignature": - bytePointer.add(BLSSignature.BLS_SIGNATURE_SIZE); - return BLSSignature.fromBytes(reader.readFixedBytes(BLSSignature.BLS_SIGNATURE_SIZE)); + bytePointer.add(BLSSignature.SSZ_BLS_SIGNATURE_SIZE); + return BLSSignature.fromSSZBytes( + reader.readFixedBytes(BLSSignature.SSZ_BLS_SIGNATURE_SIZE)); case "BLSPublicKey": - bytePointer.add(BLSPublicKey.BLS_PUBKEY_SIZE); - return BLSPublicKey.fromBytes(reader.readFixedBytes(BLSPublicKey.BLS_PUBKEY_SIZE)); + bytePointer.add(BLSPublicKey.SSZ_BLS_PUBKEY_SIZE); + return BLSPublicKey.fromSSZBytes(reader.readFixedBytes(BLSPublicKey.SSZ_BLS_PUBKEY_SIZE)); case "Boolean": case "boolean": bytePointer.add(BOOLEAN_SIZE); diff --git a/ethereum/datastructures/src/test/java/tech/pegasys/teku/datastructures/util/MockStartValidatorKeyPairFactoryTest.java b/ethereum/datastructures/src/test/java/tech/pegasys/teku/datastructures/util/MockStartValidatorKeyPairFactoryTest.java index 465b269351b..393b2458c24 100644 --- a/ethereum/datastructures/src/test/java/tech/pegasys/teku/datastructures/util/MockStartValidatorKeyPairFactoryTest.java +++ b/ethereum/datastructures/src/test/java/tech/pegasys/teku/datastructures/util/MockStartValidatorKeyPairFactoryTest.java @@ -58,7 +58,7 @@ public void shouldGenerateValidKeys() { final List keyPairs = factory.generateKeyPairs(0, 10); final List actualPrivateKeys = keyPairs.stream() - .map(keyPair -> keyPair.getSecretKey().getSecretKey().toBytes().toBigInteger()) + .map(keyPair -> keyPair.getSecretKey().toBytes().toBigInteger()) .collect(toList()); final List expectedPrivateKeys = @@ -67,7 +67,7 @@ public void shouldGenerateValidKeys() { final List actualPublicKeys = keyPairs.stream() - .map(keyPair -> keyPair.getPublicKey().toBytes().toHexString()) + .map(keyPair -> keyPair.getPublicKey().toSSZBytes().toHexString()) .collect(toList()); final List expectedPublicKeys = diff --git a/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/util/BlockProcessorUtilTest.java b/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/util/BlockProcessorUtilTest.java index 958501b5c09..d5b0e97ef0c 100644 --- a/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/util/BlockProcessorUtilTest.java +++ b/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/util/BlockProcessorUtilTest.java @@ -19,6 +19,7 @@ import java.util.Arrays; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.bytes.Bytes48; import org.apache.tuweni.junit.BouncyCastleExtension; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -111,13 +112,13 @@ void processDepositIgnoresDepositWithInvalidPublicKey() throws BlockProcessingEx // The following deposit uses a "rogue" public key that is not in the G1 group BLSPublicKey pubkey = BLSPublicKey.fromBytesCompressed( - Bytes.fromHexString( + Bytes48.fromHexString( "0x9378a6e3984e96d2cd50450c76ca14732f1300efa04aecdb805b22e6d6926a85ef409e8f3acf494a1481090bf32ce3bd")); Bytes32 withdrawalCredentials = Bytes32.fromHexString("0x79e43d39ee55749c55994a7ab2a3cb91460cec544fdbf27eb5717c43f970c1b6"); UnsignedLong amount = UnsignedLong.valueOf(1000000000L); BLSSignature signature = - BLSSignature.fromBytes( + BLSSignature.fromBytesCompressed( Bytes.fromHexString( "0xddc1ca509e29c6452441069f26da6e073589b3bd1cace50e3427426af5bfdd566d077d4bdf618e249061b9770471e3d515779aa758b8ccb4b06226a8d5ebc99e19d4c3278e5006b837985bec4e0ce39df92c1f88d1afd0f98dbae360024a390d")); DepositData depositInput = diff --git a/gradle/check-licenses.gradle b/gradle/check-licenses.gradle index 20bed852125..ce4894835da 100644 --- a/gradle/check-licenses.gradle +++ b/gradle/check-licenses.gradle @@ -169,6 +169,7 @@ downloadLicenses { //JMH-Core is licensed under GPLv2 with the Classpath Exception, which allows us to link it and license the derived work under our license. 'org.openjdk.jmh:jmh-core:1.21': apache, (group('io.libp2p')): apache, + (group('tech.pegasys')): apache, ] } diff --git a/gradle/versions.gradle b/gradle/versions.gradle index 8a83075f356..02b8dcf5387 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -31,6 +31,8 @@ dependencyManagement { dependency 'io.protostuff:protostuff-runtime:1.6.2' dependency 'io.libp2p:jvm-libp2p-minimal:0.5.3-RELEASE' + dependency 'tech.pegasys:jblst:0.1.0-RELEASE' + dependency 'org.jetbrains.kotlin:kotlin-stdlib:1.3.72' dependency 'io.pkts:pkts-core:3.0.3' diff --git a/networking/p2p/src/test/java/tech/pegasys/teku/networking/p2p/DiscoveryNetworkTest.java b/networking/p2p/src/test/java/tech/pegasys/teku/networking/p2p/DiscoveryNetworkTest.java index fc5afed72cf..5f1ffabf182 100644 --- a/networking/p2p/src/test/java/tech/pegasys/teku/networking/p2p/DiscoveryNetworkTest.java +++ b/networking/p2p/src/test/java/tech/pegasys/teku/networking/p2p/DiscoveryNetworkTest.java @@ -276,7 +276,7 @@ public void setForkInfoAtInitialization() { public DiscoveryPeer createDiscoveryPeer(Optional maybeForkId) { return new DiscoveryPeer( - BLSPublicKey.empty().toBytes(), + BLSPublicKey.empty().toSSZBytes(), InetSocketAddress.createUnresolved("yo", 9999), maybeForkId, new Bitvector(ATTESTATION_SUBNET_COUNT)); diff --git a/pow/src/main/java/tech/pegasys/teku/pow/event/Deposit.java b/pow/src/main/java/tech/pegasys/teku/pow/event/Deposit.java index 716ef63f602..9c20eda9b16 100644 --- a/pow/src/main/java/tech/pegasys/teku/pow/event/Deposit.java +++ b/pow/src/main/java/tech/pegasys/teku/pow/event/Deposit.java @@ -18,6 +18,7 @@ import java.util.Objects; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.bytes.Bytes48; import tech.pegasys.teku.bls.BLSPublicKey; import tech.pegasys.teku.bls.BLSSignature; import tech.pegasys.teku.pow.contract.DepositContract; @@ -32,9 +33,9 @@ public class Deposit { public Deposit(DepositContract.DepositEventEventResponse response) { this.merkle_tree_index = UnsignedLong.valueOf(Bytes.wrap(response.index).reverse().toLong()); - this.pubkey = BLSPublicKey.fromBytesCompressed(Bytes.wrap(response.pubkey)); + this.pubkey = BLSPublicKey.fromBytesCompressed(Bytes48.wrap(response.pubkey)); this.withdrawal_credentials = Bytes32.wrap(response.withdrawal_credentials); - this.signature = BLSSignature.fromBytes(Bytes.wrap(response.signature)); + this.signature = BLSSignature.fromBytesCompressed(Bytes.wrap(response.signature)); this.amount = UnsignedLong.valueOf(Bytes.wrap(response.amount).reverse().toLong()); } diff --git a/services/powchain/src/main/java/tech/pegasys/teku/services/powchain/DepositTransactionSender.java b/services/powchain/src/main/java/tech/pegasys/teku/services/powchain/DepositTransactionSender.java index 28dce596db9..9873db941d7 100644 --- a/services/powchain/src/main/java/tech/pegasys/teku/services/powchain/DepositTransactionSender.java +++ b/services/powchain/src/main/java/tech/pegasys/teku/services/powchain/DepositTransactionSender.java @@ -93,7 +93,7 @@ private SafeFuture sendDepositTransaction(final DepositData .deposit( depositData.getPubkey().toBytesCompressed().toArray(), depositData.getWithdrawal_credentials().toArray(), - depositData.getSignature().getSignature().toBytesCompressed().toArray(), + depositData.getSignature().toSSZBytes().toArray(), depositData.hash_tree_root().toArray(), new BigInteger(depositData.getAmount() + "000000000")) .sendAsync()); diff --git a/storage/src/main/java/tech/pegasys/teku/storage/server/rocksdb/serialization/DepositsFromBlockEventSerializer.java b/storage/src/main/java/tech/pegasys/teku/storage/server/rocksdb/serialization/DepositsFromBlockEventSerializer.java index 6c6a19343da..244b6849413 100644 --- a/storage/src/main/java/tech/pegasys/teku/storage/server/rocksdb/serialization/DepositsFromBlockEventSerializer.java +++ b/storage/src/main/java/tech/pegasys/teku/storage/server/rocksdb/serialization/DepositsFromBlockEventSerializer.java @@ -60,7 +60,7 @@ private Bytes encodeDeposit(Deposit deposit) { depositWriter -> { depositWriter.writeFixedBytes(deposit.getPubkey().toBytesCompressed()); depositWriter.writeFixedBytes(deposit.getWithdrawal_credentials()); - depositWriter.writeFixedBytes(deposit.getSignature().toBytes()); + depositWriter.writeFixedBytes(deposit.getSignature().toSSZBytes()); depositWriter.writeUInt64(deposit.getAmount().longValue()); depositWriter.writeUInt64(deposit.getMerkle_tree_index().longValue()); }); @@ -71,10 +71,11 @@ private Deposit decodeDeposit(final Bytes data) { data, reader -> { final BLSPublicKey publicKey = - BLSPublicKey.fromBytesCompressed(reader.readFixedBytes(BLSPublicKey.BLS_PUBKEY_SIZE)); + BLSPublicKey.fromSSZBytes( + Bytes.wrap(reader.readFixedBytes(BLSPublicKey.SSZ_BLS_PUBKEY_SIZE))); final Bytes32 withdrawalCredentials = Bytes32.wrap(reader.readFixedBytes(Bytes32.SIZE)); final BLSSignature signature = - BLSSignature.fromBytes(reader.readFixedBytes(BLSSignature.BLS_SIGNATURE_SIZE)); + BLSSignature.fromSSZBytes(reader.readFixedBytes(BLSSignature.SSZ_BLS_SIGNATURE_SIZE)); final UnsignedLong amount = UnsignedLong.fromLongBits(reader.readUInt64()); final UnsignedLong merkleTreeIndex = UnsignedLong.fromLongBits(reader.readUInt64()); return new Deposit(publicKey, withdrawalCredentials, signature, amount, merkleTreeIndex); diff --git a/teku/src/main/java/tech/pegasys/teku/cli/deposit/DepositRegisterCommand.java b/teku/src/main/java/tech/pegasys/teku/cli/deposit/DepositRegisterCommand.java index 2dbf5593c3d..c9614d6558c 100644 --- a/teku/src/main/java/tech/pegasys/teku/cli/deposit/DepositRegisterCommand.java +++ b/teku/src/main/java/tech/pegasys/teku/cli/deposit/DepositRegisterCommand.java @@ -21,6 +21,7 @@ import java.util.function.Function; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.bytes.Bytes48; import picocli.CommandLine.ArgGroup; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; @@ -98,7 +99,7 @@ public void run() { try (final RegisterAction registerAction = registerParams.createRegisterAction(verboseOutputParam.isVerboseOutputEnabled())) { final BLSPublicKey withdrawalPublicKey = - BLSPublicKey.fromBytesCompressed(Bytes.fromHexString(this.withdrawalKey)); + BLSPublicKey.fromBytesCompressed(Bytes48.fromHexString(this.withdrawalKey)); registerAction.displayConfirmation(1); registerAction.sendDeposit(validatorKey, withdrawalPublicKey).get(); } catch (final Throwable t) { diff --git a/teku/src/main/java/tech/pegasys/teku/cli/subcommand/debug/YamlEth1EventsChannel.java b/teku/src/main/java/tech/pegasys/teku/cli/subcommand/debug/YamlEth1EventsChannel.java index 2dfd4269492..821e7746418 100644 --- a/teku/src/main/java/tech/pegasys/teku/cli/subcommand/debug/YamlEth1EventsChannel.java +++ b/teku/src/main/java/tech/pegasys/teku/cli/subcommand/debug/YamlEth1EventsChannel.java @@ -171,7 +171,7 @@ private DepositInfo( this.publicKey = publicKey.toBytesCompressed().toHexString(); this.amount = amount; this.withdrawalCredentials = withdrawalCredentials.toHexString(); - this.signature = signature.toBytes().toHexString(); + this.signature = signature.toSSZBytes().toHexString(); this.errors = errors; } } diff --git a/teku/src/test/java/tech/pegasys/teku/cli/options/ValidatorOptionsTest.java b/teku/src/test/java/tech/pegasys/teku/cli/options/ValidatorOptionsTest.java index 3b4b28036c1..e34993f87cd 100644 --- a/teku/src/test/java/tech/pegasys/teku/cli/options/ValidatorOptionsTest.java +++ b/teku/src/test/java/tech/pegasys/teku/cli/options/ValidatorOptionsTest.java @@ -17,8 +17,8 @@ import java.net.MalformedURLException; import java.net.URL; -import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.bytes.Bytes48; import org.junit.jupiter.api.Test; import tech.pegasys.teku.bls.BLSPublicKey; import tech.pegasys.teku.cli.AbstractBeaconNodeCommandTest; @@ -33,7 +33,7 @@ public void shouldReadFromConfigurationFile() throws MalformedURLException { Bytes32.fromHexString("0x542045204b205500000000000000000000000000000000000000000000000000"); final BLSPublicKey publicKey = BLSPublicKey.fromBytesCompressed( - Bytes.fromHexString( + Bytes48.fromHexString( "0xad113a7d152dc74ae2b26db65bfb89ed07501c818bf47671c6d34e5a2f7224e4c5525dd4fddaa93aa328da86b7205009")); final TekuConfiguration config = getTekuConfigurationFromFile("validatorOptions_config.yaml"); diff --git a/util/src/main/java/tech/pegasys/teku/util/config/TekuConfiguration.java b/util/src/main/java/tech/pegasys/teku/util/config/TekuConfiguration.java index 5fe301c5b70..43c338fff5d 100644 --- a/util/src/main/java/tech/pegasys/teku/util/config/TekuConfiguration.java +++ b/util/src/main/java/tech/pegasys/teku/util/config/TekuConfiguration.java @@ -352,7 +352,7 @@ public List getValidatorExternalSignerPublicKeys() { } try { return validatorExternalSignerPublicKeys.stream() - .map(key -> BLSPublicKey.fromBytes(Bytes.fromHexString(key))) + .map(key -> BLSPublicKey.fromSSZBytes(Bytes.fromHexString(key))) .collect(Collectors.toList()); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("Invalid configuration. Signer public key is invalid", e); diff --git a/util/src/main/java/tech/pegasys/teku/util/hashtree/HashTreeUtil.java b/util/src/main/java/tech/pegasys/teku/util/hashtree/HashTreeUtil.java index 11de8aabd09..cd03fec3ca1 100644 --- a/util/src/main/java/tech/pegasys/teku/util/hashtree/HashTreeUtil.java +++ b/util/src/main/java/tech/pegasys/teku/util/hashtree/HashTreeUtil.java @@ -339,7 +339,7 @@ public static Bytes32 hash_tree_root_list_bytes(SSZList bytes) { private static Bytes32 hash_tree_root_list_pubkey(SSZList bytes) { List hashTreeRootList = bytes.stream() - .map(item -> hash_tree_root(SSZTypes.VECTOR_OF_BASIC, item.toBytes())) + .map(item -> hash_tree_root(SSZTypes.VECTOR_OF_BASIC, item.toSSZBytes())) .collect(Collectors.toList()); return mix_in_length( merkleize(hashTreeRootList, chunk_count(SSZTypes.LIST_OF_COMPOSITE, bytes.getMaxSize())), diff --git a/util/src/test/java/tech/pegasys/teku/util/bytes/KeyFormatterTest.java b/util/src/test/java/tech/pegasys/teku/util/bytes/KeyFormatterTest.java index c2a6eab6cc6..8faf344df47 100644 --- a/util/src/test/java/tech/pegasys/teku/util/bytes/KeyFormatterTest.java +++ b/util/src/test/java/tech/pegasys/teku/util/bytes/KeyFormatterTest.java @@ -26,7 +26,7 @@ public void shouldShowFirstSevenBytesOfPublicKey() { Bytes keyBytes = Bytes.fromHexString( "0xab10fc693d038b73d67279127501a05f0072cbb7147c68650ef6ac4e0a413e5cabd1f35c8711e1f7d9d885bbc3b8eddc"); - BLSPublicKey blsPublicKey = BLSPublicKey.fromBytes(keyBytes); + BLSPublicKey blsPublicKey = BLSPublicKey.fromSSZBytes(keyBytes); assertThat(KeyFormatter.shortPublicKey(blsPublicKey)).isEqualTo("ab10fc6"); } diff --git a/validator/client/src/integration-test/java/tech/pegasys/teku/validator/client/ExternalMessageSignerServiceIntegrationTest.java b/validator/client/src/integration-test/java/tech/pegasys/teku/validator/client/ExternalMessageSignerServiceIntegrationTest.java index c6e07fa9d91..41f91799941 100644 --- a/validator/client/src/integration-test/java/tech/pegasys/teku/validator/client/ExternalMessageSignerServiceIntegrationTest.java +++ b/validator/client/src/integration-test/java/tech/pegasys/teku/validator/client/ExternalMessageSignerServiceIntegrationTest.java @@ -79,7 +79,7 @@ void failsSigningWhenSigningServiceReturnsFailureResponse() { final ExternalMessageSignerService externalMessageSignerService = new ExternalMessageSignerService( signingServiceUri, - BLSPublicKey.fromBytes(Bytes.fromHexString(UNKNOWN_PUBLIC_KEY)), + BLSPublicKey.fromSSZBytes(Bytes.fromHexString(UNKNOWN_PUBLIC_KEY)), TIMEOUT); assertThatThrownBy(() -> externalMessageSignerService.signBlock(SIGNING_ROOT).join()) @@ -117,9 +117,7 @@ void failsSigningWhenSigningServiceReturnsInvalidSignatureResponse() { @Test void signsBlockWhenSigningServiceReturnsSuccessfulResponse() { - client - .when(request()) - .respond(response().withBody(expectedSignature.getSignature().toString())); + client.when(request()).respond(response().withBody(expectedSignature.toString())); final BLSSignature signature = externalMessageSignerService.signBlock(SIGNING_ROOT).join(); assertThat(signature).isEqualTo(expectedSignature); diff --git a/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/ExternalMessageSignerService.java b/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/ExternalMessageSignerService.java index a558f3ad330..96268ac186c 100644 --- a/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/ExternalMessageSignerService.java +++ b/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/ExternalMessageSignerService.java @@ -73,7 +73,7 @@ public SafeFuture signVoluntaryExit(final Bytes signingRoot) { } private SafeFuture sign(final Bytes signingRoot) { - final String publicKey = blsPublicKey.getPublicKey().toString(); + final String publicKey = blsPublicKey.toBytesCompressed().toString(); return SafeFuture.ofComposed( () -> { final String requestBody = createSigningRequestBody(signingRoot); @@ -115,7 +115,7 @@ private BLSSignature getBlsSignature( try { final Bytes signature = Bytes.fromHexString(response.body()); - return BLSSignature.fromBytes(signature); + return BLSSignature.fromBytesCompressed(signature); } catch (final IllegalArgumentException e) { throw new ExternalSignerException( "External signer returned an invalid signature: " + e.getMessage(), e); diff --git a/validator/client/src/test/java/tech/pegasys/teku/validator/client/loader/ValidatorLoaderTest.java b/validator/client/src/test/java/tech/pegasys/teku/validator/client/loader/ValidatorLoaderTest.java index a9429ef0714..03dcb3b7f65 100644 --- a/validator/client/src/test/java/tech/pegasys/teku/validator/client/loader/ValidatorLoaderTest.java +++ b/validator/client/src/test/java/tech/pegasys/teku/validator/client/loader/ValidatorLoaderTest.java @@ -53,7 +53,7 @@ void initializeValidatorsWithExternalMessageSignerWhenConfigHasExternalSigningPu ValidatorLoader.initializeValidators(tekuConfiguration); assertThat(validators).hasSize(1); - final BLSPublicKey key = BLSPublicKey.fromBytes(Bytes.fromHexString(PUBLIC_KEY1)); + final BLSPublicKey key = BLSPublicKey.fromSSZBytes(Bytes.fromHexString(PUBLIC_KEY1)); final Validator validator = validators.get(key); assertThat(validator).isNotNull(); assertThat(validator.getPublicKey()).isEqualTo(key); @@ -77,7 +77,7 @@ void initializeValidatorsWithLocalMessageSignerWhenConfigHasValidatorsKeyFile( ValidatorLoader.initializeValidators(tekuConfiguration); assertThat(validators).hasSize(1); - final BLSPublicKey key = BLSPublicKey.fromBytes(Bytes.fromHexString(PUBLIC_KEY1)); + final BLSPublicKey key = BLSPublicKey.fromSSZBytes(Bytes.fromHexString(PUBLIC_KEY1)); final Validator validator = validators.get(key); assertThat(validator).isNotNull(); assertThat(validator.getPublicKey()).isEqualTo(key); @@ -104,14 +104,14 @@ void initializeValidatorsWithBothLocalAndExternalSigners(@TempDir Path tempDir) assertThat(validators).hasSize(2); - final BLSPublicKey key1 = BLSPublicKey.fromBytes(Bytes.fromHexString(PUBLIC_KEY1)); + final BLSPublicKey key1 = BLSPublicKey.fromSSZBytes(Bytes.fromHexString(PUBLIC_KEY1)); final Validator validator1 = validators.get(key1); assertThat(validator1).isNotNull(); assertThat(validator1.getPublicKey()).isEqualTo(key1); assertThat(validator1.getSigner().getMessageSignerService()) .isInstanceOf(LocalMessageSignerService.class); - final BLSPublicKey key2 = BLSPublicKey.fromBytes(Bytes.fromHexString(PUBLIC_KEY2)); + final BLSPublicKey key2 = BLSPublicKey.fromSSZBytes(Bytes.fromHexString(PUBLIC_KEY2)); final Validator validator2 = validators.get(key2); assertThat(validator2).isNotNull(); assertThat(validator2.getPublicKey()).isEqualTo(key2); @@ -140,7 +140,7 @@ void initializeValidatorsWithDuplicateKeysInLocalAndExternalSignersTakesExternal assertThat(validators).hasSize(1); // Local validators are listed first - final BLSPublicKey key = BLSPublicKey.fromBytes(Bytes.fromHexString(PUBLIC_KEY1)); + final BLSPublicKey key = BLSPublicKey.fromSSZBytes(Bytes.fromHexString(PUBLIC_KEY1)); final Validator validator = validators.get(key); assertThat(validator).isNotNull(); assertThat(validator.getPublicKey()).isEqualTo(key); diff --git a/validator/client/src/test/java/tech/pegasys/teku/validator/client/loader/YamlValidatorKeyProviderTest.java b/validator/client/src/test/java/tech/pegasys/teku/validator/client/loader/YamlValidatorKeyProviderTest.java index d8047acb68e..a5ff328bd33 100644 --- a/validator/client/src/test/java/tech/pegasys/teku/validator/client/loader/YamlValidatorKeyProviderTest.java +++ b/validator/client/src/test/java/tech/pegasys/teku/validator/client/loader/YamlValidatorKeyProviderTest.java @@ -47,16 +47,16 @@ public void shouldLoadExampleFile(@TempDir Path tempDirectory) throws Exception final List EXPECTED_PRIVATE_KEYS = asList( - "0x0000000000000000000000000000000025295f0d1d592a90b333e26e85149708208e9f8e8bc18f6c77bd62f8ad7a6866", - "0x0000000000000000000000000000000051d0b65185db6989ab0b560d6deed19c7ead0e24b9b6372cbecb1f26bdfad000", - "0x00000000000000000000000000000000315ed405fafe339603932eebe8dbfd650ce5dafa561f6928664c75db85f97857"); + "0x25295f0d1d592a90b333e26e85149708208e9f8e8bc18f6c77bd62f8ad7a6866", + "0x51d0b65185db6989ab0b560d6deed19c7ead0e24b9b6372cbecb1f26bdfad000", + "0x315ed405fafe339603932eebe8dbfd650ce5dafa561f6928664c75db85f97857"); when(config.getValidatorsKeyFile()).thenReturn(tempFile.toAbsolutePath().toString()); final List keys = provider.loadValidatorKeys(config); final List actualPrivateKeys = keys.stream() - .map(keypair -> keypair.getSecretKey().getSecretKey().toBytes().toHexString()) + .map(keypair -> keypair.getSecretKey().toBytes().toHexString()) .collect(Collectors.toList()); assertEquals(EXPECTED_PRIVATE_KEYS, actualPrivateKeys); diff --git a/validator/client/src/test/java/tech/pegasys/teku/validator/client/signer/SignerTest.java b/validator/client/src/test/java/tech/pegasys/teku/validator/client/signer/SignerTest.java index 5cf34d192a3..6547ce01ba1 100644 --- a/validator/client/src/test/java/tech/pegasys/teku/validator/client/signer/SignerTest.java +++ b/validator/client/src/test/java/tech/pegasys/teku/validator/client/signer/SignerTest.java @@ -53,11 +53,11 @@ public void shouldCreateRandaoReveal() { } @Test - public void shouldSignBlock() { + public void shouldSignBlock1() { final BeaconBlock block = dataStructureUtil.randomBeaconBlock(10); final BLSSignature signature = dataStructureUtil.randomSignature(); final Bytes expectedSigningRoot = - Bytes.fromHexString("0xc0e4ed8375c98504b262f610f217d31ebf109f0f73c164362090c6ad7d4770c1"); + Bytes.fromHexString("0xfa8b3cfed0268ed15e354e84db5558eb76ad30737a86d6d057615e331ff30d44"); when(signerService.signBlock(expectedSigningRoot)) .thenReturn(SafeFuture.completedFuture(signature)); diff --git a/validator/coordinator/src/test/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandlerTest.java b/validator/coordinator/src/test/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandlerTest.java index ac0721d2393..185a607f0af 100644 --- a/validator/coordinator/src/test/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandlerTest.java +++ b/validator/coordinator/src/test/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandlerTest.java @@ -301,8 +301,8 @@ public void createUnsignedAttestation_shouldCreateAttestation() { AttestationUtil.getGenericAttestationData( slot, state, blockAndState.getBlock(), UnsignedLong.valueOf(committeeIndex))); assertThat(attestation.getData().getSlot()).isEqualTo(slot); - assertThat(attestation.getAggregate_signature().toBytes()) - .isEqualTo(BLSSignature.empty().toBytes()); + assertThat(attestation.getAggregate_signature().toSSZBytes()) + .isEqualTo(BLSSignature.empty().toSSZBytes()); } @Test