@chainsafe/bls
Version:
Implementation of bls signature verification for ethereum 2.0
115 lines (114 loc) • 4.69 kB
JavaScript
import { getContext } from "./context.js";
import { PublicKey } from "./publicKey.js";
import { bytesToHex, concatUint8Arrays, hexToBytes, isZeroUint8Array, validateBytes } from "../helpers/index.js";
import { PointFormat } from "../types.js";
import { EmptyAggregateError, InvalidLengthError, InvalidOrderError } from "../errors.js";
import { SIGNATURE_LENGTH_COMPRESSED, SIGNATURE_LENGTH_UNCOMPRESSED } from "../constants.js";
export class Signature {
constructor(value) {
if (!value.isValidOrder()) {
throw new InvalidOrderError();
}
this.value = value;
}
/**
* @param type Does not affect `herumi` implementation, always de-serializes to `jacobian`
* @param validate With `herumi` implementation signature validation is always on regardless of this flag.
*/
static fromBytes(bytes, _type, _validate = true) {
const context = getContext();
const signature = new context.Signature();
if (!isZeroUint8Array(bytes)) {
if (bytes.length === SIGNATURE_LENGTH_COMPRESSED) {
signature.deserialize(bytes);
}
else if (bytes.length === SIGNATURE_LENGTH_UNCOMPRESSED) {
signature.deserializeUncompressed(bytes);
}
else {
throw new InvalidLengthError("Signature", bytes.length);
}
signature.deserialize(bytes);
}
return new Signature(signature);
}
static fromHex(hex) {
return this.fromBytes(hexToBytes(hex));
}
static aggregate(signatures) {
if (signatures.length === 0) {
throw new EmptyAggregateError();
}
const context = getContext();
const agg = new context.Signature();
agg.aggregate(signatures.map(Signature.convertToSignatureType));
return new Signature(agg);
}
static verifyMultipleSignatures(sets) {
if (!sets)
throw Error("sets is null or undefined");
const context = getContext();
return context.multiVerify(sets.map((s) => PublicKey.convertToPublicKeyType(s.publicKey)), sets.map((s) => Signature.convertToSignatureType(s.signature)), sets.map((s) => s.message));
}
static async asyncVerifyMultipleSignatures(sets) {
return Signature.verifyMultipleSignatures(sets);
}
static convertToSignatureType(signature) {
let sig;
if (signature instanceof Uint8Array) {
validateBytes(signature, "signature");
sig = Signature.fromBytes(signature);
}
else {
// need to cast to herumi sig instead of ISignature
sig = signature;
}
return sig.value;
}
verify(publicKey, message) {
validateBytes(message, "message");
return PublicKey.convertToPublicKeyType(publicKey).verify(this.value, message);
}
verifyAggregate(publicKeys, message) {
validateBytes(message, "message");
return this.value.fastAggregateVerify(publicKeys.map(PublicKey.convertToPublicKeyType), message);
}
verifyMultiple(publicKeys, messages) {
// TODO: (@matthewkeil) this was in the verifyMultiple free function but was moved here for herumi. blst-native
// does this check but throws error instead of returning false. Need to double check spec on which is
// correct handling
if (publicKeys.length === 0 || publicKeys.length != messages.length) {
return false;
}
validateBytes(messages, "message");
return this.value.aggregateVerifyNoCheck(publicKeys.map(PublicKey.convertToPublicKeyType), concatUint8Arrays(messages));
}
async asyncVerify(publicKey, message) {
return this.verify(publicKey, message);
}
async asyncVerifyAggregate(publicKeys, message) {
return this.verifyAggregate(publicKeys, message);
}
async asyncVerifyMultiple(publicKeys, messages) {
return this.verifyMultiple(publicKeys, messages);
}
toBytes(format) {
if (format === PointFormat.uncompressed) {
return this.value.serializeUncompressed();
}
else {
return this.value.serialize();
}
}
toHex(format) {
return bytesToHex(this.toBytes(format));
}
multiplyBy(_bytes) {
// TODO: I found this in the code but its not exported. Need to figure out
// how to implement
// const a = getContext();
// const randomness = new a.FR(8);
// return new Signature(a.mul(this.value, randomness));
throw new Error("multiplyBy is not implemented by bls-eth-wasm");
}
}