Skip to content

Commit 88fea8c

Browse files
feat: refactor (#49)
* wip * wip: refactor the refactor (#50) * add blst/ to .gitignore * fix test * feat: remove sig_groupcheck * fix(testSets): SECRET_KEY_LENGTH -> SECRET_KEY_SIZE * fix(index): remove missing verify.js * rebirth * chore: a bit more restructuring on zig * more updates * feat: implement verifyMultipleAggregateSignatures * remove redundant log * chore: simplify signature and pk size consts * update aggregateWithRandomness with writing pks and sigs * simple test for AggregateSignature * revert some changes * fix(sig): fix fromBytes * update bindings * fix(verifyMultipleAggSigs): fix writers for verifyMultipleAggregateSignatures * uncomment tests * missing *Signature * chore(signature): remove unused sigValidate * fix(signature.ts): do fromBytes first then validate * fix test * fix randomness * fix: uncomment aggregateVerify test * chore: simplify naming for signature length constants * remove unused code * add writeSignatures * more test fixes * test(pk): add test for aggregatePublicKeys * define and use MAX_AGGREGATE_PER_JOB * use pointers to PublicKey in aggregatePublicKey * fix length for aggregate signatures * remove redundant print * AggregateSignature: fix rands input for aggregateWithRandomness * fix scratch for aggsig and aggpk * remove comment * uncomment more checks * chore: remove redundant SignatureSet * fix rands input to aggregateWithRandomness * fix stuff related to rands * fixes * remove redundant c abi code; mostly unused stuff * update README * chore(error): remove redundant error definitions * chore(aggPk): cleanup AggregatePublicKey.zig * small correciton * more minor changes to aggPk * chore(aggSig): doc comment and tidy up * add dst as a const * fix link for DST * doc comments for pairing.zig * finish doc comments * fix comment * remove comment * remove unnecessary import * pairing: remove unnecessary assert * remove redundant portable * pairing: explain that pairing owns memory * remove all usage of c pointers in native zig code * remove `min_pk.zig` and move declarations Moved declarations to more appropriate places; for example for sizes of `PublicKey` (serialize and compressed), moved their declarations to `PublicKey`. * fix bun lints * pairing: refactor pairing as top-level struct * secret_key: refactor secret_key as top-level struct * pk: make PublicKey top level struct * move fast verification to own file * signature: make Signature top-level struct * fix PublicKey.point * move aggregateVerify test to Signature.zig * move more tests around * fix abi * more descriptive errorFromInt * chore: more descriptive namings * fix and expose fast verif * remove blst from gitignore now that we use zig package * remove redundant rands * use defined MAX_AGGREGATE_PER_JOB * uncomment pk validation * bun lint * fix: use same rands for aggregateWithRandomness * bun lint --fix * fix incorrect usage of ptrCast * public key back to extern struct * bun lint fix * signature back to extern struct * bun lint --fix * undo extern struct changes * bun lint fix' * minor AggregateSignature fixes * do not copy pks * bun lint * use raw C types * refactor entire eth_c_abi.zig to use raw C types as params * bun lint --fix * fix signature.zig * bun lint --fix * Update README * fix aggregateVerify test count * bun lint --fix * expose c --------- Co-authored-by: bing <[email protected]>
1 parent a36c213 commit 88fea8c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+2116
-4940
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
zig-out/
22
.zig-cache/
33
bun/node_modules/
4-
bun/benchmark_data/
4+
bun/benchmark_data/

README.md

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,45 @@
11
# blst-z
2-
Zig wrapper for [supranational/blst](https://github.com/supranational/blst) native bindings, a highly performant BLS12-381 signature library.
2+
Zig bindings for [supranational's blst](https://github.com/supranational/blst) native bindings, a highly performant BLS12-381 signature library.
3+
4+
This set of bindings only support the `min_pk` variant.
35

46
## Installation
5-
- clone blst to root: `git clone --recurse-submodules https://github.com/supranational/blst.git`
6-
- `zig build test`
7+
8+
Run zig tests:
9+
10+
```sh
11+
zig build test
12+
```
13+
14+
Build zig library:
15+
16+
```sh
17+
zig build -Doptimize=ReleaseSafe
18+
```
19+
20+
Install and generate bun bindings:
21+
22+
```sh
23+
cd bun && bun install && bun run build && bun generate
24+
```
25+
26+
Run bun tests:
27+
28+
```sh
29+
cd bun && bun test
30+
```
31+
32+
Run bun benchmarks:
33+
34+
```sh
35+
cd bun && bun benchmark
36+
```
737

838
## Usage
9-
The below shows how to use min_pk namespace, should be the same for min_sig.
1039

1140
```zig
12-
pub const min_pk = @import("blst").min_pk;
13-
const SecretKey = min_pk.SecretKey;
41+
pub const blst = @import("blst");
42+
const SecretKey = blst.SecretKey;
1443
const ikm: [32]u8 = [_]u8{
1544
0x93, 0xad, 0x7e, 0x65, 0xde, 0xad, 0x05, 0x2a, 0x08, 0x3a,
1645
0x91, 0x0c, 0x8b, 0x72, 0x85, 0x91, 0x46, 0x4c, 0xca, 0x56,
@@ -27,4 +56,4 @@ const sig = sk.sign(msg[0..], dst[0..], null);
2756
2857
// aug is null
2958
try sig.verify(true, msg[0..], dst[0..], null, &pk, true);
30-
```
59+
```

build.zig

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub fn build(b: *std.Build) !void {
1212
});
1313

1414
const lib_blst_c = blst_c.artifact("blst");
15+
1516
// blst module (for downstream zig consumers)
1617
const blst_mod = b.addModule("blst", .{
1718
.root_source_file = b.path("src/root.zig"),
@@ -25,7 +26,7 @@ pub fn build(b: *std.Build) !void {
2526
// blst dynamic library (for bun consumers)
2627
const blst_dylib = b.addLibrary(.{
2728
.root_module = b.createModule(.{
28-
.root_source_file = b.path("src/root_c_abi_min_pk.zig"),
29+
.root_source_file = b.path("src/eth_c_abi.zig"),
2930
.target = target,
3031
.optimize = optimize,
3132
// blst does not need libc, however we need to link it to enable threading
@@ -40,8 +41,6 @@ pub fn build(b: *std.Build) !void {
4041

4142
b.installArtifact(blst_dylib);
4243

43-
// Creates a step for unit testing. This only builds the test executable
44-
// but does not run it.
4544
const lib_unit_tests = b.addTest(.{
4645
.root_source_file = b.path("src/root.zig"),
4746
.target = target,
@@ -50,17 +49,6 @@ pub fn build(b: *std.Build) !void {
5049

5150
lib_unit_tests.linkLibrary(lib_blst_c);
5251
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
53-
54-
const exe_unit_tests = b.addTest(.{
55-
.root_source_file = b.path("src/main.zig"),
56-
.target = target,
57-
.optimize = optimize,
58-
});
59-
60-
exe_unit_tests.linkLibrary(lib_blst_c);
61-
const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
62-
6352
const test_step = b.step("test", "Run unit tests");
6453
test_step.dependOn(&run_lib_unit_tests.step);
65-
test_step.dependOn(&run_exe_unit_tests.step);
6654
}

bun/bun.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"": {
55
"name": "blst-bun",
66
"dependencies": {
7-
"@chainsafe/bun-ffi-z": "^v1.0.0",
7+
"@chainsafe/bun-ffi-z": "^1.1.1",
88
},
99
"devDependencies": {
1010
"@biomejs/biome": "^1.9.3",
@@ -83,7 +83,7 @@
8383

8484
"@chainsafe/benchmark": ["@chainsafe/[email protected]", "", { "dependencies": { "@actions/cache": "^4.0.0", "@actions/github": "^6.0.0", "@vitest/runner": "^2.1.8", "ajv": "^8.17.1", "aws-sdk": "^2.932.0", "cli-table3": "^0.6.5", "csv-parse": "^5.6.0", "csv-stringify": "^6.5.2", "debug": "^4.4.0", "glob": "^10.4.5", "log-symbols": "^7.0.0", "yaml": "^2.7.0", "yargs": "^17.7.2" }, "bin": { "benchmark": "bin/index.js" } }, "sha512-5rQKK2ar1k8DwqEMU5hTQlTrtrPzNdlfrMtbRgtrfYT/QpSADXGq1vBlywofY1D8HuEuv1LhRrn3p2rgibIXew=="],
8585

86-
"@chainsafe/bun-ffi-z": ["@chainsafe/bun-ffi-z@1.0.0", "", { "peerDependencies": { "typescript": "^5" }, "bin": { "bun-ffi-z": "src/cli.ts" } }, "sha512-IRQrGWvDVSWQOcNYPZqPeqMN8qRotad/84RHSxQ8xzIuhVhkpUp2lx4VeD92Tnkcwbp5Gr+tUT4Lb47nbIe1Xg=="],
86+
"@chainsafe/bun-ffi-z": ["@chainsafe/bun-ffi-z@1.1.1", "", { "peerDependencies": { "typescript": "^5" }, "bin": { "bun-ffi-z": "src/cli.ts" } }, "sha512-RSJhdS8Ytj3NuEvwshAFRI/y9/GbD1IhVuYEjVt0u16Hw2owDvVUVAgiN4/g8WWcFMhs8i5a4VXiFzK25fARSA=="],
8787

8888
"@colors/colors": ["@colors/[email protected]", "", {}, "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="],
8989

bun/package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"module": "src/index.ts",
55
"type": "module",
66
"dependencies": {
7-
"@chainsafe/bun-ffi-z": "^v1.0.0"
7+
"@chainsafe/bun-ffi-z": "^1.1.1"
88
},
99
"devDependencies": {
1010
"@types/bun": "latest",
@@ -20,6 +20,7 @@
2020
},
2121
"scripts": {
2222
"build": "bun ./node_modules/.bin/bun-ffi-z build",
23+
"generate": "bun ./node_modules/.bin/bun-ffi-z generate-binding",
2324
"prepublishOnly": "bun ./node_modules/.bin/bun-ffi-z prepublish --artifacts artifacts",
2425
"publish": "bun ./node_modules/.bin/bun-ffi-z publish",
2526
"test:unit": "bun test test/unit",
@@ -39,6 +40,9 @@
3940
"darwin-arm64"
4041
],
4142
"optimize": "ReleaseSafe",
42-
"zigCwd": ".."
43+
"zigCwd": "..",
44+
"zigExportFiles": [
45+
"src/eth_c_abi.zig"
46+
]
4347
}
4448
}

bun/src/aggregate.ts

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import {binding, writeUint8ArrayArray} from "./binding.js";
2-
import {MAX_AGGREGATE_PER_JOB} from "./const.js";
3-
import {PublicKey, writePublicKeysReference} from "./publicKey.js";
4-
import {Signature, writeSignaturesReference} from "./signature.js";
1+
import {binding} from "./binding.js";
2+
import {pksU8, sigsU8, writePublicKeys, writeSignatures} from "./buffer.ts";
3+
import {MAX_AGGREGATE_PER_JOB, PUBLIC_KEY_SIZE, SIGNATURE_LENGTH} from "./const.js";
4+
import {PublicKey} from "./publicKey.js";
5+
import {Signature} from "./signature.js";
6+
import {writePublicKeysReference, writeSignaturesReference, writeUint8ArrayArray} from "./writers.ts";
57

68
// global public keys reference to be reused across multiple calls
79
// each 2 items are 8 bytes, store the reference of each public key
810
const publicKeysRef = new Uint32Array(MAX_AGGREGATE_PER_JOB * 2);
9-
1011
const signaturesRef = new Uint32Array(MAX_AGGREGATE_PER_JOB * 2);
1112

1213
/**
@@ -23,9 +24,9 @@ export function aggregatePublicKeys(pks: Array<PublicKey>, pksValidate?: boolean
2324

2425
for (let i = 0; i < pks.length; i += MAX_AGGREGATE_PER_JOB) {
2526
const pksBatch = pks.slice(i, Math.min(pks.length, i + MAX_AGGREGATE_PER_JOB));
26-
const pksRef = writePublicKeysReference(pksBatch);
27-
const outPk = PublicKey.defaultPublicKey();
28-
const res = binding.aggregatePublicKeys(outPk.blst_point, pksRef, pksBatch.length, pksValidate ?? false);
27+
writePublicKeys(pksBatch);
28+
const outPk = new PublicKey(new Uint8Array(PUBLIC_KEY_SIZE));
29+
const res = binding.publicKeyAggregate(outPk.ptr, pksU8, pksBatch.length, pksValidate ?? false);
2930

3031
if (res !== 0) {
3132
throw new Error(`Failed to aggregate public keys: ${res}`);
@@ -50,9 +51,9 @@ export function aggregateSignatures(sigs: Array<Signature>, sigsGroupcheck?: boo
5051

5152
for (let i = 0; i < sigs.length; i += MAX_AGGREGATE_PER_JOB) {
5253
const sigsBatch = sigs.slice(i, Math.min(sigs.length, i + MAX_AGGREGATE_PER_JOB));
53-
const sigsRef = writeSignaturesReference(sigsBatch);
54-
const outSig = Signature.defaultSignature();
55-
const res = binding.aggregateSignatures(outSig.blst_point, sigsRef, sigsBatch.length, sigsGroupcheck ?? false);
54+
writeSignatures(sigsBatch);
55+
const outSig = new Signature(new Uint8Array(SIGNATURE_LENGTH));
56+
const res = binding.signatureAggregate(outSig.ptr, sigsU8, sigsBatch.length, sigsGroupcheck ?? false);
5657

5758
if (res !== 0) {
5859
throw new Error(`Failed to aggregate signatures: ${res}`);
@@ -81,14 +82,8 @@ export function aggregateSerializedPublicKeys(
8182
for (let i = 0; i < pks.length; i += MAX_AGGREGATE_PER_JOB) {
8283
const pksBatch = pks.slice(i, Math.min(pks.length, i + MAX_AGGREGATE_PER_JOB));
8384
const pksRef = writeSerializedPublicKeysReference(pksBatch);
84-
const outPk = PublicKey.defaultPublicKey();
85-
const res = binding.aggregateSerializedPublicKeys(
86-
outPk.blst_point,
87-
pksRef,
88-
pksBatch.length,
89-
pks[0].length,
90-
pksValidate ?? false
91-
);
85+
const outPk = new PublicKey(new Uint8Array(PUBLIC_KEY_SIZE));
86+
const res = binding.aggregatePublicKeys(outPk.ptr, pksRef, pksBatch.length, pks[0].length, pksValidate ?? false);
9287

9388
if (res !== 0) {
9489
throw new Error(`Failed to aggregate serialized public keys: ${res}`);
@@ -117,22 +112,18 @@ export function aggregateSerializedSignatures(
117112
for (let i = 0; i < sigs.length; i += MAX_AGGREGATE_PER_JOB) {
118113
const sigsBatch = sigs.slice(i, Math.min(sigs.length, i + MAX_AGGREGATE_PER_JOB));
119114
const sigsRef = writeSerializedSignaturesReference(sigsBatch);
120-
const outSig = Signature.defaultSignature();
121-
const res = binding.aggregateSerializedSignatures(
122-
outSig.blst_point,
123-
sigsRef,
124-
sigsBatch.length,
125-
sigs[0].length,
126-
sigsGroupcheck ?? false
127-
);
115+
const outSig = new Signature(new Uint8Array(SIGNATURE_LENGTH));
116+
const res = binding.signatureAggregate(outSig.ptr, sigsRef, sigsBatch.length, sigsGroupcheck ?? false);
128117

129118
if (res !== 0) {
130119
throw new Error(`Failed to aggregate serialized signatures: ${res}`);
131120
}
132121
resultSignatures.push(outSig);
133122
}
134123

135-
return resultSignatures.length === 1 ? resultSignatures[0] : aggregateSignatures(resultSignatures, sigsGroupcheck);
124+
return resultSignatures.length === 1
125+
? resultSignatures[0]
126+
: aggregateSerializedSignatures(resultSignatures, sigsGroupcheck);
136127
}
137128

138129
function writeSerializedPublicKeysReference(pks: Uint8Array[]): Uint32Array {

bun/src/aggregateWithRandomness.ts

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import {JSCallback} from "bun:ffi";
2-
import {binding, writeNumber, writeReference} from "./binding.js";
3-
import {MAX_AGGREGATE_WITH_RANDOMNESS_PER_JOB} from "./const.js";
2+
import {binding} from "./binding.js";
3+
import {pksU8, writePublicKeys} from "./buffer.ts";
4+
import {MAX_AGGREGATE_WITH_RANDOMNESS_PER_JOB, PUBLIC_KEY_SIZE, SIGNATURE_LENGTH} from "./const.js";
45
import {PublicKey} from "./publicKey.js";
56
import {Signature} from "./signature.js";
7+
import {writeNumber, writePublicKeysReference, writeReference, writeSignaturesReference} from "./writers.ts";
68

79
export interface PkAndSerializedSig {
810
pk: PublicKey;
@@ -14,10 +16,6 @@ export interface PkAndSig {
1416
sig: Signature;
1517
}
1618

17-
// global signature sets reference to be reused across multiple calls
18-
// each 2 tems are 8 bytes, store the reference of each PkAndSerializedSig
19-
const pkAndSerializedSigsRefs = new Uint32Array(MAX_AGGREGATE_WITH_RANDOMNESS_PER_JOB * 2);
20-
2119
/**
2220
* Aggregate multiple public keys and multiple serialized signatures into a single blinded public key and blinded signature.
2321
*
@@ -33,14 +31,14 @@ export function aggregateWithRandomness(sets: Array<PkAndSerializedSig>): PkAndS
3331
throw new Error("At least one PkAndSerializedSig is required");
3432
}
3533

36-
const refs = pkAndSerializedSigsRefs.subarray(0, sets.length * 2);
37-
writePkAndSerializedSigsReference(sets, refs);
38-
const pkOut = PublicKey.defaultPublicKey();
39-
const sigOut = Signature.defaultSignature();
34+
const pksRef = writePublicKeysReference(sets.map((s) => s.pk));
35+
const sigsRef = writeSignaturesReference(sets.map((s) => Signature.fromBytes(s.sig, true)));
36+
const pkOut = new PublicKey(new Uint8Array(PUBLIC_KEY_SIZE));
37+
const sigOut = new Signature(new Uint8Array(SIGNATURE_LENGTH));
4038

41-
const res = binding.aggregateWithRandomness(refs, sets.length, pkOut.blst_point, sigOut.blst_point);
39+
const res = binding.aggregateWithRandomness(pkOut.ptr, sigOut.ptr, sets.length, pksRef, sigsRef, false, false);
4240

43-
if (res !== 0) {
41+
if (res) {
4442
throw new Error("Failed to aggregate with randomness res = " + res);
4543
}
4644

@@ -69,8 +67,8 @@ export function asyncAggregateWithRandomness(sets: Array<PkAndSerializedSig>): P
6967

7068
// 1s timeout
7169
const TIMEOUT_MS = 1_000;
72-
const pkOut = PublicKey.defaultPublicKey();
73-
const sigOut = Signature.defaultSignature();
70+
const pkOut = new PublicKey(new Uint8Array(PUBLIC_KEY_SIZE));
71+
const sigOut = new Signature(new Uint8Array(SIGNATURE_LENGTH));
7472

7573
return new Promise((resolve, reject) => {
7674
let jscallback: JSCallback | null = null;
@@ -107,15 +105,14 @@ export function asyncAggregateWithRandomness(sets: Array<PkAndSerializedSig>): P
107105
}
108106
);
109107

110-
// cannot reuse pkAndSerializedSigsRefs() due to async nature
111108
const refs = new Uint32Array(sets.length * 2);
112109
writePkAndSerializedSigsReference(sets, refs);
113110

114-
const res = binding.asyncAggregateWithRandomness(
111+
const res = binding.aggregateWithRandomness(
115112
refs,
116113
sets.length,
117-
pkOut.blst_point,
118-
sigOut.blst_point,
114+
pkOut.ptr,
115+
sigOut.ptr,
119116
// it's noted in bun:ffi doc that using JSCallback.prototype.ptr is faster than JSCallback object
120117
jscallback.ptr
121118
);
@@ -156,7 +153,7 @@ function writePkAndSerializedSigsReference(sets: PkAndSerializedSig[], out: Uint
156153
*
157154
*/
158155
function writePkAndSerializedSigReference(set: PkAndSerializedSig, out: Uint32Array, offset: number): void {
159-
set.pk.writeReference(out, offset);
156+
writeReference(set.pk, out, offset);
160157
writeReference(set.sig, out, offset + 2);
161158
writeNumber(set.sig.length, out, offset + 4);
162159
}

0 commit comments

Comments
 (0)