Skip to content

Commit

Permalink
Merge pull request #118 from ChainSafe/mkeil/rebuild-mult-by
Browse files Browse the repository at this point in the history
feat: add MultiplyBy
  • Loading branch information
matthewkeil authored Mar 8, 2024
2 parents 6caaddc + 30dad3e commit c0d69b1
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 45 deletions.
2 changes: 2 additions & 0 deletions rebuild/lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export class PublicKey implements Serializable {
toHex(compress?: boolean): string;
keyValidate(): void;
isInfinity(): boolean;
multiplyBy(randomBytes: BlstBuffer): PublicKey;
}

export class Signature implements Serializable {
Expand All @@ -92,6 +93,7 @@ export class Signature implements Serializable {
toHex(compress?: boolean): string;
sigValidate(): void;
isInfinity(): boolean;
multiplyBy(randomBytes: BlstBuffer): Signature;
}

/**
Expand Down
23 changes: 21 additions & 2 deletions rebuild/src/public_key.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ void PublicKey::Init(
"isInfinity",
&PublicKey::IsInfinity,
static_cast<napi_property_attributes>(napi_enumerable)),
InstanceMethod(
"multiplyBy",
&PublicKey::MultiplyBy,
static_cast<napi_property_attributes>(napi_enumerable)),
};

Napi::Function ctr = DefineClass(env, "PublicKey", proto, module);
Expand Down Expand Up @@ -130,6 +134,21 @@ Napi::Value PublicKey::KeyValidate(const Napi::CallbackInfo &info) {
return info.Env().Undefined();
}

Napi::Value PublicKey::IsInfinity(const Napi::CallbackInfo &info) {
BLST_TS_IS_INFINITY
Napi::Value PublicKey::IsInfinity(const Napi::CallbackInfo &info){
BLST_TS_IS_INFINITY}

Napi::Value PublicKey::MultiplyBy(const Napi::CallbackInfo &info) {
BLST_TS_FUNCTION_PREAMBLE(info, env, module)
Napi::Value rand_bytes_value = info[0];
BLST_TS_UNWRAP_UINT_8_ARRAY(rand_bytes_value, rand_bytes, "randomBytes")

Napi::Object pk_obj = module->_public_key_ctr.New(
// Default to jacobian coordinates
{Napi::External<P1Wrapper>::New(
env,
new P1{_point->MultiplyBy(
rand_bytes.Data(), rand_bytes.ByteLength())}),
Napi::Boolean::New(env, false)});

return scope.Escape(pk_obj);
}
21 changes: 21 additions & 0 deletions rebuild/src/public_key.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ class P1Wrapper {
virtual bool InGroup() = 0;
virtual void Serialize(bool compress, blst::byte *out) = 0;
virtual void AddTo(blst::P1 &point) = 0;
virtual blst::P1 MultiplyBy(
blst::byte *rand_bytes, size_t rand_bytes_length) = 0;
virtual P1AffineGroup AsAffine() = 0;
};

Expand All @@ -36,6 +38,15 @@ class P1 : public P1Wrapper {
compress ? _point.compress(out) : _point.serialize(out);
}
void AddTo(blst::P1 &point) override { point.add(_point); };
blst::P1 MultiplyBy(
blst::byte *rand_bytes, size_t rand_bytes_length) override {
blst::byte out[96];
_point.serialize(out);
// this should get std::move all the way into the P1 member value
blst::P1 point{out, 96};
point.mult(rand_bytes, rand_bytes_length);
return point;
};
P1AffineGroup AsAffine() override {
P1AffineGroup group{std::make_unique<blst::P1_Affine>(_point), nullptr};
group.raw_point = group.smart_pointer.get();
Expand All @@ -54,6 +65,15 @@ class P1Affine : public P1Wrapper {
compress ? _point.compress(out) : _point.serialize(out);
}
void AddTo(blst::P1 &point) override { point.add(_point); };
blst::P1 MultiplyBy(
blst::byte *rand_bytes, size_t rand_bytes_length) override {
blst::byte out[96];
_point.serialize(out);
// this should get std::move all the way into the P1 member value
blst::P1 point{out, 96};
point.mult(rand_bytes, rand_bytes_length);
return point;
};
P1AffineGroup AsAffine() override {
P1AffineGroup group{nullptr, &_point};
return group;
Expand All @@ -70,6 +90,7 @@ class PublicKey : public Napi::ObjectWrap<PublicKey> {
Napi::Value Serialize(const Napi::CallbackInfo &info);
Napi::Value KeyValidate(const Napi::CallbackInfo &info);
Napi::Value IsInfinity(const Napi::CallbackInfo &info);
Napi::Value MultiplyBy(const Napi::CallbackInfo &info);
};

#endif /* BLST_TS_PUBLIC_KEY_H__ */
23 changes: 21 additions & 2 deletions rebuild/src/signature.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ void Signature::Init(
"isInfinity",
&Signature::IsInfinity,
static_cast<napi_property_attributes>(napi_enumerable)),
InstanceMethod(
"multiplyBy",
&Signature::MultiplyBy,
static_cast<napi_property_attributes>(napi_enumerable)),
};

