Define common implementation

This commit is contained in:
dapplion 2020-11-20 19:03:17 +00:00
parent c354386dab
commit 4424bed87d
19 changed files with 143 additions and 96 deletions

View File

@ -4,6 +4,7 @@ import {PrivateKey} from "./privateKey";
import {PublicKey} from "./publicKey"; import {PublicKey} from "./publicKey";
import {Signature} from "./signature"; import {Signature} from "./signature";
import {toBuffer} from "../helpers/utils"; import {toBuffer} from "../helpers/utils";
import {IBls} from "../interface";
export * from "../constants"; export * from "../constants";
export {Keypair, PrivateKey, PublicKey, Signature}; export {Keypair, PrivateKey, PublicKey, Signature};
@ -15,23 +16,6 @@ export function destroy(): void {
// Native bindings require no destroy() call // Native bindings require no destroy() call
} }
/**
* Generates new secret and public key
*/
export function generateKeyPair(): Keypair {
return Keypair.generate();
}
/**
* Generates public key from given secret.
* @param {BLSSecretKey} secretKey
*/
export function generatePublicKey(secretKey: Uint8Array): Buffer {
assert(secretKey, "secretKey is null or undefined");
const keypair = new Keypair(PrivateKey.fromBytes(toBuffer(secretKey)));
return keypair.publicKey.toBytes();
}
/** /**
* Signs given message using secret key. * Signs given message using secret key.
* @param secretKey * @param secretKey
@ -127,9 +111,7 @@ export function verifyMultiple(publicKeys: Uint8Array[], messageHashes: Uint8Arr
} }
} }
export default { const bls: IBls = {
generateKeyPair,
generatePublicKey,
sign, sign,
aggregateSignatures, aggregateSignatures,
aggregatePubkeys, aggregatePubkeys,
@ -137,10 +119,12 @@ export default {
verifyAggregate, verifyAggregate,
verifyMultiple, verifyMultiple,
Keypair, // Keypair,
PrivateKey, PrivateKey,
PublicKey, PublicKey,
Signature, Signature,
initBLS, initBLS,
destroy, destroy,
}; };
export default bls;

View File

@ -1,7 +1,8 @@
import {PublicKey} from "./publicKey"; import {PublicKey} from "./publicKey";
import {PrivateKey} from "./privateKey"; import {PrivateKey} from "./privateKey";
import {IKeypair} from "../interface";
export class Keypair { export class Keypair implements IKeypair {
private readonly _publicKey: PublicKey; private readonly _publicKey: PublicKey;
private readonly _privateKey: PrivateKey; private readonly _privateKey: PrivateKey;

View File

@ -1,9 +1,10 @@
import * as blst from "@chainsafe/blst"; import * as blst from "@chainsafe/blst";
import {bytesToHex, getRandomBytes, hexToBytes} from "../helpers/utils"; import {bytesToHex, getRandomBytes, hexToBytes} from "../helpers/utils";
import {IPrivateKey} from "../interface";
import {PublicKey} from "./publicKey"; import {PublicKey} from "./publicKey";
import {Signature} from "./signature"; import {Signature} from "./signature";
export class PrivateKey { export class PrivateKey implements IPrivateKey {
readonly value: blst.SecretKey; readonly value: blst.SecretKey;
constructor(value: blst.SecretKey) { constructor(value: blst.SecretKey) {
@ -25,7 +26,7 @@ export class PrivateKey {
} }
signMessage(message: Uint8Array): Signature { signMessage(message: Uint8Array): Signature {
return Signature.fromValue(this.value.sign(message)); return new Signature(this.value.sign(message));
} }
toPublicKey(): PublicKey { toPublicKey(): PublicKey {

View File

@ -1,8 +1,9 @@
import * as blst from "@chainsafe/blst"; import * as blst from "@chainsafe/blst";
import {bytesToHex, hexToBytes} from "../helpers/utils"; import {bytesToHex, hexToBytes} from "../helpers/utils";
import {IPublicKey} from "../interface";
import {Signature} from "./signature"; import {Signature} from "./signature";
export class PublicKey { export class PublicKey implements IPublicKey {
readonly affine: blst.PublicKey; readonly affine: blst.PublicKey;
readonly jacobian: blst.AggregatePublicKey; readonly jacobian: blst.AggregatePublicKey;

View File

@ -1,8 +1,9 @@
import * as blst from "@chainsafe/blst"; import * as blst from "@chainsafe/blst";
import {bytesToHex, hexToBytes} from "../helpers/utils"; import {bytesToHex, hexToBytes} from "../helpers/utils";
import {ISignature} from "../interface";
import {PublicKey} from "./publicKey"; import {PublicKey} from "./publicKey";
export class Signature { export class Signature implements ISignature {
readonly affine: blst.Signature; readonly affine: blst.Signature;
constructor(value: blst.Signature) { constructor(value: blst.Signature) {
@ -17,10 +18,6 @@ export class Signature {
return this.fromBytes(hexToBytes(hex)); return this.fromBytes(hexToBytes(hex));
} }
static fromValue(signature: blst.Signature): Signature {
return new Signature(signature);
}
static aggregate(signatures: Signature[]): Signature { static aggregate(signatures: Signature[]): Signature {
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());

View File

@ -3,18 +3,17 @@ import bls from "@chainsafe/eth2-bls-wasm";
type Bls = typeof bls; type Bls = typeof bls;
let blsGlobal: Bls | null = null; let blsGlobal: Bls | null = null;
let blsGlobalPromise: Promise<Bls> | null = null; let blsGlobalPromise: Promise<void> | null = null;
export async function setupBls(): Promise<Bls> { export async function setupBls(): Promise<void> {
if (!blsGlobal) { if (!blsGlobal) {
await bls.init(); await bls.init();
blsGlobal = bls; blsGlobal = bls;
} }
return blsGlobal;
} }
// Cache a promise for Bls instead of Bls to make sure it is initialized only once // Cache a promise for Bls instead of Bls to make sure it is initialized only once
export async function initBLS(): Promise<Bls> { export async function initBLS(): Promise<void> {
if (!blsGlobalPromise) { if (!blsGlobalPromise) {
blsGlobalPromise = setupBls(); blsGlobalPromise = setupBls();
} }

View File

@ -5,26 +5,10 @@ import {Signature} from "./signature";
import {initBLS, destroy} from "./context"; import {initBLS, destroy} from "./context";
import assert from "assert"; import assert from "assert";
import {toBuffer} from "../helpers/utils"; import {toBuffer} from "../helpers/utils";
import {IBls} from "../interface";
export {Keypair, PrivateKey, PublicKey, Signature, initBLS, destroy}; export {Keypair, PrivateKey, PublicKey, Signature, initBLS, destroy};
/**
* Generates new secret and public key
*/
export function generateKeyPair(): Keypair {
return Keypair.generate();
}
/**
* Generates public key from given secret.
* @param {BLSSecretKey} secretKey
*/
export function generatePublicKey(secretKey: Uint8Array): Buffer {
assert(secretKey, "secretKey is null or undefined");
const keypair = new Keypair(PrivateKey.fromBytes(toBuffer(secretKey)));
return keypair.publicKey.toBytes();
}
/** /**
* Signs given message using secret key. * Signs given message using secret key.
* @param secretKey * @param secretKey
@ -121,9 +105,7 @@ export function verifyMultiple(publicKeys: Uint8Array[], messageHashes: Uint8Arr
} }
} }
export default { const bls: IBls = {
generateKeyPair,
generatePublicKey,
sign, sign,
aggregateSignatures, aggregateSignatures,
aggregatePubkeys, aggregatePubkeys,
@ -131,10 +113,12 @@ export default {
verifyAggregate, verifyAggregate,
verifyMultiple, verifyMultiple,
Keypair, // Keypair,
PrivateKey, PrivateKey,
PublicKey, PublicKey,
Signature, Signature,
initBLS, initBLS,
destroy, destroy,
}; };
export default bls;

View File

@ -1,7 +1,8 @@
import {PublicKey} from "./publicKey"; import {PublicKey} from "./publicKey";
import {PrivateKey} from "./privateKey"; import {PrivateKey} from "./privateKey";
import {IKeypair} from "../interface";
export class Keypair { export class Keypair implements IKeypair {
private readonly _publicKey: PublicKey; private readonly _publicKey: PublicKey;
private readonly _privateKey: PrivateKey; private readonly _privateKey: PrivateKey;

View File

@ -6,8 +6,9 @@ import {getContext} from "./context";
import {PublicKey} from "./publicKey"; import {PublicKey} from "./publicKey";
import {Signature} from "./signature"; import {Signature} from "./signature";
import {bytesToHex, hexToBytes} from "../helpers/utils"; import {bytesToHex, hexToBytes} from "../helpers/utils";
import {IPrivateKey} from "../interface";
export class PrivateKey { export class PrivateKey implements IPrivateKey {
readonly value: SecretKeyType; readonly value: SecretKeyType;
constructor(value: SecretKeyType) { constructor(value: SecretKeyType) {

View File

@ -3,8 +3,9 @@ import {getContext} from "./context";
import {EMPTY_PUBLIC_KEY} from "../constants"; import {EMPTY_PUBLIC_KEY} from "../constants";
import {Signature} from "./signature"; import {Signature} from "./signature";
import {bytesToHex, hexToBytes, isEqualBytes} from "../helpers/utils"; import {bytesToHex, hexToBytes, isEqualBytes} from "../helpers/utils";
import {IPublicKey} from "../interface";
export class PublicKey { export class PublicKey implements IPublicKey {
readonly value: PublicKeyType; readonly value: PublicKeyType;
constructor(value: PublicKeyType) { constructor(value: PublicKeyType) {

View File

@ -4,8 +4,9 @@ import {SignatureType} from "@chainsafe/eth2-bls-wasm";
import {getContext} from "./context"; import {getContext} from "./context";
import {PublicKey} from "./publicKey"; import {PublicKey} from "./publicKey";
import {bytesToHex, hexToBytes, isEqualBytes} from "../helpers/utils"; import {bytesToHex, hexToBytes, isEqualBytes} from "../helpers/utils";
import {ISignature} from "../interface";
export class Signature { export class Signature implements ISignature {
readonly value: SignatureType; readonly value: SignatureType;
constructor(value: SignatureType) { constructor(value: SignatureType) {

53
src/interface.ts Normal file
View File

@ -0,0 +1,53 @@
export interface IBls {
PrivateKey: {
fromBytes(bytes: Uint8Array): IPrivateKey;
fromHex(hex: string): IPrivateKey;
fromKeygen(): IPrivateKey;
};
PublicKey: {
fromBytes(bytes: Uint8Array): IPublicKey;
fromHex(hex: string): IPublicKey;
aggregate(pubkeys: IPublicKey[]): IPublicKey;
};
Signature: {
fromBytes(bytes: Uint8Array): ISignature;
fromHex(hex: string): ISignature;
aggregate(signatures: ISignature[]): ISignature;
};
sign(secretKey: Uint8Array, messageHash: Uint8Array): Buffer;
aggregatePubkeys(publicKeys: Uint8Array[]): Buffer;
aggregateSignatures(signatures: Uint8Array[]): Buffer;
verify(publicKey: Uint8Array, messageHash: Uint8Array, signature: Uint8Array): boolean;
verifyAggregate(publicKeys: Uint8Array[], messageHash: Uint8Array, signature: Uint8Array): boolean;
verifyMultiple(publicKeys: Uint8Array[], messageHashes: Uint8Array[], signature: Uint8Array): boolean;
initBLS: () => Promise<void>;
destroy: () => void;
}
export interface IKeypair {
publicKey: IPublicKey;
privateKey: IPrivateKey;
}
export interface IPublicKey {
toBytes(): Buffer;
toHex(): string;
}
export interface ISignature {
toBytes(): Buffer;
toHex(): string;
verify(publicKey: IPublicKey, message: Uint8Array): boolean;
verifyAggregate(publicKeys: IPublicKey[], message: Uint8Array): boolean;
verifyMultiple(publicKeys: IPublicKey[], messages: Uint8Array[]): boolean;
}
export interface IPrivateKey {
value: any;
toPublicKey(): IPublicKey;
signMessage(message: Uint8Array): ISignature;
toBytes(): Buffer;
toHex(): string;
}

View File

@ -1,11 +1,11 @@
import blst from "../src/blst"; import blst from "../src/blst";
import herumi from "../src/herumi"; import herumi from "../src/herumi";
import {IBls} from "../src/interface";
export type Implementation = "blst" | "herumi"; export type Implementation = "blst" | "herumi";
export const implementations: Implementation[] = ["blst", "herumi"];
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function getBls(implementation: Implementation) { export function getBls(implementation: Implementation): IBls {
switch (implementation) { switch (implementation) {
case "blst": case "blst":
return blst; return blst;
@ -15,17 +15,16 @@ export function getBls(implementation: Implementation) {
} }
export function forEachImplementation( export function forEachImplementation(
implementations: Implementation[],
callback: (bls: ReturnType<typeof getBls>, implementation: Implementation) => void callback: (bls: ReturnType<typeof getBls>, implementation: Implementation) => void
): void { ): void {
for (const implementation of implementations) { for (const implementation of implementations) {
describe(implementation, () => { describe(implementation, () => {
const bls = getBls(implementation); const bls = getBls(implementation);
if (implementation === "herumi") {
before(async () => { before(async () => {
await bls.initBLS(); await bls.initBLS();
}); });
}
callback(bls, implementation); callback(bls, implementation);
}); });

View File

@ -1,8 +1,8 @@
import {expect} from "chai"; import {expect} from "chai";
import {forEachImplementation} from "../switch"; import {IBls} from "../../src/interface";
import {getN, randomMessage} from "../util"; import {getN, randomMessage} from "../util";
forEachImplementation((bls) => { export function runIndexTests(bls: IBls) {
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
function getRandomData() { function getRandomData() {
const sk = bls.PrivateKey.fromKeygen(); const sk = bls.PrivateKey.fromKeygen();
@ -95,4 +95,4 @@ forEachImplementation((bls) => {
expect(isValid).to.be.false; expect(isValid).to.be.false;
}); });
}); });
}); }

View File

@ -1,27 +1,27 @@
import {expect} from "chai"; // import {expect} from "chai";
import {forEachImplementation} from "../switch"; // import {IBls} from "../../src/interface";
forEachImplementation((bls) => { // export function runKeypairTests(bls: IBls) {
describe("Keypair", () => { // describe("Keypair", () => {
it("should create from private and public key", () => { // it("should create from private and public key", () => {
const sk = bls.PrivateKey.fromKeygen(); // const sk = bls.PrivateKey.fromKeygen();
const sk2 = bls.PrivateKey.fromKeygen(); // const sk2 = bls.PrivateKey.fromKeygen();
const pk = sk.toPublicKey(); // const pk = sk.toPublicKey();
const keypair = new bls.Keypair(sk as any, pk as any); // const keypair = new bls.Keypair(sk, pk);
expect(keypair.publicKey).to.be.equal(pk); // expect(keypair.publicKey).to.be.equal(pk);
expect(keypair.privateKey).to.be.equal(sk); // expect(keypair.privateKey).to.be.equal(sk);
expect(keypair.privateKey).to.not.be.equal(sk2); // expect(keypair.privateKey).to.not.be.equal(sk2);
}); // });
it("should create from PrivateKey", () => { // it("should create from PrivateKey", () => {
const sk = bls.PrivateKey.fromKeygen(); // const sk = bls.PrivateKey.fromKeygen();
const pk = sk.toPublicKey(); // const pk = sk.toPublicKey();
const keypair = new bls.Keypair(sk as any); // const keypair = new bls.Keypair(sk as any);
expect(keypair.publicKey.toHex()).to.equal(pk.toHex()); // expect(keypair.publicKey.toHex()).to.equal(pk.toHex());
}); // });
}); // });
}); // }

View File

@ -1,7 +1,7 @@
import {expect} from "chai"; import {expect} from "chai";
import {forEachImplementation} from "../switch"; import {IBls} from "../../src/interface";
forEachImplementation((bls) => { export function runPrivateKeyTests(bls: IBls) {
describe("PrivateKey", () => { describe("PrivateKey", () => {
it("should generate fromKeygen private key", () => { it("should generate fromKeygen private key", () => {
const privateKey1 = bls.PrivateKey.fromKeygen(); const privateKey1 = bls.PrivateKey.fromKeygen();
@ -23,4 +23,4 @@ forEachImplementation((bls) => {
expect(() => bls.PrivateKey.fromHex("0x2123")).to.throw(); expect(() => bls.PrivateKey.fromHex("0x2123")).to.throw();
}); });
}); });
}); }

View File

@ -1,7 +1,7 @@
import {expect} from "chai"; import {expect} from "chai";
import {forEachImplementation} from "../switch"; import {IBls} from "../../src/interface";
forEachImplementation((bls) => { export function runPublicKeyTests(bls: IBls) {
describe("PublicKey", () => { describe("PublicKey", () => {
const publicKey = const publicKey =
"0xb6f21199594b56d77670564bf422cb331d5281ca2c1f9a45588a56881d8287ef8619efa6456d6cd2ef61306aa5b21311"; "0xb6f21199594b56d77670564bf422cb331d5281ca2c1f9a45588a56881d8287ef8619efa6456d6cd2ef61306aa5b21311";
@ -18,4 +18,4 @@ forEachImplementation((bls) => {
bls.PrivateKey.fromKeygen().toPublicKey(); bls.PrivateKey.fromKeygen().toPublicKey();
}); });
}); });
}); }

View File

@ -0,0 +1,12 @@
import {runPrivateKeyTests} from "./privateKey.test";
import {runPublicKeyTests} from "./publicKey.test";
// import {runKeypairTests} from "./keypair.test";
import {runIndexTests} from "./index.test";
import {forEachImplementation} from "../switch";
forEachImplementation(["blst", "herumi"], (bls) => {
runPrivateKeyTests(bls);
runPublicKeyTests(bls);
// runKeypairTests(bls);
runIndexTests(bls);
});

View File

@ -0,0 +1,12 @@
import {runPrivateKeyTests} from "./privateKey.test";
import {runPublicKeyTests} from "./publicKey.test";
// import {runKeypairTests} from "./keypair.test";
import {runIndexTests} from "./index.test";
import {forEachImplementation} from "../switch";
forEachImplementation(["herumi"], (bls) => {
runPrivateKeyTests(bls);
runPublicKeyTests(bls);
// runKeypairTests(bls);
runIndexTests(bls);
});