rewritten bls to use wasm
This commit is contained in:
parent
d5d8284be5
commit
2d0725a5cc
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@chainsafe/bls",
|
||||
"version": "0.1.7",
|
||||
"version": "0.2.0",
|
||||
"description": "Implementation of bls signature verification for ethereum 2.0",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
|
@ -23,7 +23,7 @@
|
|||
"build": "yarn build-lib && yarn build-types",
|
||||
"build:release": "yarn clean && yarn build && yarn build-web",
|
||||
"build-lib": "babel src -x .ts -d lib",
|
||||
"build-types": "tsc --declaration --incremental --outDir lib --emitDeclarationOnly",
|
||||
"build-types": "tsc --declaration --project tsconfig.build.json --incremental --outDir lib --emitDeclarationOnly",
|
||||
"build-web": "webpack --mode production --entry ./lib/web.js --output ./dist/bls.min.js",
|
||||
"check-types": "tsc --noEmit",
|
||||
"lint": "eslint --ext .ts src/",
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
declare module "secure-random" {
|
||||
export function randomBuffer(length: number): Buffer;
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
import CTX from "@chainsafe/milagro-crypto-js";
|
||||
|
||||
export default new CTX("BLS381");
|
|
@ -1,14 +0,0 @@
|
|||
import {G1point} from "./g1point";
|
||||
import {G2point} from "./g2point";
|
||||
import {FP12} from "@chainsafe/milagro-crypto-js/src/fp12";
|
||||
import ctx from "../ctx";
|
||||
|
||||
|
||||
export class ElipticCurvePairing {
|
||||
|
||||
public static pair(p1: G1point, p2: G2point): FP12 {
|
||||
const e = ctx.PAIR.ate(p2.getPoint(), p1.getPoint());
|
||||
return ctx.PAIR.fexp(e);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,134 +0,0 @@
|
|||
import {BIG} from "@chainsafe/milagro-crypto-js/src/big";
|
||||
import {ECP} from "@chainsafe/milagro-crypto-js/src/ecp";
|
||||
import ctx from "../ctx";
|
||||
import assert from "assert";
|
||||
import {calculateYFlag, getModulus} from "./utils";
|
||||
import * as random from "secure-random";
|
||||
import {FP_POINT_LENGTH} from "../constants";
|
||||
import {bytes48} from "@chainsafe/eth2.0-types";
|
||||
|
||||
export class G1point {
|
||||
|
||||
private point: ECP;
|
||||
|
||||
public constructor(point: ECP) {
|
||||
this.point = point;
|
||||
}
|
||||
|
||||
public static fromBytesCompressed(value: bytes48): G1point {
|
||||
assert(value.length === FP_POINT_LENGTH, `Expected g1 compressed input to have ${FP_POINT_LENGTH} bytes`);
|
||||
value = Buffer.from(value);
|
||||
const aIn = (value[0] & (1 << 5)) != 0;
|
||||
const bIn = (value[0] & (1 << 6)) != 0;
|
||||
const cIn = (value[0] & (1 << 7)) != 0;
|
||||
value[0] &= 31;
|
||||
|
||||
if (!cIn) {
|
||||
throw new Error("The serialised input does not have the C flag set.");
|
||||
}
|
||||
|
||||
const x = ctx.BIG.frombytearray(value, 0);
|
||||
if (bIn) {
|
||||
if (!aIn && x.iszilch()) {
|
||||
// This is a correctly formed serialisation of infinity
|
||||
return new G1point(new ctx.ECP());
|
||||
} else {
|
||||
// The input is malformed
|
||||
throw new Error(
|
||||
"The serialised input has B flag set, but A flag is set, or X is non-zero.");
|
||||
}
|
||||
}
|
||||
const modulus = getModulus();
|
||||
if (ctx.BIG.comp(modulus, x) <= 0) {
|
||||
throw new Error("X coordinate is too large.");
|
||||
}
|
||||
|
||||
const point = new ctx.ECP();
|
||||
point.setx(x);
|
||||
|
||||
if (point.is_infinity()) {
|
||||
throw new Error("X coordinate is not on the curve.");
|
||||
}
|
||||
|
||||
// Did we get the right branch of the sqrt?
|
||||
if (!point.is_infinity() && aIn != calculateYFlag(point.getY())) {
|
||||
// We didn't: so choose the other branch of the sqrt.
|
||||
const x = new ctx.FP(point.getX());
|
||||
const yneg = new ctx.FP(point.getY());
|
||||
yneg.neg();
|
||||
point.setxy(x.redc(), yneg.redc());
|
||||
}
|
||||
|
||||
return new G1point(point);
|
||||
}
|
||||
|
||||
public static aggregate(values: bytes48[]): G1point {
|
||||
return values.map((value) => {
|
||||
return G1point.fromBytesCompressed(value);
|
||||
}).reduce((previousValue, currentValue): G1point => {
|
||||
return previousValue.add(currentValue);
|
||||
});
|
||||
}
|
||||
|
||||
public static generator(): G1point {
|
||||
return new G1point(ctx.ECP.generator());
|
||||
}
|
||||
|
||||
public static random(): G1point {
|
||||
let ecp: ECP;
|
||||
do {
|
||||
ecp = new ctx.ECP();
|
||||
ecp.setx(
|
||||
ctx.BIG.frombytearray(
|
||||
random.randomBuffer(FP_POINT_LENGTH),
|
||||
0
|
||||
)
|
||||
);
|
||||
} while (ecp.is_infinity());
|
||||
return new G1point(ecp);
|
||||
}
|
||||
|
||||
public mul(value: BIG): G1point {
|
||||
const newPoint = this.point.mul(value);
|
||||
return new G1point(newPoint);
|
||||
}
|
||||
|
||||
public add(other: G1point): G1point {
|
||||
const sum = new ctx.ECP();
|
||||
sum.add(this.point);
|
||||
sum.add(other.point);
|
||||
sum.affine();
|
||||
return new G1point(sum);
|
||||
}
|
||||
|
||||
public addRaw(other: bytes48): G1point {
|
||||
return this.add(G1point.fromBytesCompressed(other));
|
||||
}
|
||||
|
||||
public equal(other: G1point): boolean {
|
||||
return this.point.equals(other.point);
|
||||
}
|
||||
|
||||
public toBytes(): bytes48 {
|
||||
const buffer = Buffer.alloc(FP_POINT_LENGTH, 0);
|
||||
this.point.getX().tobytearray(buffer, 0);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public getPoint(): ECP {
|
||||
return this.point;
|
||||
}
|
||||
|
||||
public toBytesCompressed(): bytes48 {
|
||||
const output = this.toBytes();
|
||||
const c = true;
|
||||
const b = this.point.is_infinity();
|
||||
const a = !b && calculateYFlag(this.point.getY());
|
||||
|
||||
const flags = ((a ? 1 << 5 : 0) | (b ? 1 << 6 : 0) | (c ? 1 << 7 : 0));
|
||||
const mask = 31;
|
||||
output[0] &= mask;
|
||||
output[0] |= flags;
|
||||
return output;
|
||||
}
|
||||
}
|
|
@ -1,250 +0,0 @@
|
|||
import {BIG} from "@chainsafe/milagro-crypto-js/src/big";
|
||||
import {ECP2} from "@chainsafe/milagro-crypto-js/src/ecp2";
|
||||
import {sha256} from "js-sha256";
|
||||
import ctx from "../ctx";
|
||||
import * as random from "secure-random";
|
||||
import {calculateYFlag, getModulus, padLeft} from "./utils";
|
||||
import assert from "assert";
|
||||
import {FP_POINT_LENGTH, G2_HASH_PADDING} from "../constants";
|
||||
import {bytes48, Domain, Hash} from "@chainsafe/eth2.0-types";
|
||||
|
||||
export class G2point {
|
||||
|
||||
private point: ECP2;
|
||||
|
||||
public constructor(point: ECP2) {
|
||||
this.point = point;
|
||||
}
|
||||
|
||||
|
||||
public static hashToG2(message: Hash, domain: Domain): G2point {
|
||||
const padding = Buffer.alloc(G2_HASH_PADDING, 0);
|
||||
const xReBytes = Buffer.concat([
|
||||
padding,
|
||||
Buffer.from(sha256.arrayBuffer(
|
||||
Buffer.concat([
|
||||
message,
|
||||
padLeft(domain, 8),
|
||||
Buffer.from("01", "hex")
|
||||
])
|
||||
))
|
||||
]);
|
||||
const xImBytes = Buffer.concat([
|
||||
padding,
|
||||
Buffer.from(sha256.arrayBuffer(
|
||||
Buffer.concat([
|
||||
message,
|
||||
padLeft(domain, 8),
|
||||
Buffer.from("02", "hex")
|
||||
])
|
||||
))
|
||||
]);
|
||||
const xRe = ctx.BIG.frombytearray(xReBytes, 0);
|
||||
const xIm = ctx.BIG.frombytearray(xImBytes, 0);
|
||||
const one = new ctx.BIG(1);
|
||||
let point = new ctx.ECP2();
|
||||
point.setx(new ctx.FP2(xRe, xIm));
|
||||
while (point.is_infinity()) {
|
||||
xRe.add(one);
|
||||
xRe.norm();
|
||||
point = new ctx.ECP2();
|
||||
point.setx(new ctx.FP2(xRe, xIm));
|
||||
}
|
||||
return new G2point(G2point.scaleWithCofactor(G2point.normaliseY(point)));
|
||||
}
|
||||
|
||||
public static fromCompressedBytes(value: bytes48): G2point {
|
||||
assert(value.length === 2 * FP_POINT_LENGTH, "Expected signature of 96 bytes");
|
||||
value = Buffer.from(value);
|
||||
const xImBytes = value.slice(0, FP_POINT_LENGTH);
|
||||
const xReBytes = value.slice(FP_POINT_LENGTH);
|
||||
const aIn = (xImBytes[0] & (1 << 5)) != 0;
|
||||
const bIn = (xImBytes[0] & (1 << 6)) != 0;
|
||||
const cIn = (xImBytes[0] & (1 << 7)) != 0;
|
||||
//clear bits
|
||||
xImBytes[0] &= 31;
|
||||
if((xReBytes[0] & 224) != 0) {
|
||||
throw new Error("The input has non-zero a2, b2 or c2 flag on xRe");
|
||||
}
|
||||
if(!cIn) {
|
||||
throw new Error("The serialised input does not have the C flag set.");
|
||||
}
|
||||
const xIm = ctx.BIG.frombytearray(xImBytes, 0);
|
||||
const xRe = ctx.BIG.frombytearray(xReBytes, 0);
|
||||
if (bIn) {
|
||||
if (!aIn
|
||||
&& xIm.iszilch()
|
||||
&& xRe.iszilch() ) {
|
||||
// This is a correctly formed serialisation of infinity
|
||||
return new G2point(new ctx.ECP2());
|
||||
} else {
|
||||
// The input is malformed
|
||||
throw new Error(
|
||||
"The serialised input has B flag set, but A flag is set, or X is non-zero.");
|
||||
}
|
||||
}
|
||||
|
||||
const modulus = getModulus();
|
||||
if(ctx.BIG.comp(modulus, xRe) <= 0 || ctx.BIG.comp(modulus, xIm) <= 0) {
|
||||
throw new Error(
|
||||
"The deserialised X real or imaginary coordinate is too large.");
|
||||
}
|
||||
|
||||
const point = new ctx.ECP2();
|
||||
point.setx(new ctx.FP2(xRe, xIm));
|
||||
if(point.is_infinity()) {
|
||||
throw new Error("X coordinate is not on the curve.");
|
||||
}
|
||||
|
||||
if (!point.is_infinity() && aIn != calculateYFlag(point.getY().getB())) {
|
||||
// We didn't: so choose the other branch of the sqrt.
|
||||
const x = point.getX();
|
||||
const yneg = point.getY();
|
||||
yneg.neg();
|
||||
point.setxy(x, yneg);
|
||||
}
|
||||
|
||||
return new G2point(point);
|
||||
}
|
||||
|
||||
public static fromUncompressedInput(
|
||||
xReBytes: Buffer,
|
||||
xImBytes: Buffer,
|
||||
yReBytes: Buffer,
|
||||
yImBytes: Buffer,
|
||||
zReBytes: Buffer,
|
||||
zImBytes: Buffer): G2point {
|
||||
const xRe = ctx.BIG.frombytearray(padLeft(xReBytes, FP_POINT_LENGTH), 0);
|
||||
const xIm = ctx.BIG.frombytearray(padLeft(xImBytes, FP_POINT_LENGTH), 0);
|
||||
const yRe = ctx.BIG.frombytearray(padLeft(yReBytes, FP_POINT_LENGTH), 0);
|
||||
const yIm = ctx.BIG.frombytearray(padLeft(yImBytes, FP_POINT_LENGTH), 0);
|
||||
const zRe = ctx.BIG.frombytearray(padLeft(zReBytes, FP_POINT_LENGTH), 0);
|
||||
const zIm = ctx.BIG.frombytearray(padLeft(zImBytes, FP_POINT_LENGTH), 0);
|
||||
const x = new ctx.FP2(xRe, xIm);
|
||||
const y = new ctx.FP2(yRe, yIm);
|
||||
const z = new ctx.FP2(zRe, zIm);
|
||||
z.inverse();
|
||||
x.mul(z);
|
||||
x.reduce();
|
||||
y.mul(z);
|
||||
y.reduce();
|
||||
const point = new ctx.ECP2();
|
||||
point.setxy(x, y);
|
||||
return new G2point(point);
|
||||
}
|
||||
|
||||
public static random(): G2point {
|
||||
let point: ECP2;
|
||||
do {
|
||||
point = new ctx.ECP2();
|
||||
point.setx(
|
||||
new ctx.FP2(
|
||||
ctx.BIG.frombytearray(
|
||||
random.randomBuffer(FP_POINT_LENGTH),
|
||||
0
|
||||
),
|
||||
ctx.BIG.frombytearray(
|
||||
random.randomBuffer(FP_POINT_LENGTH),
|
||||
0
|
||||
)
|
||||
)
|
||||
);
|
||||
} while (point.is_infinity());
|
||||
return new G2point(point);
|
||||
}
|
||||
|
||||
public static scaleWithCofactor(point: ECP2): ECP2 {
|
||||
const upper = ctx.BIG.frombytearray(
|
||||
Buffer.from(
|
||||
"0000000000000000000000000000000005d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddf",
|
||||
"hex"
|
||||
),
|
||||
0
|
||||
);
|
||||
const lower = ctx.BIG.frombytearray(
|
||||
Buffer.from(
|
||||
"00000000000000000000000000000000a628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5",
|
||||
"hex"
|
||||
),
|
||||
0
|
||||
);
|
||||
const shift = ctx.BIG.frombytearray(
|
||||
Buffer.from(
|
||||
"000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000",
|
||||
"hex"
|
||||
),
|
||||
0
|
||||
);
|
||||
let sum = new ctx.ECP2();
|
||||
sum.copy(point);
|
||||
sum = sum.mul(upper);
|
||||
sum = sum.mul(shift);
|
||||
|
||||
let tmp = new ctx.ECP2();
|
||||
tmp.copy(point);
|
||||
tmp = tmp.mul(lower);
|
||||
|
||||
sum.add(tmp);
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
public static normaliseY(point: ECP2): ECP2 {
|
||||
const y = point.getY();
|
||||
const yNeg = new ctx.FP2(y);
|
||||
yNeg.neg();
|
||||
if (ctx.BIG.comp(y.getB(), yNeg.getB()) < 0
|
||||
|| ((ctx.BIG.comp(y.getB(), yNeg.getB()) == 0)
|
||||
&& ctx.BIG.comp(y.getA(), yNeg.getA()) < 0)
|
||||
) {
|
||||
const newPoint = new ctx.ECP2();
|
||||
newPoint.setxy(point.getX(), yNeg);
|
||||
return newPoint;
|
||||
} else {
|
||||
return point;
|
||||
}
|
||||
}
|
||||
|
||||
public add(other: G2point): G2point {
|
||||
const sum = new ctx.ECP2();
|
||||
sum.add(this.point);
|
||||
sum.add(other.point);
|
||||
sum.affine();
|
||||
return new G2point(sum);
|
||||
}
|
||||
|
||||
public mul(value: BIG): G2point {
|
||||
const newPoint = this.point.mul(value);
|
||||
return new G2point(newPoint);
|
||||
}
|
||||
|
||||
public equal(other: G2point): boolean {
|
||||
return this.point.equals(other.point);
|
||||
}
|
||||
|
||||
public getPoint(): ECP2 {
|
||||
return this.point;
|
||||
}
|
||||
|
||||
public toBytesCompressed(): Buffer {
|
||||
const xReBytes = Buffer.alloc(FP_POINT_LENGTH, 0);
|
||||
const xImBytes = Buffer.alloc(FP_POINT_LENGTH, 0);
|
||||
this.point.getX().getA().tobytearray(xReBytes, 0);
|
||||
this.point.getX().getB().tobytearray(xImBytes, 0);
|
||||
const c1 = true;
|
||||
const b1 = this.point.is_infinity();
|
||||
const a1 = !b1 && calculateYFlag(this.point.getY().getB());
|
||||
|
||||
const flags = ((a1 ? 1 << 5 : 0) | (b1 ? 1 << 6 : 0) | (c1 ? 1 << 7 : 0));
|
||||
const mask = 31;
|
||||
xImBytes[0] &= mask;
|
||||
xImBytes[0] |= flags;
|
||||
xReBytes[0] &= mask;
|
||||
|
||||
return Buffer.concat([
|
||||
xImBytes,
|
||||
xReBytes
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,4 @@
|
|||
import assert from "assert";
|
||||
import {BIG} from "@chainsafe/milagro-crypto-js/src/big";
|
||||
import ctx from "../ctx";
|
||||
|
||||
/**
|
||||
* Pads byte array with zeroes on left side up to desired length.
|
||||
|
@ -14,21 +12,3 @@ export function padLeft(source: Buffer, length: number): Buffer {
|
|||
source.copy(result, length - source.length);
|
||||
return result;
|
||||
}
|
||||
|
||||
//TODO: find a way to convert ctx.ROM_FIELD.MODULUS to BIG (MODULUS basebit = 58, BIG basebit=23
|
||||
export function getModulus(): BIG {
|
||||
return ctx.BIG.frombytearray(
|
||||
Buffer.from(
|
||||
"1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab",
|
||||
"hex"
|
||||
),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
export function calculateYFlag(yIm: BIG): boolean {
|
||||
const tmp = new ctx.BIG(yIm);
|
||||
tmp.add(yIm);
|
||||
tmp.div(getModulus());
|
||||
return tmp.isunity();
|
||||
}
|
||||
|
|
91
src/index.ts
91
src/index.ts
|
@ -1,15 +1,17 @@
|
|||
import {Keypair} from "./keypair";
|
||||
import {PrivateKey} from "./privateKey";
|
||||
import {G2point} from "./helpers/g2point";
|
||||
import {G1point} from "./helpers/g1point";
|
||||
import {PublicKey} from "./publicKey";
|
||||
import {Signature} from "./signature";
|
||||
import {ElipticCurvePairing} from "./helpers/ec-pairing";
|
||||
import ctx from "./ctx";
|
||||
import {BLSPubkey, BLSSecretKey, BLSSignature, Domain, Hash} from "@chainsafe/eth2.0-types";
|
||||
|
||||
import {init} from "./context";
|
||||
import {PUBLIC_KEY_LENGTH} from "./constants";
|
||||
export {Keypair, PrivateKey, PublicKey, Signature};
|
||||
|
||||
|
||||
export async function initLibrary(): Promise<void> {
|
||||
await init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates new secret and public key
|
||||
*/
|
||||
|
@ -34,8 +36,7 @@ export function generatePublicKey(secretKey: BLSSecretKey): BLSPubkey {
|
|||
*/
|
||||
export function sign(secretKey: BLSSecretKey, messageHash: Hash, domain: Domain): BLSSignature {
|
||||
const privateKey = PrivateKey.fromBytes(secretKey);
|
||||
const hash = G2point.hashToG2(messageHash, domain);
|
||||
return privateKey.sign(hash).toBytesCompressed();
|
||||
return privateKey.signMessage(messageHash, domain).toBytesCompressed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -56,9 +57,16 @@ export function aggregateSignatures(signatures: BLSSignature[]): BLSSignature {
|
|||
*/
|
||||
export function aggregatePubkeys(publicKeys: BLSPubkey[]): BLSPubkey {
|
||||
if(publicKeys.length === 0) {
|
||||
return new G1point(new ctx.ECP()).toBytesCompressed();
|
||||
return Buffer.alloc(PUBLIC_KEY_LENGTH);
|
||||
}
|
||||
return G1point.aggregate(publicKeys).toBytesCompressed();
|
||||
return publicKeys.map(PublicKey.fromBytes).reduce((agg, pubKey) => {
|
||||
if(agg) {
|
||||
return agg.add(pubKey);
|
||||
} else {
|
||||
return pubKey;
|
||||
}
|
||||
}
|
||||
).toBytesCompressed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -70,15 +78,9 @@ export function aggregatePubkeys(publicKeys: BLSPubkey[]): BLSPubkey {
|
|||
*/
|
||||
export function verify(publicKey: BLSPubkey, messageHash: Hash, signature: BLSSignature, domain: Domain): boolean {
|
||||
try {
|
||||
const key = PublicKey.fromBytes(publicKey);
|
||||
const sig = Signature.fromCompressedBytes(signature);
|
||||
|
||||
key.getPoint().getPoint().affine();
|
||||
sig.getPoint().getPoint().affine();
|
||||
const g1Generated = G1point.generator();
|
||||
const e1 = ElipticCurvePairing.pair(key.getPoint(), G2point.hashToG2(messageHash, domain));
|
||||
const e2 = ElipticCurvePairing.pair(g1Generated, sig.getPoint());
|
||||
return e1.equals(e2);
|
||||
return PublicKey
|
||||
.fromBytes(publicKey)
|
||||
.verifyMessage(Signature.fromCompressedBytes(signature), messageHash, domain);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
@ -101,54 +103,13 @@ export function verifyMultiple(
|
|||
return false;
|
||||
}
|
||||
try {
|
||||
const sig = Signature.fromCompressedBytes(signature).getPoint();
|
||||
sig.getPoint().affine();
|
||||
|
||||
const eCombined = new ctx.FP12(1);
|
||||
|
||||
// @ts-ignore
|
||||
const reduction = messageHashes.reduce((previous, current, index) => {
|
||||
// @ts-ignore
|
||||
if(previous.hash && current.equals(previous.hash)) {
|
||||
return {
|
||||
hash: previous.hash,
|
||||
// @ts-ignore
|
||||
publicKey: previous.publicKey ?
|
||||
// @ts-ignore
|
||||
previous.publicKey.addRaw(publicKeys[index])
|
||||
:
|
||||
G1point.fromBytesCompressed(publicKeys[index]),
|
||||
};
|
||||
} else if(previous.hash) {
|
||||
// @ts-ignore
|
||||
const g2 = G2point.hashToG2(previous.hash, domain);
|
||||
eCombined.mul(
|
||||
ElipticCurvePairing.pair(
|
||||
// @ts-ignore
|
||||
previous.publicKey,
|
||||
g2
|
||||
)
|
||||
return Signature
|
||||
.fromCompressedBytes(signature)
|
||||
.verifyMultiple(
|
||||
publicKeys.map((key) => PublicKey.fromBytes(key)),
|
||||
messageHashes,
|
||||
domain
|
||||
);
|
||||
return {hash: current, publicKey: G1point.fromBytesCompressed(publicKeys[index])};
|
||||
} else {
|
||||
return {
|
||||
hash: current,
|
||||
publicKey: G1point.fromBytesCompressed(publicKeys[index])
|
||||
};
|
||||
}
|
||||
}, {hash: null, publicKey: null});
|
||||
|
||||
const g2Final = G2point.hashToG2(reduction.hash, domain);
|
||||
const keyFinal = reduction.publicKey;
|
||||
eCombined.mul(
|
||||
ElipticCurvePairing.pair(
|
||||
keyFinal,
|
||||
g2Final
|
||||
)
|
||||
);
|
||||
|
||||
const e2 = ElipticCurvePairing.pair(G1point.generator(), sig);
|
||||
return e2.equals(eCombined);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@ import {PrivateKey} from "./privateKey";
|
|||
|
||||
export class Keypair {
|
||||
|
||||
private _publicKey: PublicKey;
|
||||
private readonly _publicKey: PublicKey;
|
||||
|
||||
private _privateKey: PrivateKey;
|
||||
private readonly _privateKey: PrivateKey;
|
||||
|
||||
public constructor(privateKey: PrivateKey, publicKey?: PublicKey) {
|
||||
this._privateKey = privateKey;
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import {SECRET_KEY_LENGTH} from "./constants";
|
||||
import assert from "assert";
|
||||
import {BLSSecretKey, Domain, Hash} from "@chainsafe/eth2.0-types";
|
||||
import {SecretKeyType, SignatureType} from "@chainsafe/eth2-bls-wasm";
|
||||
import {SecretKeyType} from "@chainsafe/eth2-bls-wasm";
|
||||
import {getContext} from "./context";
|
||||
import {PublicKey} from "./publicKey";
|
||||
import {Signature} from "./signature";
|
||||
import {padLeft} from "./helpers/utils";
|
||||
|
||||
export class PrivateKey {
|
||||
|
||||
|
@ -38,12 +41,17 @@ export class PrivateKey {
|
|||
return this.value;
|
||||
}
|
||||
|
||||
public sign(message: Uint8Array): SignatureType {
|
||||
return this.value.sign(message);
|
||||
// public sign(message: Uint8Array): Signature {
|
||||
// return Signature.fromValue(this.value.sign(message));
|
||||
// }
|
||||
|
||||
public signMessage(message: Hash, domain: Domain): Signature {
|
||||
domain = padLeft(domain, 8);
|
||||
return Signature.fromValue(this.value.signHashWithDomain(Buffer.concat([message, domain])));
|
||||
}
|
||||
|
||||
public signMessage(message: Hash, domain: Domain): SignatureType {
|
||||
return this.value.signHashWithDomain(Buffer.concat([message, domain]));
|
||||
public toPublicKey(): PublicKey {
|
||||
return PublicKey.fromPublicKeyType(this.value.getPublicKey());
|
||||
}
|
||||
|
||||
public toBytes(): BLSSecretKey {
|
||||
|
|
|
@ -1,36 +1,64 @@
|
|||
import {G1point} from "./helpers/g1point";
|
||||
import {PrivateKey} from "./privateKey";
|
||||
import {BLSPubkey} from "@chainsafe/eth2.0-types";
|
||||
import {BLSPubkey, BLSSignature, Domain, Hash} from "@chainsafe/eth2.0-types";
|
||||
import {PublicKeyType} from "@chainsafe/eth2-bls-wasm";
|
||||
import {getContext} from "./context";
|
||||
import {PUBLIC_KEY_LENGTH} from "./constants";
|
||||
import assert from "assert";
|
||||
import {Signature} from "./signature";
|
||||
|
||||
export class PublicKey {
|
||||
|
||||
private point: G1point;
|
||||
private value: PublicKeyType;
|
||||
|
||||
public constructor(point: G1point) {
|
||||
this.point = point;
|
||||
protected constructor(value: PublicKeyType) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static fromPrivateKey(privateKey: PrivateKey): PublicKey {
|
||||
return privateKey.toPublicKey();
|
||||
}
|
||||
|
||||
public static fromBytes(bytes: BLSPubkey): PublicKey {
|
||||
const context = getContext();
|
||||
const publicKey = new context.PublicKey();
|
||||
publicKey.deserialize(bytes);
|
||||
return new PublicKey(
|
||||
G1point.generator().mul(privateKey.getValue())
|
||||
publicKey
|
||||
);
|
||||
}
|
||||
|
||||
public static fromBytes(publicKey: BLSPubkey): PublicKey {
|
||||
public static fromHex(value: string): PublicKey {
|
||||
value = value.replace("0x", "");
|
||||
assert(value.length === PUBLIC_KEY_LENGTH * 2);
|
||||
const context = getContext();
|
||||
return new PublicKey(
|
||||
G1point.fromBytesCompressed(publicKey)
|
||||
context.deserializeHexStrToPublicKey(value)
|
||||
);
|
||||
}
|
||||
|
||||
public getPoint(): G1point {
|
||||
return this.point;
|
||||
public static fromPublicKeyType(value: PublicKeyType): PublicKey {
|
||||
return new PublicKey(value);
|
||||
}
|
||||
|
||||
public add(other: PublicKey): PublicKey {
|
||||
const agg = new PublicKey(this.value.clone());
|
||||
agg.value.add(other.value);
|
||||
return agg;
|
||||
}
|
||||
|
||||
public verifyMessage(signature: Signature, messageHash: Hash, domain: Domain): boolean {
|
||||
return this.value.verifyHashWithDomain(signature.getValue(), Buffer.concat([messageHash, domain]));
|
||||
}
|
||||
|
||||
public toBytesCompressed(): BLSPubkey {
|
||||
return this.point.toBytesCompressed();
|
||||
return Buffer.from(this.value.serialize());
|
||||
}
|
||||
|
||||
public toHexString(): string {
|
||||
return `0x${this.toBytesCompressed().toString("hex")}`;
|
||||
}
|
||||
|
||||
public getValue(): PublicKeyType {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,35 +1,64 @@
|
|||
import {G2point} from "./helpers/g2point";
|
||||
import assert from "assert";
|
||||
import {FP_POINT_LENGTH} from "./constants";
|
||||
import {BLSSignature} from "@chainsafe/eth2.0-types";
|
||||
import {BLSSignature, Domain, Hash} from "@chainsafe/eth2.0-types";
|
||||
import {SignatureType} from "@chainsafe/eth2-bls-wasm";
|
||||
import {getContext} from "./context";
|
||||
import {PublicKey} from "./publicKey";
|
||||
import {padLeft} from "./helpers/utils";
|
||||
|
||||
export class Signature {
|
||||
|
||||
private point: G2point;
|
||||
private value: SignatureType;
|
||||
|
||||
public constructor(point: G2point) {
|
||||
this.point = point;
|
||||
protected constructor(value: SignatureType) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static fromCompressedBytes(signature: BLSSignature): Signature {
|
||||
public static fromCompressedBytes(value: BLSSignature): Signature {
|
||||
assert(
|
||||
signature.length === 2 * FP_POINT_LENGTH,
|
||||
value.length === 2 * FP_POINT_LENGTH,
|
||||
`Signature must have ${2 * FP_POINT_LENGTH} bytes`
|
||||
);
|
||||
return new Signature(G2point.fromCompressedBytes(signature));
|
||||
const context = getContext();
|
||||
const signature = new context.Signature();
|
||||
signature.deserialize(value);
|
||||
return new Signature(signature);
|
||||
}
|
||||
|
||||
public static fromValue(signature: SignatureType): Signature {
|
||||
return new Signature(signature);
|
||||
}
|
||||
|
||||
public add(other: Signature): Signature {
|
||||
const agg = this.value.clone();
|
||||
agg.add(other.value);
|
||||
return new Signature(
|
||||
this.point.add(other.point)
|
||||
agg
|
||||
);
|
||||
}
|
||||
|
||||
public getPoint(): G2point {
|
||||
return this.point;
|
||||
public getValue(): SignatureType {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public verify(publicKey: PublicKey, message: Hash, domain: Domain): boolean {
|
||||
domain = padLeft(domain, 8);
|
||||
return publicKey.verifyMessage(this, message, domain);
|
||||
}
|
||||
|
||||
public verifyMultiple(publicKeys: PublicKey[], messages: Hash[], domain: Domain): boolean {
|
||||
domain = padLeft(domain, 8);
|
||||
return this.value.verifyAggregatedHashWithDomain(
|
||||
publicKeys.map((key) => key.getValue()),
|
||||
messages.map((message) => Buffer.concat([message, domain]))
|
||||
);
|
||||
}
|
||||
|
||||
public toBytesCompressed(): BLSSignature {
|
||||
return this.point.toBytesCompressed();
|
||||
return Buffer.from(this.value.serialize());
|
||||
}
|
||||
|
||||
public toHex(): string {
|
||||
return "0x" + this.value.serializeToHexStr();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
// Import benchmarks
|
||||
import * as suites from "./suites";
|
||||
import {createReportDir, runSuite} from "@chainsafe/benchmark-utils";
|
||||
import {initLibrary} from "../../src";
|
||||
// Create file
|
||||
const directory: string = createReportDir();
|
||||
|
||||
|
||||
initLibrary().then(() => {
|
||||
// Run benchmarks
|
||||
Object.values(suites).forEach((suite) => {
|
||||
runSuite(suite(directory));
|
||||
});
|
||||
});
|
|
@ -1,5 +1,5 @@
|
|||
// export {verifyInValidSignatureBenchmark} from './verifyInValidSignature';
|
||||
// export {verifyValidSignatureBenchmark} from './verifyValidSignature';
|
||||
export {verifyInValidSignatureBenchmark} from './verifyInValidSignature';
|
||||
export {verifyValidSignatureBenchmark} from './verifyValidSignature';
|
||||
export {verifyValidAggregatedSignature} from './verifyValidAggregatedSignature';
|
||||
// export {verifyInvalidAggregatedSignature} from './verifyInvalidAggregatedSignature';
|
||||
// export {aggregateSignaturesBenchmark} from './signatureAggregation';
|
||||
export {verifyInvalidAggregatedSignature} from './verifyInvalidAggregatedSignature';
|
||||
export {aggregateSignaturesBenchmark} from './signatureAggregation';
|
|
@ -25,6 +25,7 @@ export function aggregateSignaturesBenchmark(dir: string): BenchSuite {
|
|||
};
|
||||
|
||||
return {
|
||||
name: FUNCTION_NAME,
|
||||
testFunctions: [aggregateSignatures],
|
||||
setup: function() {
|
||||
global.signatures = [];
|
||||
|
|
|
@ -27,6 +27,7 @@ export function verifyInValidSignatureBenchmark(dir: string): BenchSuite {
|
|||
};
|
||||
|
||||
return {
|
||||
name: FUNCTION_NAME,
|
||||
testFunctions: [verifyInValidSignature],
|
||||
setup: function() {
|
||||
const {Keypair} = require("../../../src");
|
||||
|
|
|
@ -27,6 +27,7 @@ export function verifyInvalidAggregatedSignature(dir: string): BenchSuite {
|
|||
};
|
||||
|
||||
return {
|
||||
name: FUNCTION_NAME,
|
||||
testFunctions: [verifyInvalidAggregatedSignature],
|
||||
setup: function() {
|
||||
const {Keypair, aggregateSignatures} = require("../../../src");
|
||||
|
|
|
@ -32,13 +32,13 @@ export function verifyValidAggregatedSignature(dir: string): BenchSuite {
|
|||
const FUNCTION_NAME = "verifyValidAggregatedSignature"; // PLEASE FILL THIS OUT
|
||||
|
||||
const verifyValidAggregatedSignature = function (): void {
|
||||
global.verify(global.publicKeys, global.messages, global.signature, global.domain)
|
||||
global.verify(global.publicKeys, global.messages, global.signature, global.domain);
|
||||
};
|
||||
|
||||
return {
|
||||
testFunctions: [verifyValidAggregatedSignature],
|
||||
setup: function() {
|
||||
const sha256 = require('js-sha256');
|
||||
const sha256 = require("js-sha256");
|
||||
const {aggregateSignatures} = require("../../../src");
|
||||
const message = Buffer.from(sha256.arrayBuffer(Math.random().toString(36)));
|
||||
const signatures = [];
|
||||
|
|
|
@ -29,6 +29,7 @@ export function verifyValidSignatureBenchmark(dir: string): BenchSuite {
|
|||
};
|
||||
|
||||
return {
|
||||
name: FUNCTION_NAME,
|
||||
testFunctions: [verifyValidSignature],
|
||||
setup: function() {
|
||||
const {Keypair} = require("../../../src");
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import bls from "../../src";
|
||||
import bls, {initLibrary} from "../../src";
|
||||
import {describeDirectorySpecTest, InputType} from "@chainsafe/eth2.0-spec-test-util/lib/single";
|
||||
import path from "path";
|
||||
|
||||
|
@ -9,6 +9,10 @@ interface IAggregatePubKeysTestCase {
|
|||
};
|
||||
}
|
||||
|
||||
before(async function f() {
|
||||
await initLibrary();
|
||||
});
|
||||
|
||||
describeDirectorySpecTest<IAggregatePubKeysTestCase, string>(
|
||||
"aggregate pubkeys",
|
||||
path.join(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import path from "path";
|
||||
import bls from "../../src";
|
||||
import bls, {initLibrary} from "../../src";
|
||||
import {describeDirectorySpecTest, InputType} from "@chainsafe/eth2.0-spec-test-util/lib/single";
|
||||
|
||||
interface AggregateSigsTestCase {
|
||||
|
@ -9,6 +9,10 @@ interface AggregateSigsTestCase {
|
|||
};
|
||||
}
|
||||
|
||||
before(async function f() {
|
||||
await initLibrary();
|
||||
});
|
||||
|
||||
describeDirectorySpecTest<AggregateSigsTestCase, string>(
|
||||
"aggregate sigs",
|
||||
path.join(
|
||||
|
|
|
@ -1,38 +1,36 @@
|
|||
import path from "path";
|
||||
import {padLeft} from "../../src/helpers/utils";
|
||||
import {G2point} from "../../src/helpers/g2point";
|
||||
import {describeDirectorySpecTest, InputType} from "@chainsafe/eth2.0-spec-test-util/lib/single";
|
||||
|
||||
interface IMsgHHashCOmpressed {
|
||||
data: {
|
||||
input: {
|
||||
message: string;
|
||||
domain: string;
|
||||
};
|
||||
output: string[];
|
||||
};
|
||||
}
|
||||
|
||||
describeDirectorySpecTest<IMsgHHashCOmpressed, string>(
|
||||
"msg_hash_compressed",
|
||||
path.join(
|
||||
__dirname,
|
||||
"../../../../node_modules/@chainsafe/eth2-spec-tests/tests/general/phase0/bls/msg_hash_compressed/small"
|
||||
),
|
||||
(testCase => {
|
||||
const domain = padLeft(Buffer.from(testCase.data.input.domain.replace("0x", ""), "hex"), 8);
|
||||
const input = Buffer.from(testCase.data.input.message.replace("0x", ""), "hex");
|
||||
const result = G2point.hashToG2(input, domain);
|
||||
return `0x${result.toBytesCompressed().toString("hex")}`;
|
||||
}),
|
||||
{
|
||||
inputTypes: {
|
||||
data: InputType.YAML,
|
||||
},
|
||||
getExpected: (testCase => {
|
||||
const xReExpected = padLeft(Buffer.from(testCase.data.output[0].replace("0x", ""), "hex"), 48);
|
||||
const xImExpected = padLeft(Buffer.from(testCase.data.output[1].replace("0x", ""), "hex"), 48);
|
||||
return "0x" + Buffer.concat([xReExpected, xImExpected]).toString("hex");
|
||||
})
|
||||
}
|
||||
);
|
||||
// import path from "path";
|
||||
// import {describeDirectorySpecTest, InputType} from "@chainsafe/eth2.0-spec-test-util/lib/single";
|
||||
//
|
||||
// interface IMsgHHashCOmpressed {
|
||||
// data: {
|
||||
// input: {
|
||||
// message: string;
|
||||
// domain: string;
|
||||
// };
|
||||
// output: string[];
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// describeDirectorySpecTest<IMsgHHashCOmpressed, string>(
|
||||
// "msg_hash_compressed",
|
||||
// path.join(
|
||||
// __dirname,
|
||||
// "../../../../node_modules/@chainsafe/eth2-spec-tests/tests/general/phase0/bls/msg_hash_compressed/small"
|
||||
// ),
|
||||
// (testCase => {
|
||||
// const domain = Buffer.from(testCase.data.input.domain.replace("0x", ""), "hex");
|
||||
// const input = Buffer.from(testCase.data.input.message.replace("0x", ""), "hex");
|
||||
// const result = G2point.hashToG2(input, domain);
|
||||
// return `0x${result.toBytesCompressed().toString("hex")}`;
|
||||
// }),
|
||||
// {
|
||||
// inputTypes: {
|
||||
// data: InputType.YAML,
|
||||
// },
|
||||
// getExpected: (testCase => {
|
||||
// const xReExpected = padLeft(Buffer.from(testCase.data.output[0].replace("0x", ""), "hex"), 48);
|
||||
// const xImExpected = padLeft(Buffer.from(testCase.data.output[1].replace("0x", ""), "hex"), 48);
|
||||
// return "0x" + Buffer.concat([xReExpected, xImExpected]).toString("hex");
|
||||
// })
|
||||
// }
|
||||
// );
|
|
@ -1,43 +1,43 @@
|
|||
import path from "path";
|
||||
import {padLeft} from "../../src/helpers/utils";
|
||||
import {G2point} from "../../src/helpers/g2point";
|
||||
import {describeDirectorySpecTest, InputType} from "@chainsafe/eth2.0-spec-test-util/lib/single";
|
||||
|
||||
interface IMsgHHashUnCompressed {
|
||||
data: {
|
||||
input: {
|
||||
message: string;
|
||||
domain: string;
|
||||
};
|
||||
output: string[][];
|
||||
};
|
||||
}
|
||||
|
||||
describeDirectorySpecTest<IMsgHHashUnCompressed, string>(
|
||||
"msg_hash_uncompressed",
|
||||
path.join(
|
||||
__dirname,
|
||||
"../../../../node_modules/@chainsafe/eth2-spec-tests/tests/general/phase0/bls/msg_hash_uncompressed/small"
|
||||
),
|
||||
(testCase => {
|
||||
const domain = padLeft(Buffer.from(testCase.data.input.domain.replace("0x", ""), "hex"), 8);
|
||||
const input = Buffer.from(testCase.data.input.message.replace("0x", ""), "hex");
|
||||
const result = G2point.hashToG2(input, domain);
|
||||
return `0x${result.toBytesCompressed().toString("hex")}`;
|
||||
}),
|
||||
{
|
||||
inputTypes: {
|
||||
data: InputType.YAML,
|
||||
},
|
||||
getExpected: (testCase => {
|
||||
return "0x" + G2point.fromUncompressedInput(
|
||||
Buffer.from(testCase.data.output[0][0].replace("0x", ""), "hex"),
|
||||
Buffer.from(testCase.data.output[0][1].replace("0x", ""), "hex"),
|
||||
Buffer.from(testCase.data.output[1][0].replace("0x", ""), "hex"),
|
||||
Buffer.from(testCase.data.output[1][1].replace("0x", ""), "hex"),
|
||||
Buffer.from(testCase.data.output[2][0].replace("0x", ""), "hex"),
|
||||
Buffer.from(testCase.data.output[2][1].replace("0x", ""), "hex"),
|
||||
).toBytesCompressed().toString("hex");
|
||||
})
|
||||
}
|
||||
);
|
||||
// import path from "path";
|
||||
// import {padLeft} from "../../src/helpers/utils";
|
||||
// import {G2point} from "../../src/helpers/g2point";
|
||||
// import {describeDirectorySpecTest, InputType} from "@chainsafe/eth2.0-spec-test-util/lib/single";
|
||||
//
|
||||
// interface IMsgHHashUnCompressed {
|
||||
// data: {
|
||||
// input: {
|
||||
// message: string;
|
||||
// domain: string;
|
||||
// };
|
||||
// output: string[][];
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// describeDirectorySpecTest<IMsgHHashUnCompressed, string>(
|
||||
// "msg_hash_uncompressed",
|
||||
// path.join(
|
||||
// __dirname,
|
||||
// "../../../../node_modules/@chainsafe/eth2-spec-tests/tests/general/phase0/bls/msg_hash_uncompressed/small"
|
||||
// ),
|
||||
// (testCase => {
|
||||
// const domain = padLeft(Buffer.from(testCase.data.input.domain.replace("0x", ""), "hex"), 8);
|
||||
// const input = Buffer.from(testCase.data.input.message.replace("0x", ""), "hex");
|
||||
// const result = G2point.hashToG2(input, domain);
|
||||
// return `0x${result.toBytesCompressed().toString("hex")}`;
|
||||
// }),
|
||||
// {
|
||||
// inputTypes: {
|
||||
// data: InputType.YAML,
|
||||
// },
|
||||
// getExpected: (testCase => {
|
||||
// return "0x" + G2point.fromUncompressedInput(
|
||||
// Buffer.from(testCase.data.output[0][0].replace("0x", ""), "hex"),
|
||||
// Buffer.from(testCase.data.output[0][1].replace("0x", ""), "hex"),
|
||||
// Buffer.from(testCase.data.output[1][0].replace("0x", ""), "hex"),
|
||||
// Buffer.from(testCase.data.output[1][1].replace("0x", ""), "hex"),
|
||||
// Buffer.from(testCase.data.output[2][0].replace("0x", ""), "hex"),
|
||||
// Buffer.from(testCase.data.output[2][1].replace("0x", ""), "hex"),
|
||||
// ).toBytesCompressed().toString("hex");
|
||||
// })
|
||||
// }
|
||||
// );
|
|
@ -1,4 +1,4 @@
|
|||
import bls from "../../src";
|
||||
import bls, {initLibrary} from "../../src";
|
||||
import {describeDirectorySpecTest, InputType} from "@chainsafe/eth2.0-spec-test-util/lib/single";
|
||||
import path from "path";
|
||||
|
||||
|
@ -9,6 +9,10 @@ interface IPrivToPubTestCase {
|
|||
};
|
||||
}
|
||||
|
||||
before(async function f() {
|
||||
await initLibrary();
|
||||
});
|
||||
|
||||
describeDirectorySpecTest<IPrivToPubTestCase, string>(
|
||||
"priv_to_pub",
|
||||
path.join(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import path from "path";
|
||||
import bls from "../../src";
|
||||
import bls, {initLibrary} from "../../src";
|
||||
import {padLeft} from "../../src/helpers/utils";
|
||||
import {describeDirectorySpecTest, InputType} from "@chainsafe/eth2.0-spec-test-util/lib/single";
|
||||
|
||||
|
@ -14,6 +14,10 @@ interface ISignMessageTestCase {
|
|||
};
|
||||
}
|
||||
|
||||
before(async function f() {
|
||||
await initLibrary();
|
||||
});
|
||||
|
||||
describeDirectorySpecTest<ISignMessageTestCase, string>(
|
||||
"priv_to_pub",
|
||||
path.join(
|
||||
|
|
|
@ -1,109 +0,0 @@
|
|||
import {G1point} from "../../../src/helpers/g1point";
|
||||
import {expect} from "chai";
|
||||
|
||||
describe('g1point', function() {
|
||||
|
||||
it('should generate different random point', () => {
|
||||
const g1 = G1point.random();
|
||||
const g2 = G1point.random();
|
||||
expect(g1.equal(g2)).to.be.false;
|
||||
});
|
||||
|
||||
it('should be same', () => {
|
||||
const g1 = G1point.random();
|
||||
expect(g1.equal(g1)).to.be.true;
|
||||
});
|
||||
|
||||
it('serialize adn deserialize should produce same result', () => {
|
||||
const g1 = G1point.random();
|
||||
const g2 = G1point.fromBytesCompressed(g1.toBytesCompressed());
|
||||
expect(g1.equal(g2)).to.be.true;
|
||||
});
|
||||
|
||||
it('deserialize correct point doesn not throw', () => {
|
||||
expect(() => {
|
||||
G1point.fromBytesCompressed(
|
||||
Buffer.from(
|
||||
'8123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
|
||||
'hex'
|
||||
)
|
||||
)
|
||||
}).to.not.throw;
|
||||
});
|
||||
|
||||
it('deserialize incorrect point throws', () => {
|
||||
expect(() => {
|
||||
G1point.fromBytesCompressed(
|
||||
Buffer.from(
|
||||
'8123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde0',
|
||||
'hex'
|
||||
)
|
||||
)
|
||||
}).to.throw('X coordinate is not on the curve.');
|
||||
});
|
||||
|
||||
it('deserialize incorrect point throws 2', () => {
|
||||
expect(() => {
|
||||
G1point.fromBytesCompressed(
|
||||
Buffer.from(
|
||||
'9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab',
|
||||
'hex'
|
||||
)
|
||||
)
|
||||
}).to.throw('X coordinate is too large.');
|
||||
});
|
||||
|
||||
it('deserialize incorrect point throws 3', () => {
|
||||
expect(() => {
|
||||
G1point.fromBytesCompressed(
|
||||
Buffer.from(
|
||||
'9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaac',
|
||||
'hex'
|
||||
)
|
||||
)
|
||||
}).to.throw('X coordinate is too large.');
|
||||
});
|
||||
|
||||
it('deserialize incorrect point throws to few bytes', () => {
|
||||
expect(() => {
|
||||
G1point.fromBytesCompressed(
|
||||
Buffer.from(
|
||||
'9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa',
|
||||
'hex'
|
||||
)
|
||||
)
|
||||
}).to.throw('Expected g1 compressed input to have 48 bytes');
|
||||
});
|
||||
|
||||
it('deserialize incorrect point throws to many bytes', () => {
|
||||
expect(() => {
|
||||
G1point.fromBytesCompressed(
|
||||
Buffer.from(
|
||||
'9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa900',
|
||||
'hex'
|
||||
)
|
||||
)
|
||||
}).to.throw('Expected g1 compressed input to have 48 bytes');
|
||||
});
|
||||
|
||||
it('deserialize infinity', () => {
|
||||
const g1 = G1point.fromBytesCompressed(
|
||||
Buffer.from(
|
||||
'c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
||||
'hex'
|
||||
)
|
||||
);
|
||||
expect(g1.getPoint().is_infinity()).to.be.true
|
||||
});
|
||||
|
||||
it('wrong infinity serialization', () => {
|
||||
expect(() => {
|
||||
G1point.fromBytesCompressed(
|
||||
Buffer.from(
|
||||
'e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
||||
'hex'
|
||||
)
|
||||
)
|
||||
}).to.throw('The serialised input has B flag set, but A flag is set, or X is non-zero.');
|
||||
});
|
||||
});
|
|
@ -1,153 +0,0 @@
|
|||
import {G2point} from "../../../src/helpers/g2point";
|
||||
import {expect} from "chai";
|
||||
|
||||
describe('g2point', function() {
|
||||
|
||||
it('should be equals', () => {
|
||||
const g2 = G2point.random();
|
||||
expect(g2.equal(g2)).to.be.true;
|
||||
});
|
||||
|
||||
it('should not be equals', () => {
|
||||
const g2 = G2point.random();
|
||||
const g22 = G2point.random();
|
||||
expect(g2.equal(g22)).to.be.false;
|
||||
});
|
||||
|
||||
it('serialize deserialize should be equal', () => {
|
||||
const g2 = G2point.random();
|
||||
expect(G2point.fromCompressedBytes(g2.toBytesCompressed()).equal(g2)).to.be.true;
|
||||
});
|
||||
|
||||
it('should deserialize from compress', () => {
|
||||
const x =
|
||||
"8123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
+ "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
|
||||
expect(() => {
|
||||
G2point.fromCompressedBytes(
|
||||
Buffer.from(
|
||||
x,
|
||||
'hex'
|
||||
)
|
||||
)
|
||||
}).to.not.throw;
|
||||
});
|
||||
|
||||
it('should fail to deserialize', () => {
|
||||
const x =
|
||||
"800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
+ "1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab";
|
||||
expect(() => {
|
||||
G2point.fromCompressedBytes(
|
||||
Buffer.from(
|
||||
x,
|
||||
'hex'
|
||||
)
|
||||
)
|
||||
}).to.throw('The deserialised X real or imaginary coordinate is too large.');
|
||||
});
|
||||
|
||||
it('should fail to deserialize 2', () => {
|
||||
const x =
|
||||
"9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"
|
||||
+ "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
|
||||
expect(() => {
|
||||
G2point.fromCompressedBytes(
|
||||
Buffer.from(
|
||||
x,
|
||||
'hex'
|
||||
)
|
||||
)
|
||||
}).to.throw('The deserialised X real or imaginary coordinate is too large.');
|
||||
});
|
||||
|
||||
it('should fail to deserialize 3', () => {
|
||||
const x =
|
||||
"800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
+ "1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaac";
|
||||
expect(() => {
|
||||
G2point.fromCompressedBytes(
|
||||
Buffer.from(
|
||||
x,
|
||||
'hex'
|
||||
)
|
||||
)
|
||||
}).to.throw('The deserialised X real or imaginary coordinate is too large.');
|
||||
});
|
||||
|
||||
it('should fail to deserialize 4', () => {
|
||||
const x =
|
||||
"9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaac"
|
||||
+ "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
|
||||
expect(() => {
|
||||
G2point.fromCompressedBytes(
|
||||
Buffer.from(
|
||||
x,
|
||||
'hex'
|
||||
)
|
||||
)
|
||||
}).to.throw('The deserialised X real or imaginary coordinate is too large.');
|
||||
});
|
||||
|
||||
it('should fail to deserialize 5', () => {
|
||||
const x =
|
||||
"8123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
+ "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde0";
|
||||
expect(() => {
|
||||
G2point.fromCompressedBytes(
|
||||
Buffer.from(
|
||||
x,
|
||||
'hex'
|
||||
)
|
||||
)
|
||||
}).to.throw('X coordinate is not on the curve.');
|
||||
});
|
||||
|
||||
it('should fail to deserialize infinity', () => {
|
||||
const x =
|
||||
"800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
+ "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
|
||||
expect(() => {
|
||||
G2point.fromCompressedBytes(
|
||||
Buffer.from(
|
||||
x,
|
||||
'hex'
|
||||
)
|
||||
)
|
||||
}).to.throw('X coordinate is not on the curve.');
|
||||
});
|
||||
|
||||
it('should fail to deserialize - too few bytes', () => {
|
||||
const x = "8123456789abcd";
|
||||
expect(() => {
|
||||
G2point.fromCompressedBytes(
|
||||
Buffer.from(
|
||||
x,
|
||||
'hex'
|
||||
)
|
||||
)
|
||||
}).to.throw('Expected signature of 96 bytes');
|
||||
});
|
||||
|
||||
it('should fail to deserialize - too many bytes', () => {
|
||||
expect(() => {
|
||||
G2point.fromCompressedBytes(
|
||||
Buffer.alloc(100, 1),
|
||||
)
|
||||
}).to.throw('Expected signature of 96 bytes');
|
||||
});
|
||||
|
||||
it('should deserialize infinity', () => {
|
||||
const x =
|
||||
"c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
+ "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
|
||||
const g2 = G2point.fromCompressedBytes(
|
||||
Buffer.from(
|
||||
x,
|
||||
'hex'
|
||||
)
|
||||
);
|
||||
expect(g2.getPoint().is_infinity()).to.be.true;
|
||||
})
|
||||
|
||||
});
|
|
@ -1,27 +0,0 @@
|
|||
import {expect} from "chai";
|
||||
import {padLeft} from "../../../src/helpers/utils";
|
||||
import {G1point} from "../../../src/helpers/g1point";
|
||||
|
||||
describe('helpers tests', function() {
|
||||
|
||||
describe('padLeft', function() {
|
||||
|
||||
it('throw if source larger than target', () => {
|
||||
expect(
|
||||
() => padLeft(Buffer.alloc(2, 0), 1)
|
||||
).to.throw;
|
||||
});
|
||||
|
||||
it('pad one 0 on left side', () => {
|
||||
const result = padLeft(
|
||||
Buffer.alloc(1, 1),
|
||||
2
|
||||
);
|
||||
expect(result.length).to.be.equal(2);
|
||||
expect(result[0]).to.be.equal(0);
|
||||
expect(result[1]).to.be.equal(1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -1,11 +1,19 @@
|
|||
import bls from "../../src";
|
||||
import {Keypair} from "../../src/keypair";
|
||||
import bls, {aggregatePubkeys, aggregateSignatures, initLibrary, Keypair, verify, verifyMultiple} from "../../src";
|
||||
import {sha256} from "js-sha256";
|
||||
import {G2point} from "../../src/helpers/g2point";
|
||||
import {expect} from "chai";
|
||||
import {destroy} from "../../src/context";
|
||||
import {padLeft} from "../../lib/helpers/utils";
|
||||
|
||||
describe("test bls", function () {
|
||||
|
||||
before(async function() {
|
||||
await initLibrary();
|
||||
});
|
||||
|
||||
after(function () {
|
||||
destroy();
|
||||
});
|
||||
|
||||
describe("aggregate pubkey", function () {
|
||||
it("should aggregate empty array", function () {
|
||||
expect(bls.aggregatePubkeys([])).to.not.throw;
|
||||
|
@ -17,10 +25,11 @@ describe("test bls", function () {
|
|||
const keypair = Keypair.generate();
|
||||
const messageHash = Buffer.from(sha256.arrayBuffer("Test"));
|
||||
const domain = Buffer.alloc(8, 1);
|
||||
const signature = keypair.privateKey.sign(
|
||||
G2point.hashToG2(messageHash, domain)
|
||||
const signature = keypair.privateKey.signMessage(
|
||||
messageHash,
|
||||
domain
|
||||
);
|
||||
const result = bls.verify(
|
||||
const result = verify(
|
||||
keypair.publicKey.toBytesCompressed(),
|
||||
messageHash,
|
||||
signature.toBytesCompressed(),
|
||||
|
@ -34,11 +43,12 @@ describe("test bls", function () {
|
|||
const keypair = Keypair.generate();
|
||||
const messageHash = Buffer.from(sha256.arrayBuffer("Test"));
|
||||
const domain = Buffer.alloc(8, 1);
|
||||
const signature = keypair.privateKey.sign(
|
||||
G2point.hashToG2(messageHash, domain)
|
||||
const signature = keypair.privateKey.signMessage(
|
||||
messageHash,
|
||||
domain
|
||||
);
|
||||
const pubKey = keypair.publicKey.toBytesCompressed();
|
||||
bls.verify(
|
||||
verify(
|
||||
pubKey,
|
||||
messageHash,
|
||||
signature.toBytesCompressed(),
|
||||
|
@ -53,7 +63,7 @@ describe("test bls", function () {
|
|||
const messageHash2 = Buffer.from(sha256.arrayBuffer("Test message2"));
|
||||
const domain = Buffer.from("01", "hex");
|
||||
const signature = Buffer.alloc(96);
|
||||
const result = bls.verify(
|
||||
const result = verify(
|
||||
keypair.publicKey.toBytesCompressed(),
|
||||
messageHash2,
|
||||
signature,
|
||||
|
@ -66,11 +76,12 @@ describe("test bls", function () {
|
|||
const keypair = Keypair.generate();
|
||||
const messageHash = Buffer.from(sha256.arrayBuffer("Test message"));
|
||||
const messageHash2 = Buffer.from(sha256.arrayBuffer("Test message2"));
|
||||
const domain = Buffer.from("01", "hex");
|
||||
const signature = keypair.privateKey.sign(
|
||||
G2point.hashToG2(messageHash, domain)
|
||||
const domain = padLeft(Buffer.from("01", "hex"), 8);
|
||||
const signature = keypair.privateKey.signMessage(
|
||||
messageHash,
|
||||
domain
|
||||
);
|
||||
const result = bls.verify(
|
||||
const result = verify(
|
||||
keypair.publicKey.toBytesCompressed(),
|
||||
messageHash2,
|
||||
signature.toBytesCompressed(),
|
||||
|
@ -82,12 +93,13 @@ describe("test bls", function () {
|
|||
it("should fail verify signature of different domain", () => {
|
||||
const keypair = Keypair.generate();
|
||||
const messageHash = Buffer.from(sha256.arrayBuffer("Test message"));
|
||||
const domain = Buffer.from("01", "hex");
|
||||
const domain2 = Buffer.from("02", "hex");
|
||||
const signature = keypair.privateKey.sign(
|
||||
G2point.hashToG2(messageHash, domain)
|
||||
const domain = padLeft(Buffer.from("01", "hex"), 8);
|
||||
const domain2 = padLeft(Buffer.from("02", "hex"), 8);
|
||||
const signature = keypair.privateKey.signMessage(
|
||||
messageHash,
|
||||
domain
|
||||
);
|
||||
const result = bls.verify(
|
||||
const result = verify(
|
||||
keypair.publicKey.toBytesCompressed(),
|
||||
messageHash,
|
||||
signature.toBytesCompressed(),
|
||||
|
@ -101,10 +113,11 @@ describe("test bls", function () {
|
|||
const keypair2 = Keypair.generate();
|
||||
const messageHash = Buffer.from(sha256.arrayBuffer("Test message"));
|
||||
const domain = Buffer.from("01", "hex");
|
||||
const signature = keypair.privateKey.sign(
|
||||
G2point.hashToG2(messageHash, domain)
|
||||
const signature = keypair.privateKey.signMessage(
|
||||
messageHash,
|
||||
domain
|
||||
);
|
||||
const result = bls.verify(
|
||||
const result = verify(
|
||||
keypair2.publicKey.toBytesCompressed(),
|
||||
messageHash,
|
||||
signature.toBytesCompressed(),
|
||||
|
@ -127,32 +140,32 @@ describe("test bls", function () {
|
|||
const keypair3 = Keypair.generate();
|
||||
const keypair4 = Keypair.generate();
|
||||
|
||||
const message1 = Buffer.from("Test1", "utf-8");
|
||||
const message2 = Buffer.from("Test2", "utf-8");
|
||||
const message1 = Buffer.from(sha256.arrayBuffer("Test1"));
|
||||
const message2 = Buffer.from(sha256.arrayBuffer("Test2"));
|
||||
|
||||
const signature1 = keypair1.privateKey.signMessage(message1, domain);
|
||||
const signature2 = keypair2.privateKey.signMessage(message1, domain);
|
||||
const signature3 = keypair3.privateKey.signMessage(message2, domain);
|
||||
const signature4 = keypair4.privateKey.signMessage(message2, domain);
|
||||
|
||||
const aggregatePubKey12 = bls.aggregatePubkeys([
|
||||
const aggregatePubKey12 = aggregatePubkeys([
|
||||
keypair1.publicKey.toBytesCompressed(),
|
||||
keypair2.publicKey.toBytesCompressed(),
|
||||
]);
|
||||
|
||||
const aggregatePubKey34 = bls.aggregatePubkeys([
|
||||
const aggregatePubKey34 = aggregatePubkeys([
|
||||
keypair3.publicKey.toBytesCompressed(),
|
||||
keypair4.publicKey.toBytesCompressed(),
|
||||
]);
|
||||
|
||||
const aggregateSignature = bls.aggregateSignatures([
|
||||
const aggregateSignature = aggregateSignatures([
|
||||
signature1.toBytesCompressed(),
|
||||
signature2.toBytesCompressed(),
|
||||
signature3.toBytesCompressed(),
|
||||
signature4.toBytesCompressed(),
|
||||
]);
|
||||
|
||||
const result = bls.verifyMultiple(
|
||||
const result = verifyMultiple(
|
||||
[aggregatePubKey12, aggregatePubKey34],
|
||||
[message1, message2],
|
||||
aggregateSignature,
|
||||
|
@ -173,21 +186,21 @@ describe("test bls", function () {
|
|||
const keypair3 = Keypair.generate();
|
||||
const keypair4 = Keypair.generate();
|
||||
|
||||
const message = Buffer.from("Test1", "utf-8");
|
||||
const message = Buffer.from(sha256.arrayBuffer("Test1"));
|
||||
|
||||
const signature1 = keypair1.privateKey.signMessage(message, domain);
|
||||
const signature2 = keypair2.privateKey.signMessage(message, domain);
|
||||
const signature3 = keypair3.privateKey.signMessage(message, domain);
|
||||
const signature4 = keypair4.privateKey.signMessage(message, domain);
|
||||
|
||||
const aggregateSignature = bls.aggregateSignatures([
|
||||
const aggregateSignature = aggregateSignatures([
|
||||
signature1.toBytesCompressed(),
|
||||
signature2.toBytesCompressed(),
|
||||
signature3.toBytesCompressed(),
|
||||
signature4.toBytesCompressed(),
|
||||
]);
|
||||
|
||||
const result = bls.verifyMultiple(
|
||||
const result = verifyMultiple(
|
||||
[
|
||||
keypair1.publicKey.toBytesCompressed(),
|
||||
keypair2.publicKey.toBytesCompressed(),
|
||||
|
@ -212,8 +225,8 @@ describe("test bls", function () {
|
|||
const keypair3 = Keypair.generate();
|
||||
const keypair4 = Keypair.generate();
|
||||
|
||||
const message1 = Buffer.from("Test1", "utf-8");
|
||||
const message2 = Buffer.from("Test2", "utf-8");
|
||||
const message1 = Buffer.from(sha256.arrayBuffer("Test1"));
|
||||
const message2 = Buffer.from(sha256.arrayBuffer("Test2"));
|
||||
|
||||
const signature1 = keypair1.privateKey.signMessage(message1, domain);
|
||||
const signature2 = keypair2.privateKey.signMessage(message1, domain);
|
||||
|
@ -256,8 +269,8 @@ describe("test bls", function () {
|
|||
const keypair3 = Keypair.generate();
|
||||
const keypair4 = Keypair.generate();
|
||||
|
||||
const message1 = Buffer.from("Test1", "utf-8");
|
||||
const message2 = Buffer.from("Test2", "utf-8");
|
||||
const message1 = Buffer.from(sha256.arrayBuffer("Test1"));
|
||||
const message2 = Buffer.from(sha256.arrayBuffer("Test2"));
|
||||
|
||||
const signature1 = keypair1.privateKey.signMessage(message1, domain);
|
||||
const signature2 = keypair2.privateKey.signMessage(message1, domain);
|
||||
|
@ -297,8 +310,8 @@ describe("test bls", function () {
|
|||
const keypair3 = Keypair.generate();
|
||||
const keypair4 = Keypair.generate();
|
||||
|
||||
const message1 = Buffer.from("Test1", "utf-8");
|
||||
const message2 = Buffer.from("Test2", "utf-8");
|
||||
const message1 = Buffer.from(sha256.arrayBuffer("Test1"));
|
||||
const message2 = Buffer.from(sha256.arrayBuffer("Test2"));
|
||||
|
||||
const signature1 = keypair1.privateKey.signMessage(message1, domain);
|
||||
const signature2 = keypair2.privateKey.signMessage(message1, domain);
|
||||
|
@ -335,8 +348,8 @@ describe("test bls", function () {
|
|||
|
||||
const signature = Buffer.alloc(96);
|
||||
|
||||
const message1 = Buffer.from("Test1", "utf-8");
|
||||
const message2 = Buffer.from("Test2", "utf-8");
|
||||
const message1 = Buffer.from(sha256.arrayBuffer("Test1"));
|
||||
const message2 = Buffer.from(sha256.arrayBuffer("Test2"));
|
||||
|
||||
const result = bls.verifyMultiple(
|
||||
[],
|
||||
|
|
|
@ -2,10 +2,19 @@ import {PrivateKey} from "../../src/privateKey";
|
|||
import {PublicKey} from "../../src/publicKey";
|
||||
import {Keypair} from "../../src/keypair";
|
||||
import {expect} from "chai";
|
||||
import {destroy, init} from "../../src/context";
|
||||
|
||||
describe('keypair', function() {
|
||||
describe("keypair", function() {
|
||||
|
||||
it('should create from private and public key', () => {
|
||||
before(async function () {
|
||||
await init();
|
||||
});
|
||||
|
||||
after(function () {
|
||||
destroy();
|
||||
});
|
||||
|
||||
it("should create from private and public key", () => {
|
||||
const secret = PrivateKey.random();
|
||||
const secret2 = PrivateKey.random();
|
||||
const publicKey = PublicKey.fromBytes(PublicKey.fromPrivateKey(secret2).toBytesCompressed());
|
||||
|
@ -15,11 +24,11 @@ describe('keypair', function() {
|
|||
expect(keypair.privateKey).to.not.be.equal(secret2);
|
||||
});
|
||||
|
||||
it('should create from private', () => {
|
||||
it("should create from private", () => {
|
||||
const secret = PrivateKey.random();
|
||||
const publicKey = PublicKey.fromPrivateKey(secret);
|
||||
const keypair = new Keypair(secret);
|
||||
expect(keypair.publicKey.toBytesCompressed().toString('hex'))
|
||||
.to.be.equal(publicKey.toBytesCompressed().toString('hex'));
|
||||
})
|
||||
expect(keypair.publicKey.toBytesCompressed().toString("hex"))
|
||||
.to.be.equal(publicKey.toBytesCompressed().toString("hex"));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
import {destroy, init} from "../../src/context";
|
||||
import {PublicKey} from "../../src/publicKey";
|
||||
import {expect} from "chai";
|
||||
import { PrivateKey } from "../../src/privateKey";
|
||||
|
||||
|
||||
describe("public key", function () {
|
||||
|
||||
before(async function f() {
|
||||
await init();
|
||||
});
|
||||
|
||||
after(function () {
|
||||
destroy();
|
||||
});
|
||||
|
||||
it("from hex", function () {
|
||||
const publicKey =
|
||||
"0xb6f21199594b56d77670564bf422cb331d5281ca2c1f9a45588a56881d8287ef8619efa6456d6cd2ef61306aa5b21311";
|
||||
expect(PublicKey.fromHex(publicKey).toHexString()).to.be.equal(publicKey);
|
||||
});
|
||||
|
||||
it("from bytes", function () {
|
||||
const publicKey =
|
||||
"b6f21199594b56d77670564bf422cb331d5281ca2c1f9a45588a56881d8287ef8619efa6456d6cd2ef61306aa5b21311";
|
||||
expect(PublicKey.fromBytes(Buffer.from(publicKey, "hex")).toHexString()).to.be.equal(`0x${publicKey}`);
|
||||
});
|
||||
|
||||
it("from private key", function () {
|
||||
PublicKey.fromPrivateKey(PrivateKey.random());
|
||||
});
|
||||
|
||||
});
|
Reference in New Issue