Napi::Function ctr = DefineClass(env, "Signature", proto, module);
Expand Down Expand Up @@ -127,6 +131,21 @@ Napi::Value Signature::SigValidate(const Napi::CallbackInfo &info) {
return env.Undefined();
}

Napi::Value Signature::IsInfinity(const Napi::CallbackInfo &info) {
BLST_TS_IS_INFINITY
Napi::Value Signature::IsInfinity(const Napi::CallbackInfo &info){
BLST_TS_IS_INFINITY}

Napi::Value Signature::MultiplyBy(const Napi::CallbackInfo &info) {
BLST_TS_FUNCTION_PREAMBLE(info, env, module)
Napi::Value rand_bytes_value = info[0];
BLST_TS_UNWRAP_UINT_8_ARRAY(rand_bytes_value, rand_bytes, "randomBytes")

Napi::Object sig_obj = module->_signature_ctr.New(
// Default to jacobian coordinates
{Napi::External<P2Wrapper>::New(
env,
new P2{_point->MultiplyBy(
rand_bytes.Data(), rand_bytes.ByteLength())}),
Napi::Boolean::New(env, false)});

return scope.Escape(sig_obj);
}
21 changes: 21 additions & 0 deletions rebuild/src/signature.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ class P2Wrapper {
virtual bool InGroup() = 0;
virtual void Serialize(bool compress, blst::byte *out) = 0;
virtual void AddTo(blst::P2 &point) = 0;
virtual blst::P2 MultiplyBy(
blst::byte *rand_bytes, size_t rand_bytes_length) = 0;
virtual P2AffineGroup AsAffine() = 0;
virtual void Sign(
const blst::SecretKey &key,
Expand All @@ -41,6 +43,15 @@ class P2 : public P2Wrapper {
compress ? _point.compress(out) : _point.serialize(out);
}
void AddTo(blst::P2 &point) override { point.add(_point); };
blst::P2 MultiplyBy(
blst::byte *rand_bytes, size_t rand_bytes_length) override {
blst::byte out[192];
_point.serialize(out);
// this should get std::move all the way into the P2 member value
blst::P2 point{out, 192};
point.mult(rand_bytes, rand_bytes_length);
return point;
};
P2AffineGroup AsAffine() override {
P2AffineGroup group{std::make_unique<blst::P2_Affine>(_point), nullptr};
group.raw_point = group.smart_pointer.get();
Expand All @@ -67,6 +78,15 @@ class P2Affine : public P2Wrapper {
compress ? _point.compress(out) : _point.serialize(out);
}
void AddTo(blst::P2 &point) override { point.add(_point); };
blst::P2 MultiplyBy(
blst::byte *rand_bytes, size_t rand_bytes_length) override {
blst::byte out[192];
_point.serialize(out);
// this should get std::move all the way into the P2 member value
blst::P2 point{out, 192};
point.mult(rand_bytes, rand_bytes_length);
return point;
};
P2AffineGroup AsAffine() override {
P2AffineGroup group{nullptr, &_point};
return group;
Expand All @@ -93,6 +113,7 @@ class Signature : public Napi::ObjectWrap<Signature> {
Napi::Value Serialize(const Napi::CallbackInfo &info);
Napi::Value SigValidate(const Napi::CallbackInfo &info);
Napi::Value IsInfinity(const Napi::CallbackInfo &info);
Napi::Value MultiplyBy(const Napi::CallbackInfo &info);
};

#endif /* BLST_TS_SIGNATURE_H__ */
2 changes: 1 addition & 1 deletion src/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ export function verifyMultipleAggregateSignatures(signatureSets: SignatureSet[])
* `rand` must not be exactly zero. Otherwise it would allow the verification of invalid signatures
* See https://github.com/ChainSafe/blst-ts/issues/45
*/
function randomBytesNonZero(BYTES_COUNT: number): Buffer {
export function randomBytesNonZero(BYTES_COUNT: number): Buffer {
const rand = crypto.randomBytes(BYTES_COUNT);
for (let i = 0; i < BYTES_COUNT; i++) {
if (rand[0] !== 0) return rand;
Expand Down
67 changes: 31 additions & 36 deletions test/perf/multithreading.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,34 @@ import {itBench} from "@dapplion/benchmark";
import {expect} from "chai";
import {BlsMultiThreading, BlsPoolType, getGroupsOfBatchesOfSignatureSets} from "../utils";

describe("multithreading perf", function () {
const minutes = 10;
this.timeout(minutes * 60 * 1000);
const getGroupsInfo = (isSwig: boolean): Parameters<typeof getGroupsOfBatchesOfSignatureSets> => [
isSwig,
16,
128,
256,
256,
];
const minutes = 10;
const getGroupsInfo = (isSwig: boolean): Parameters<typeof getGroupsOfBatchesOfSignatureSets> => [
isSwig,
16,
128,
256,
256,
];

let libuvPool: BlsMultiThreading;
let workerPool: BlsMultiThreading;

describe("libuv", () => {
for (const addVerificationRandomness of [true, false]) {
describe.only(`multithreading perf - addVerificationRandomness ${addVerificationRandomness}`, function () {
this.timeout(minutes * 60 * 1000);
let libuvPool: BlsMultiThreading;
let workerPool: BlsMultiThreading;
let napiGroups: ReturnType<typeof getGroupsOfBatchesOfSignatureSets>;
let swigGroups: ReturnType<typeof getGroupsOfBatchesOfSignatureSets>;

before(async () => {
libuvPool = new BlsMultiThreading({blsPoolType: BlsPoolType.libuv});
libuvPool = new BlsMultiThreading({blsPoolType: BlsPoolType.libuv, addVerificationRandomness});
napiGroups = getGroupsOfBatchesOfSignatureSets(...getGroupsInfo(false));

workerPool = new BlsMultiThreading({blsPoolType: BlsPoolType.workers, addVerificationRandomness});
await workerPool.waitTillInitialized();
swigGroups = getGroupsOfBatchesOfSignatureSets(...getGroupsInfo(true));
});

itBench({
id: "libuv multithreading - napi",
id: `libuv multithreading - napi - addVerificationRandomness ${addVerificationRandomness}`,
fn: async () => {
const responses = [] as Promise<boolean>[];
for (const sets of napiGroups) {
Expand All @@ -33,20 +39,9 @@ describe("multithreading perf", function () {
expect(results.every((r) => r)).to.be.true;
},
});
});

describe("workers", () => {
let swigGroups: ReturnType<typeof getGroupsOfBatchesOfSignatureSets>;
before(async () => {
workerPool = new BlsMultiThreading({blsPoolType: BlsPoolType.workers});
await workerPool.waitTillInitialized();
swigGroups = getGroupsOfBatchesOfSignatureSets(...getGroupsInfo(true));
});
after(async () => {
await workerPool.close();
});
itBench({
id: "worker multithreading - swig",
id: `worker multithreading - swig - addVerificationRandomness ${addVerificationRandomness}`,
fn: async () => {
const responses = [] as Promise<any>[];
for (const sets of swigGroups) {
Expand All @@ -56,14 +51,14 @@ describe("multithreading perf", function () {
expect(results.every((r) => r)).to.be.true;
},
});
});

after(async () => {
console.log({
libuvPoolSize: libuvPool.blsPoolSize,
workerPoolSize: workerPool.blsPoolSize,
after(async () => {
console.log({
libuvPoolSize: libuvPool.blsPoolSize,
workerPoolSize: workerPool.blsPoolSize,
});
await libuvPool.close();
await workerPool.close();
});
await libuvPool.close();
await workerPool.close();
});
});
}
4 changes: 3 additions & 1 deletion test/utils/multithreading/blsMultiThreading.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const MAX_JOBS_CAN_ACCEPT_WORK = 512;
export class BlsMultiThreading {
readonly blsPoolSize: number;

private readonly addVerificationRandomness: boolean;
private readonly blsVerifyAllInQueue: boolean;
private readonly blsPoolType: BlsPoolType;
private readonly workers: WorkerDescriptor[] = [];
Expand All @@ -63,6 +64,7 @@ export class BlsMultiThreading {
private workersBusy = 0;

constructor(options: BlsMultiThreadWorkerPoolOptions /*, modules: BlsMultiThreadWorkerPoolModules */) {
this.addVerificationRandomness = options.addVerificationRandomness ?? false;
this.blsVerifyAllInQueue = options.blsVerifyAllInQueue ?? false;
this.blsPoolType = options.blsPoolType ?? BlsPoolType.workers;

Expand Down Expand Up @@ -122,7 +124,7 @@ export class BlsMultiThreading {
resolve,
reject,
addedTimeMs: Date.now(),
opts,
opts: {...opts, addVerificationRandomness: this.addVerificationRandomness},
sets: setsChunk,
message,
});
Expand Down
46 changes: 43 additions & 3 deletions test/utils/multithreading/queuedJob.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as swig from "../../../src";
import * as swig from "../../../src/lib";
import * as swigBindings from "../../../src/bindings";
import napi from "../../../rebuild/lib";
import {PublicKey} from "../types";
import {BlsWorkRequest, ISignatureSet, SignatureSetType, VerifySignatureOpts} from "./types";
Expand Down Expand Up @@ -47,6 +48,28 @@ export function prepareSwigWorkReqFromJob(job: QueuedJob): BlsWorkRequest {
};
}

if (job.opts.addVerificationRandomness) {
const pk_point = new swigBindings.blst.P1();
const sig_point = new swigBindings.blst.P2();
for (let i = 0; i < job.sets.length; i++) {
const randomness = swig.randomBytesNonZero(8);
pk_point.add((job.sets[i].publicKey as swig.PublicKey).jacobian.mult(randomness));
const sig = swig.Signature.fromBytes(job.sets[i].signature, swig.CoordType.affine);
sig.sigValidate();
sig_point.add(sig.jacobian.mult(randomness));
}
return {
opts: job.opts,
sets: [
{
pk: pk_point.serialize(),
sig: sig_point.serialize(),
msg: job.message,
},
],
};
}

const publicKey = swig.aggregatePubkeys(job.sets.map((set) => set.publicKey as swig.PublicKey));
const signature = swig.aggregateSignatures(
job.sets.map((set) => {
Expand Down Expand Up @@ -82,11 +105,28 @@ export function prepareNapiWorkReqFromJob(job: QueuedJob): BlsWorkRequest {
};
}

const publicKey = napi.aggregatePublicKeys(job.sets.map((set) => set.publicKey as napi.PublicKey));
const randomness: Uint8Array[] = [];
if (job.opts.addVerificationRandomness) {
for (let i = 0; i < job.sets.length; i++) {
randomness.push(swig.randomBytesNonZero(8));
}
}

const publicKey = napi.aggregatePublicKeys(
job.sets.map((set, i) => {
if (job.opts.addVerificationRandomness) {
return (set.publicKey as napi.PublicKey).multiplyBy(randomness[i]);
}
return set.publicKey as napi.PublicKey;
})
);
const signature = napi.aggregateSignatures(
job.sets.map((set) => {
job.sets.map((set, i) => {
const sig = napi.Signature.deserialize(set.signature, napi.CoordType.affine);
sig.sigValidate();
if (job.opts.addVerificationRandomness) {
return sig.multiplyBy(randomness[i]);
}
return sig;
})
);
Expand Down
2 changes: 2 additions & 0 deletions test/utils/multithreading/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export enum BlsPoolType {
export type BlsMultiThreadWorkerPoolOptions = {
blsVerifyAllInQueue?: boolean;
blsPoolType?: BlsPoolType;
addVerificationRandomness?: boolean;
};

export type BlsMultiThreadWorkerPoolModules = Record<string, never>;
Expand All @@ -89,6 +90,7 @@ export interface VerifySignatureOpts {
batchable?: boolean;
verifyOnMainThread?: boolean;
priority?: boolean;
addVerificationRandomness?: boolean;
}

export interface SerializedSwigSet {
Expand Down

0 comments on commit c0d69b1

Please sign in to comment.