import * as blst from "@chainsafe/blst"; import { bytesToHex, hexToBytes } from "../helpers/index.js"; import { PointFormat } from "../types.js"; import { EmptyAggregateError, ZeroSignatureError } from "../errors.js"; export class Signature extends blst.Signature { constructor(value) { super(value); } /** @param type Defaults to `CoordType.affine` */ static fromBytes(bytes, type, validate = true) { const sig = blst.Signature.fromBytes(bytes, type); if (validate) sig.sigValidate(); return new Signature(sig.value); } static fromHex(hex) { return this.fromBytes(hexToBytes(hex)); } static aggregate(signatures) { if (signatures.length === 0) { throw new EmptyAggregateError(); } const agg = blst.aggregateSignatures(signatures); return new Signature(agg.value); } static verifyMultipleSignatures(sets) { return blst.verifyMultipleAggregateSignatures(sets.map((s) => ({ msg: s.message, pk: s.publicKey, sig: s.signature }))); } verify(publicKey, message) { // Individual infinity signatures are NOT okay. Aggregated signatures MAY be infinity if (this.value.is_inf()) { throw new ZeroSignatureError(); } return blst.verify(message, publicKey, this); } verifyAggregate(publicKeys, message) { return blst.fastAggregateVerify(message, publicKeys, this); } verifyMultiple(publicKeys, messages) { return blst.aggregateVerify(messages, publicKeys, this); } toBytes(format) { if (format === PointFormat.uncompressed) { return this.value.serialize(); } else { return this.value.compress(); } } toHex(format) { return bytesToHex(this.toBytes(format)); } aggregateVerify(msgs, pks) { // If this set is simply an infinity signature and infinity publicKey then skip verification. // This has the effect of always declaring that this sig/publicKey combination is valid. // for Eth2.0 specs tests if (this.value.is_inf() && pks.length === 1 && pks[0].value.is_inf()) { return true; } return blst.aggregateVerify(msgs, pks, this); } }