Make errors consistent

This commit is contained in:
dapplion 2020-11-30 00:20:52 +00:00
parent 60d795c694
commit c557db4a78
9 changed files with 61 additions and 29 deletions

View File

@ -1,5 +1,5 @@
import * as blst from "@chainsafe/blst"; import * as blst from "@chainsafe/blst";
import {ZeroPublicKeyError} from "../errors"; import {EmptyAggregateError, ZeroPublicKeyError} from "../errors";
import {bytesToHex, hexToBytes} from "../helpers"; import {bytesToHex, hexToBytes} from "../helpers";
import {IPublicKey} from "../interface"; import {IPublicKey} from "../interface";
@ -27,6 +27,10 @@ export class PublicKey implements IPublicKey {
} }
static aggregate(pubkeys: PublicKey[]): PublicKey { static aggregate(pubkeys: PublicKey[]): PublicKey {
if (pubkeys.length === 0) {
throw new EmptyAggregateError();
}
const jacobian = blst.aggregatePubkeys(pubkeys.map((pk) => pk.jacobian)); const jacobian = blst.aggregatePubkeys(pubkeys.map((pk) => pk.jacobian));
const affine = jacobian.toPublicKey(); const affine = jacobian.toPublicKey();
return new PublicKey(affine, jacobian); return new PublicKey(affine, jacobian);

View File

@ -2,7 +2,7 @@ import * as blst from "@chainsafe/blst";
import {bytesToHex, hexToBytes} from "../helpers"; import {bytesToHex, hexToBytes} from "../helpers";
import {ISignature} from "../interface"; import {ISignature} from "../interface";
import {PublicKey} from "./publicKey"; import {PublicKey} from "./publicKey";
import {ZeroSignatureError} from "../errors"; import {EmptyAggregateError, ZeroSignatureError} from "../errors";
export class Signature implements ISignature { export class Signature implements ISignature {
readonly affine: blst.Signature; readonly affine: blst.Signature;
@ -12,12 +12,7 @@ export class Signature implements ISignature {
} }
static fromBytes(bytes: Uint8Array): Signature { static fromBytes(bytes: Uint8Array): Signature {
const affine = blst.Signature.fromBytes(bytes); return new Signature(blst.Signature.fromBytes(bytes));
if (affine.value.is_inf()) {
throw new ZeroSignatureError();
}
return new Signature(affine);
} }
static fromHex(hex: string): Signature { static fromHex(hex: string): Signature {
@ -25,11 +20,20 @@ export class Signature implements ISignature {
} }
static aggregate(signatures: Signature[]): Signature { static aggregate(signatures: Signature[]): Signature {
if (signatures.length === 0) {
throw new EmptyAggregateError();
}
const agg = blst.AggregateSignature.fromSignatures(signatures.map((sig) => sig.affine)); const agg = blst.AggregateSignature.fromSignatures(signatures.map((sig) => sig.affine));
return new Signature(agg.toSignature()); return new Signature(agg.toSignature());
} }
verify(publicKey: PublicKey, message: Uint8Array): boolean { verify(publicKey: PublicKey, message: Uint8Array): boolean {
// Individual infinity signatures are NOT okay. Aggregated signatures MAY be infinity
if (this.affine.value.is_inf()) {
throw new ZeroSignatureError();
}
return this.aggregateVerify([message], [publicKey.affine]); return this.aggregateVerify([message], [publicKey.affine]);
} }

View File

@ -1,23 +1,45 @@
/** /**
* Indicate that this error is expected and should not be ignored * This error should not be ignored by the functional interface
* by the functional interface try / catch blocks * try / catch blocks, to prevent false negatives
*/ */
export class ExpectedError extends Error {} export class NotInitializedError extends Error {
constructor(implementation: string) {
super(`NOT_INITIALIZED: ${implementation}`);
}
}
export class ZeroPrivateKeyError extends Error { export class ZeroPrivateKeyError extends Error {
constructor() { constructor() {
super("PRIVATE_KEY_IS_ZERO"); super("ZERO_PRIVATE_KEY");
} }
} }
export class ZeroPublicKeyError extends Error { export class ZeroPublicKeyError extends Error {
constructor() { constructor() {
super("PUBLIC_KEY_IS_ZERO"); super("ZERO_PUBLIC_KEY");
} }
} }
export class ZeroSignatureError extends Error { export class ZeroSignatureError extends Error {
constructor() { constructor() {
super("SIGNATURE_IS_ZERO"); super("ZERO_SIGNATURE");
}
}
export class EmptyAggregateError extends Error {
constructor() {
super("EMPTY_AGGREGATE_ARRAY");
}
}
export class InvalidOrderError extends Error {
constructor() {
super("INVALID_ORDER");
}
}
export class InvalidLengthError extends Error {
constructor(arg: string, length: number) {
super(`INVALID_LENGTH: ${arg} must have ${length} bytes`);
} }
} }

View File

@ -1,6 +1,6 @@
import {IBls} from "./interface"; import {IBls} from "./interface";
import {validateBytes} from "./helpers"; import {validateBytes} from "./helpers";
import {ExpectedError} from "./errors"; import {NotInitializedError} from "./errors";
// Returned type is enforced at each implementation's index // Returned type is enforced at each implementation's index
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
@ -54,7 +54,7 @@ export function functionalInterfaceFactory({
try { try {
return Signature.fromBytes(signature).verify(PublicKey.fromBytes(publicKey), message); return Signature.fromBytes(signature).verify(PublicKey.fromBytes(publicKey), message);
} catch (e) { } catch (e) {
if (e instanceof ExpectedError) throw e; if (e instanceof NotInitializedError) throw e;
return false; return false;
} }
} }
@ -76,7 +76,7 @@ export function functionalInterfaceFactory({
message message
); );
} catch (e) { } catch (e) {
if (e instanceof ExpectedError) throw e; if (e instanceof NotInitializedError) throw e;
return false; return false;
} }
} }
@ -102,7 +102,7 @@ export function functionalInterfaceFactory({
messages.map((msg) => msg) messages.map((msg) => msg)
); );
} catch (e) { } catch (e) {
if (e instanceof ExpectedError) throw e; if (e instanceof NotInitializedError) throw e;
return false; return false;
} }
} }

