Skip to content

Commit a2957fa

Browse files
authored
Merge pull request #3 from DoraFactory/feature/proof-compression
Feature/proof compression
2 parents 461aba5 + 7b7d220 commit a2957fa

File tree

6 files changed

+147
-5
lines changed

6 files changed

+147
-5
lines changed

packages/voter/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
},
4848
"dependencies": {
4949
"snarkjs": "0.7.5",
50+
"ffjavascript": "0.3.1",
5051
"@noble/curves": "^1.4.2",
5152
"@noble/hashes": "^1.4.0",
5253
"@scure/bip32": "^1.3.3",
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Groth16Proof } from 'snarkjs';
2+
import { utils } from 'ffjavascript';
3+
const { unstringifyBigInts } = utils;
4+
5+
import * as curves from './curve';
6+
7+
const Bytes2Str = (arr: number[]) => {
8+
let str = '';
9+
for (let i = 0; i < arr.length; i++) {
10+
let tmp = arr[i].toString(16);
11+
if (tmp.length == 1) {
12+
tmp = '0' + tmp;
13+
}
14+
str += tmp;
15+
}
16+
return str;
17+
};
18+
19+
let BN128Curve: any = null;
20+
21+
export const adaptToUncompressed = async (proof: Groth16Proof) => {
22+
const p = unstringifyBigInts(proof);
23+
24+
let curve = BN128Curve;
25+
if (!curve) {
26+
BN128Curve = await curves.getCurveFromName('BN128');
27+
curve = BN128Curve;
28+
}
29+
30+
// convert u8 array(little-endian order)to uncompressed type(big-endian order and on bls12_381 curve)
31+
// which can be convert into Affine type in bellman
32+
const pi_a = curve.G1.toUncompressed(curve.G1.fromObject(p.pi_a));
33+
const pi_b = curve.G2.toUncompressed(curve.G2.fromObject(p.pi_b));
34+
const pi_c = curve.G1.toUncompressed(curve.G1.fromObject(p.pi_c));
35+
36+
return {
37+
a: Bytes2Str(Array.from(pi_a)),
38+
b: Bytes2Str(Array.from(pi_b)),
39+
c: Bytes2Str(Array.from(pi_c)),
40+
};
41+
};

packages/voter/src/crypto/curve.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { Scalar, buildBn128, buildBls12381 } from 'ffjavascript';
2+
3+
const bls12381r = Scalar.e(
4+
'73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001',
5+
16
6+
);
7+
const bn128r = Scalar.e(
8+
'21888242871839275222246405745257275088548364400416034343698204186575808495617'
9+
);
10+
11+
const bls12381q = Scalar.e(
12+
'1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab',
13+
16
14+
);
15+
const bn128q = Scalar.e(
16+
'21888242871839275222246405745257275088696311157297823662689037894645226208583'
17+
);
18+
19+
export async function getCurveFromR(r: bigint) {
20+
let curve;
21+
if (Scalar.eq(r, bn128r)) {
22+
curve = await buildBn128();
23+
} else if (Scalar.eq(r, bls12381r)) {
24+
curve = await buildBls12381();
25+
} else {
26+
throw new Error(`Curve not supported: ${Scalar.toString(r)}`);
27+
}
28+
return curve;
29+
}
30+
31+
export async function getCurveFromQ(q: bigint) {
32+
let curve;
33+
if (Scalar.eq(q, bn128q)) {
34+
curve = await buildBn128();
35+
} else if (Scalar.eq(q, bls12381q)) {
36+
curve = await buildBls12381();
37+
} else {
38+
throw new Error(`Curve not supported: ${Scalar.toString(q)}`);
39+
}
40+
return curve;
41+
}
42+
43+
export async function getCurveFromName(name: string) {
44+
let curve;
45+
const normName = normalizeName(name);
46+
if (['BN128', 'BN254', 'ALTBN128'].indexOf(normName) >= 0) {
47+
curve = await buildBn128();
48+
} else if (['BLS12381'].indexOf(normName) >= 0) {
49+
curve = await buildBls12381();
50+
} else {
51+
throw new Error(`Curve not supported: ${name}`);
52+
}
53+
return curve;
54+
55+
function normalizeName(n: string) {
56+
return (n.toUpperCase().match(/[A-Za-z0-9]+/g) || []).join('');
57+
}
58+
}

