diff --git a/src/blst/signature.ts b/src/blst/signature.ts index 0385233..69b0e0f 100644 --- a/src/blst/signature.ts +++ b/src/blst/signature.ts @@ -28,6 +28,14 @@ export class Signature implements ISignature { return new Signature(agg.toSignature()); } + static verifyMultipleSignatures(publicKeys: PublicKey[], messages: Uint8Array[], signatures: Signature[]): boolean { + return blst.verifyMultipleAggregateSignatures( + messages, + publicKeys.map((publicKey) => publicKey.affine), + signatures.map((signature) => signature.affine) + ); + } + verify(publicKey: PublicKey, message: Uint8Array): boolean { // Individual infinity signatures are NOT okay. Aggregated signatures MAY be infinity if (this.affine.value.is_inf()) { diff --git a/src/functional.ts b/src/functional.ts index 61e04c8..aac05ca 100644 --- a/src/functional.ts +++ b/src/functional.ts @@ -106,6 +106,36 @@ export function functionalInterfaceFactory({ } } + /** + * Verifies multiple signatures at once returning true if all valid or false + * if at least one is not. Optimized method when knowing which signature is + * wrong is not relevant, i.e. verifying an Eth2.0 block. + * https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407 + */ + function verifyMultipleSignatures( + publicKeys: Uint8Array[], + messages: Uint8Array[], + signatures: Uint8Array[] + ): boolean { + validateBytes(publicKeys, "publicKey"); + validateBytes(messages, "message"); + validateBytes(signatures, "signatures"); + + if (publicKeys.length === 0 || publicKeys.length !== messages.length || publicKeys.length !== signatures.length) { + return false; + } + try { + return Signature.verifyMultipleSignatures( + publicKeys.map((publicKey) => PublicKey.fromBytes(publicKey)), + messages.map((msg) => msg), + signatures.map((signature) => Signature.fromBytes(signature)) + ); + } catch (e) { + if (e instanceof NotInitializedError) throw e; + return false; + } + } + /** * Computes a public key from a secret key */ @@ -121,6 +151,7 @@ export function functionalInterfaceFactory({ verify, verifyAggregate, verifyMultiple, + verifyMultipleSignatures, secretKeyToPublicKey, }; } diff --git a/src/herumi/signature.ts b/src/herumi/signature.ts index 6a23c89..cca6857 100644 --- a/src/herumi/signature.ts +++ b/src/herumi/signature.ts @@ -1,5 +1,5 @@ import {SIGNATURE_LENGTH} from "../constants"; -import {SignatureType} from "bls-eth-wasm"; +import {SignatureType, multiVerify} from "bls-eth-wasm"; import {getContext} from "./context"; import {PublicKey} from "./publicKey"; import {bytesToHex, hexToBytes, isZeroUint8Array} from "../helpers"; @@ -45,6 +45,14 @@ export class Signature implements ISignature { return new Signature(signature); } + static verifyMultipleSignatures(publicKeys: PublicKey[], messages: Uint8Array[], signatures: Signature[]): boolean { + return multiVerify( + publicKeys.map((publicKey) => publicKey.value), + signatures.map((signature) => signature.value), + messages + ); + } + verify(publicKey: PublicKey, message: Uint8Array): boolean { return publicKey.value.verify(this.value, message); } diff --git a/src/interface.ts b/src/interface.ts index 5e9dc5b..822e657 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -13,6 +13,7 @@ export interface IBls { fromBytes(bytes: Uint8Array): Signature; fromHex(hex: string): Signature; aggregate(signatures: Signature[]): Signature; + verifyMultipleSignatures(publicKeys: PublicKey[], messages: Uint8Array[], signatures: Signature[]): boolean; }; sign(secretKey: Uint8Array, message: Uint8Array): Uint8Array; @@ -21,6 +22,7 @@ export interface IBls { verify(publicKey: Uint8Array, message: Uint8Array, signature: Uint8Array): boolean; verifyAggregate(publicKeys: Uint8Array[], message: Uint8Array, signature: Uint8Array): boolean; verifyMultiple(publicKeys: Uint8Array[], messages: Uint8Array[], signature: Uint8Array): boolean; + verifyMultipleSignatures(publicKeys: Uint8Array[], messages: Uint8Array[], signatures: Uint8Array[]): boolean; secretKeyToPublicKey(secretKey: Uint8Array): Uint8Array; init(): Promise; @@ -49,6 +51,7 @@ export declare class Signature { static fromBytes(bytes: Uint8Array): Signature; static fromHex(hex: string): Signature; static aggregate(signatures: Signature[]): Signature; + static verifyMultipleSignatures(publicKeys: PublicKey[], messages: Uint8Array[], signatures: Signature[]): boolean; verify(publicKey: PublicKey, message: Uint8Array): boolean; verifyAggregate(publicKeys: PublicKey[], message: Uint8Array): boolean; verifyMultiple(publicKeys: PublicKey[], messages: Uint8Array[]): boolean;