View File

@ -1,6 +1,6 @@
/* eslint-disable require-atomic-updates */ /* eslint-disable require-atomic-updates */
import bls from "bls-eth-wasm"; import bls from "bls-eth-wasm";
import {ExpectedError} from "../errors"; import {NotInitializedError} from "../errors";
type Bls = typeof bls; type Bls = typeof bls;
let blsGlobal: Bls | null = null; let blsGlobal: Bls | null = null;
@ -28,7 +28,7 @@ export function destroy(): void {
export function getContext(): Bls { export function getContext(): Bls {
if (!blsGlobal) { if (!blsGlobal) {
throw new ExpectedError("BLS not initialized"); throw new NotInitializedError("herumi");
} }
return blsGlobal; return blsGlobal;
} }

View File

@ -6,7 +6,7 @@ import {PublicKey} from "./publicKey";
import {Signature} from "./signature"; import {Signature} from "./signature";
import {bytesToHex, hexToBytes} from "../helpers"; import {bytesToHex, hexToBytes} from "../helpers";
import {IPrivateKey} from "../interface"; import {IPrivateKey} from "../interface";
import {ZeroPrivateKeyError} from "../errors"; import {InvalidLengthError, ZeroPrivateKeyError} from "../errors";
export class PrivateKey implements IPrivateKey { export class PrivateKey implements IPrivateKey {
readonly value: SecretKeyType; readonly value: SecretKeyType;
@ -21,7 +21,7 @@ export class PrivateKey implements IPrivateKey {
static fromBytes(bytes: Uint8Array): PrivateKey { static fromBytes(bytes: Uint8Array): PrivateKey {
if (bytes.length !== SECRET_KEY_LENGTH) { if (bytes.length !== SECRET_KEY_LENGTH) {
throw Error(`Private key should have ${SECRET_KEY_LENGTH} bytes`); throw new InvalidLengthError("PrivateKey", SECRET_KEY_LENGTH);
} }
const context = getContext(); const context = getContext();

View File

@ -3,7 +3,7 @@ import {getContext} from "./context";
import {PUBLIC_KEY_LENGTH} from "../constants"; import {PUBLIC_KEY_LENGTH} from "../constants";
import {bytesToHex, hexToBytes, isZeroUint8Array} from "../helpers"; import {bytesToHex, hexToBytes, isZeroUint8Array} from "../helpers";
import {IPublicKey} from "../interface"; import {IPublicKey} from "../interface";
import {ZeroPublicKeyError} from "../errors"; import {EmptyAggregateError, InvalidLengthError, ZeroPublicKeyError} from "../errors";
export class PublicKey implements IPublicKey { export class PublicKey implements IPublicKey {
readonly value: PublicKeyType; readonly value: PublicKeyType;
@ -18,7 +18,7 @@ export class PublicKey implements IPublicKey {
static fromBytes(bytes: Uint8Array): PublicKey { static fromBytes(bytes: Uint8Array): PublicKey {
if (bytes.length !== PUBLIC_KEY_LENGTH) { if (bytes.length !== PUBLIC_KEY_LENGTH) {
throw Error(`Public key must have ${PUBLIC_KEY_LENGTH} bytes`); throw new InvalidLengthError("PublicKey", PUBLIC_KEY_LENGTH);
} }
const context = getContext(); const context = getContext();
@ -35,7 +35,7 @@ export class PublicKey implements IPublicKey {
static aggregate(pubkeys: PublicKey[]): PublicKey { static aggregate(pubkeys: PublicKey[]): PublicKey {
if (pubkeys.length === 0) { if (pubkeys.length === 0) {
throw Error("EMPTY_AGGREGATE_ARRAY"); throw new EmptyAggregateError();
} }
const agg = new PublicKey(pubkeys[0].value.clone()); const agg = new PublicKey(pubkeys[0].value.clone());

View File

@ -4,13 +4,14 @@ import {getContext} from "./context";
import {PublicKey} from "./publicKey"; import {PublicKey} from "./publicKey";
import {bytesToHex, hexToBytes, isZeroUint8Array} from "../helpers"; import {bytesToHex, hexToBytes, isZeroUint8Array} from "../helpers";
import {ISignature} from "../interface"; import {ISignature} from "../interface";
import {EmptyAggregateError, InvalidLengthError, InvalidOrderError} from "../errors";
export class Signature implements ISignature { export class Signature implements ISignature {
readonly value: SignatureType; readonly value: SignatureType;
constructor(value: SignatureType) { constructor(value: SignatureType) {
if (!value.isValidOrder()) { if (!value.isValidOrder()) {
throw Error("Signature is not in valid order"); throw new InvalidOrderError();
} }
this.value = value; this.value = value;
@ -18,7 +19,7 @@ export class Signature implements ISignature {
static fromBytes(bytes: Uint8Array): Signature { static fromBytes(bytes: Uint8Array): Signature {
if (bytes.length !== SIGNATURE_LENGTH) { if (bytes.length !== SIGNATURE_LENGTH) {
throw Error(`Signature must have ${SIGNATURE_LENGTH} bytes`); throw new InvalidLengthError("Signature", SIGNATURE_LENGTH);
} }
const context = getContext(); const context = getContext();
@ -35,7 +36,7 @@ export class Signature implements ISignature {
static aggregate(signatures: Signature[]): Signature { static aggregate(signatures: Signature[]): Signature {
if (signatures.length === 0) { if (signatures.length === 0) {
throw Error("EMPTY_AGGREGATE_ARRAY"); throw new EmptyAggregateError();
} }
const context = getContext(); const context = getContext();

View File

@ -3,6 +3,7 @@ import {describeDirectorySpecTest, InputType} from "@chainsafe/lodestar-spec-tes
import {bytesToHex, hexToBytes} from "../../src/helpers"; import {bytesToHex, hexToBytes} from "../../src/helpers";
import {SPEC_TESTS_DIR} from "../params"; import {SPEC_TESTS_DIR} from "../params";
import {describeForAllImplementations} from "../switch"; import {describeForAllImplementations} from "../switch";
import {EmptyAggregateError, ZeroSignatureError} from "../../src/errors";
interface IAggregateSigsTestCase { interface IAggregateSigsTestCase {
data: { data: {
@ -21,7 +22,7 @@ describeForAllImplementations((bls) => {
const agg = bls.aggregateSignatures(signatures.map(hexToBytes)); const agg = bls.aggregateSignatures(signatures.map(hexToBytes));
return bytesToHex(agg); return bytesToHex(agg);
} catch (e) { } catch (e) {
if (e.message === "EMPTY_AGGREGATE_ARRAY") return null; if (e instanceof EmptyAggregateError) return null;
throw e; throw e;
} }
}, },