packages/voter/src/crypto/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@ export * from './hashing';
55
export * from './types';
66
export * from './tree';
77
export * from './babyjub';
8+
export * from './curve';
9+
export * from './adapter';
810
export type { Keypair, PubKey, PrivKey } from './types';

packages/voter/src/types/lib.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
declare module 'ffjavascript' {
2+
export const utils: any;
3+
// eslint-disable-next-line @typescript-eslint/naming-convention
4+
export const Scalar: any;
5+
export const buildBn128: any;
6+
export const buildBls12381: any;
7+
}

packages/voter/src/voter.ts

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import CryptoJS from 'crypto-js';
22
import { solidityPackedSha256 } from 'ethers';
3+
import snarkjs from 'snarkjs';
34

45
import { MaciAccount } from './account';
56
import {
@@ -12,6 +13,7 @@ import {
1213
Tree,
1314
stringizing,
1415
SNARK_FIELD_SIZE,
16+
adaptToUncompressed,
1517
} from './crypto';
1618
import { poseidon } from './crypto/hashing';
1719
import { poseidonEncrypt } from '@zk-kit/poseidon-cipher';
@@ -102,7 +104,7 @@ export class VoterClient {
102104
derivePathParams
103105
);
104106

105-
return payload;
107+
return stringizing(payload);
106108
}
107109

108110
batchGenMessage(
@@ -201,28 +203,59 @@ export class VoterClient {
201203
stateTreeDepth,
202204
operatorPubkey,
203205
deactivates,
206+
wasmFile,
207+
zkeyFile,
204208
derivePathParams,
205209
}: {
206210
stateTreeDepth: number;
207211
operatorPubkey: bigint;
208212
deactivates: DeactivateMessage[];
213+
wasmFile: string;
214+
zkeyFile: string;
209215
derivePathParams?: DerivePathParams;
210-
}) {
216+
}): Promise<{
217+
proof: {
218+
a: string;
219+
b: string;
220+
c: string;
221+
};
222+
d: string[];
223+
nullifier: string;
224+
}> {
211225
const [coordPubkeyX, coordPubkeyY] =
212226
this.unpackMaciPubkey(operatorPubkey);
213227
// const stateTreeDepth = Number(circuitPower.split('-')[0]);
214-
const inputObj = this.genAddKeyInput(stateTreeDepth + 2, {
228+
const addKeyInput = await this.genAddKeyInput(stateTreeDepth + 2, {
215229
coordPubKey: [coordPubkeyX, coordPubkeyY],
216230
deactivates: deactivates.map((d: any) => d.map(BigInt)),
217231
derivePathParams,
218232
});
219-
return inputObj;
233+
234+
if (addKeyInput === null) {
235+
throw Error('genAddKeyInput failed');
236+
}
220237

221238
// 1. generate proof
239+
const { proof } = await snarkjs.groth16.fullProve(
240+
addKeyInput,
241+
wasmFile,
242+
zkeyFile
243+
);
222244

223245
// 2. compress proof to vote proof
246+
const proofHex = await adaptToUncompressed(proof);
224247

225248
// 3. send addNewKey tx
249+
return {
250+
proof: proofHex,
251+
d: [
252+
addKeyInput.d1[0].toString(),
253+
addKeyInput.d1[1].toString(),
254+
addKeyInput.d2[0].toString(),
255+
addKeyInput.d2[1].toString(),
256+
],
257+
nullifier: addKeyInput.nullifier.toString(),
258+
};
226259
}
227260

228261
async genAddKeyInput(
@@ -318,6 +351,6 @@ export class VoterClient {
318351
[[0, 0]],
319352
derivePathParams
320353
);
321-
return payload;
354+
return stringizing(payload[0]);
322355
}
323356
}

0 commit comments

Comments
 (0)