@dorafactory/maci-sdk 0.0.35 → 0.0.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +14 -2234
- package/dist/index.js +770 -348
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +741 -350
- package/dist/index.mjs.map +1 -1
- package/dist/libs/const.d.ts +118 -0
- package/dist/libs/contract/config.d.ts +29 -0
- package/dist/libs/contract/contract.d.ts +57 -0
- package/dist/libs/contract/index.d.ts +2 -0
- package/dist/libs/contract/ts/AMaci.client.d.ts +216 -0
- package/dist/libs/contract/ts/AMaci.types.d.ts +221 -0
- package/dist/libs/contract/ts/Maci.client.d.ts +180 -0
- package/dist/libs/contract/ts/Maci.types.d.ts +226 -0
- package/dist/libs/contract/ts/OracleMaci.client.d.ts +206 -0
- package/dist/libs/contract/ts/OracleMaci.types.d.ts +263 -0
- package/dist/libs/contract/ts/Registry.client.d.ts +128 -0
- package/dist/libs/contract/ts/Registry.types.d.ts +110 -0
- package/dist/libs/contract/types.d.ts +39 -0
- package/dist/libs/contract/utils.d.ts +58 -0
- package/dist/libs/contract/vars.d.ts +63 -0
- package/dist/libs/crypto/babyjub.d.ts +73 -0
- package/dist/libs/crypto/bigintUtils.d.ts +6 -0
- package/dist/libs/crypto/constants.d.ts +3 -0
- package/dist/libs/crypto/hashing.d.ts +69 -0
- package/dist/libs/crypto/index.d.ts +9 -0
- package/dist/libs/crypto/keys.d.ts +74 -0
- package/dist/libs/crypto/sign.d.ts +9 -0
- package/dist/libs/crypto/tree.d.ts +23 -0
- package/dist/libs/crypto/types.d.ts +55 -0
- package/dist/libs/errors/index.d.ts +28 -0
- package/dist/libs/errors/types.d.ts +19 -0
- package/dist/libs/http/http.d.ts +16 -0
- package/dist/libs/http/index.d.ts +1 -0
- package/dist/libs/index.d.ts +7 -0
- package/dist/libs/indexer/index.d.ts +1 -0
- package/dist/libs/indexer/indexer.d.ts +152 -0
- package/dist/libs/indexer/types.d.ts +7 -0
- package/dist/libs/maci/index.d.ts +1 -0
- package/dist/libs/maci/maci.d.ts +182 -0
- package/dist/libs/maci/types.d.ts +6 -0
- package/dist/libs/oracle-certificate/index.d.ts +1 -0
- package/dist/libs/oracle-certificate/oracle-certificate.d.ts +9 -0
- package/dist/libs/oracle-certificate/types.d.ts +40 -0
- package/dist/libs/query/account.d.ts +7 -0
- package/dist/libs/query/circuit.d.ts +8 -0
- package/dist/libs/query/event.d.ts +7 -0
- package/dist/libs/query/index.d.ts +7 -0
- package/dist/libs/query/operator.d.ts +11 -0
- package/dist/libs/query/proof.d.ts +7 -0
- package/dist/libs/query/round.d.ts +18 -0
- package/dist/libs/query/transaction.d.ts +9 -0
- package/dist/maci.d.ts +190 -0
- package/dist/types/index.d.ts +383 -0
- package/dist/utils/index.d.ts +7 -0
- package/package.json +18 -24
- package/src/index.ts +1 -3
- package/src/libs/contract/contract.ts +14 -13
- package/src/libs/contract/index.ts +1 -0
- package/src/libs/contract/types.ts +3 -3
- package/src/libs/contract/utils.ts +0 -24
- package/src/libs/crypto/babyjub.ts +132 -0
- package/src/libs/crypto/bigintUtils.ts +31 -0
- package/src/libs/crypto/constants.ts +22 -0
- package/src/libs/crypto/hashing.ts +167 -0
- package/src/libs/crypto/index.ts +9 -0
- package/src/libs/{circom/circomlib.ts → crypto/keys.ts} +104 -114
- package/src/libs/crypto/sign.ts +91 -0
- package/src/libs/crypto/tree.ts +176 -0
- package/src/libs/crypto/types.ts +101 -0
- package/src/libs/index.ts +1 -1
- package/src/libs/maci/maci.ts +28 -29
- package/src/maci.ts +132 -67
- package/src/types/index.ts +7 -0
- package/src/utils/index.ts +22 -22
- package/dist/browser.d.mts +0 -2233
- package/dist/browser.d.ts +0 -2233
- package/dist/browser.js +0 -31620
- package/dist/browser.js.map +0 -1
- package/dist/browser.mjs +0 -31631
- package/dist/browser.mjs.map +0 -1
- package/dist/index.d.mts +0 -2234
- package/src/browser.ts +0 -17
- package/src/libs/circom/index.ts +0 -98
- package/src/libs/circom/types.ts +0 -8
- package/src/polyfills/browser-polyfills.ts +0 -9
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import { randomBytes } from "crypto";
|
|
3
|
+
|
|
4
|
+
import type { PrivKey } from "./types";
|
|
5
|
+
|
|
6
|
+
import { SNARK_FIELD_SIZE } from "./constants";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @notice A class representing a point on the first group (G1)
|
|
10
|
+
* of the Jubjub curve
|
|
11
|
+
*/
|
|
12
|
+
export class G1Point {
|
|
13
|
+
x: bigint;
|
|
14
|
+
|
|
15
|
+
y: bigint;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Create a new instance of G1Point
|
|
19
|
+
* @param x the x coordinate
|
|
20
|
+
* @param y the y coordinate
|
|
21
|
+
*/
|
|
22
|
+
constructor(x: bigint, y: bigint) {
|
|
23
|
+
assert(x < SNARK_FIELD_SIZE && x >= 0, "G1Point x out of range");
|
|
24
|
+
assert(y < SNARK_FIELD_SIZE && y >= 0, "G1Point y out of range");
|
|
25
|
+
this.x = x;
|
|
26
|
+
this.y = y;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Check whether two points are equal
|
|
31
|
+
* @param pt the point to compare with
|
|
32
|
+
* @returns whether they are equal or not
|
|
33
|
+
*/
|
|
34
|
+
equals(pt: G1Point): boolean {
|
|
35
|
+
return this.x === pt.x && this.y === pt.y;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Return the point as a contract param in the form of an object
|
|
40
|
+
* @returns the point as a contract param
|
|
41
|
+
*/
|
|
42
|
+
asContractParam(): { x: string; y: string } {
|
|
43
|
+
return {
|
|
44
|
+
x: this.x.toString(),
|
|
45
|
+
y: this.y.toString(),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @notice A class representing a point on the second group (G2)
|
|
52
|
+
* of the Jubjub curve. This is usually an extension field of the
|
|
53
|
+
* base field of the curve.
|
|
54
|
+
*/
|
|
55
|
+
export class G2Point {
|
|
56
|
+
x: bigint[];
|
|
57
|
+
|
|
58
|
+
y: bigint[];
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Create a new instance of G2Point
|
|
62
|
+
* @param x the x coordinate
|
|
63
|
+
* @param y the y coordinate
|
|
64
|
+
*/
|
|
65
|
+
constructor(x: bigint[], y: bigint[]) {
|
|
66
|
+
this.checkPointsRange(x, "x");
|
|
67
|
+
this.checkPointsRange(y, "y");
|
|
68
|
+
|
|
69
|
+
this.x = x;
|
|
70
|
+
this.y = y;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Check whether two points are equal
|
|
75
|
+
* @param pt the point to compare with
|
|
76
|
+
* @returns whether they are equal or not
|
|
77
|
+
*/
|
|
78
|
+
equals(pt: G2Point): boolean {
|
|
79
|
+
return this.x[0] === pt.x[0] && this.x[1] === pt.x[1] && this.y[0] === pt.y[0] && this.y[1] === pt.y[1];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Return the point as a contract param in the form of an object
|
|
84
|
+
* @returns the point as a contract param
|
|
85
|
+
*/
|
|
86
|
+
asContractParam(): { x: string[]; y: string[] } {
|
|
87
|
+
return {
|
|
88
|
+
x: this.x.map((n) => n.toString()),
|
|
89
|
+
y: this.y.map((n) => n.toString()),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Check whether the points are in range
|
|
95
|
+
* @param x the x coordinate
|
|
96
|
+
* @param type the type of the coordinate
|
|
97
|
+
*/
|
|
98
|
+
private checkPointsRange(x: bigint[], type: "x" | "y") {
|
|
99
|
+
assert(
|
|
100
|
+
x.every((n) => n < SNARK_FIELD_SIZE && n >= 0),
|
|
101
|
+
`G2Point ${type} out of range`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Returns a BabyJub-compatible random value. We create it by first generating
|
|
108
|
+
* a random value (initially 256 bits large) modulo the snark field size as
|
|
109
|
+
* described in EIP197. This results in a key size of roughly 253 bits and no
|
|
110
|
+
* more than 254 bits. To prevent modulo bias, we then use this efficient
|
|
111
|
+
* algorithm:
|
|
112
|
+
* http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/lib/libc/crypt/arc4random_uniform.c
|
|
113
|
+
* @returns A BabyJub-compatible random value.
|
|
114
|
+
*/
|
|
115
|
+
export const genRandomBabyJubValue = (): bigint => {
|
|
116
|
+
// Prevent modulo bias
|
|
117
|
+
// const lim = BigInt('0x10000000000000000000000000000000000000000000000000000000000000000')
|
|
118
|
+
// const min = (lim - SNARK_FIELD_SIZE) % SNARK_FIELD_SIZE
|
|
119
|
+
const min = BigInt("6350874878119819312338956282401532410528162663560392320966563075034087161851");
|
|
120
|
+
|
|
121
|
+
let privKey: PrivKey = SNARK_FIELD_SIZE;
|
|
122
|
+
|
|
123
|
+
do {
|
|
124
|
+
const rand = BigInt(`0x${randomBytes(32).toString("hex")}`);
|
|
125
|
+
|
|
126
|
+
if (rand >= min) {
|
|
127
|
+
privKey = rand % SNARK_FIELD_SIZE;
|
|
128
|
+
}
|
|
129
|
+
} while (privKey >= SNARK_FIELD_SIZE);
|
|
130
|
+
|
|
131
|
+
return privKey;
|
|
132
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
type MixedData<T> = T | Array<MixedData<T>> | { [key: string]: MixedData<T> };
|
|
2
|
+
|
|
3
|
+
export const stringizing = (
|
|
4
|
+
o: MixedData<bigint>,
|
|
5
|
+
path: MixedData<bigint>[] = []
|
|
6
|
+
): MixedData<string> => {
|
|
7
|
+
if (path.includes(o)) {
|
|
8
|
+
throw new Error('loop nesting!');
|
|
9
|
+
}
|
|
10
|
+
const newPath = [...path, o];
|
|
11
|
+
|
|
12
|
+
if (Array.isArray(o)) {
|
|
13
|
+
return o.map((item) => stringizing(item, newPath));
|
|
14
|
+
} else if (typeof o === 'object') {
|
|
15
|
+
const output: { [key: string]: MixedData<string> } = {};
|
|
16
|
+
for (const key in o) {
|
|
17
|
+
output[key] = stringizing(o[key], newPath);
|
|
18
|
+
}
|
|
19
|
+
return output;
|
|
20
|
+
} else {
|
|
21
|
+
return o.toString();
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const bigInt2Buffer = (i: bigint) => {
|
|
26
|
+
let hex = i.toString(16);
|
|
27
|
+
if (hex.length % 2 === 1) {
|
|
28
|
+
hex = '0' + hex;
|
|
29
|
+
}
|
|
30
|
+
return Buffer.from(hex, 'hex');
|
|
31
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { r } from '@zk-kit/baby-jubjub';
|
|
2
|
+
import { keccak256, toUtf8Bytes } from 'ethers';
|
|
3
|
+
|
|
4
|
+
import assert from 'assert';
|
|
5
|
+
|
|
6
|
+
export const SNARK_FIELD_SIZE = r;
|
|
7
|
+
|
|
8
|
+
// A nothing-up-my-sleeve zero value
|
|
9
|
+
// Should be equal to 8370432830353022751713833565135785980866757267633941821328460903436894336785
|
|
10
|
+
export const NOTHING_UP_MY_SLEEVE =
|
|
11
|
+
BigInt(keccak256(toUtf8Bytes('Maci'))) % SNARK_FIELD_SIZE;
|
|
12
|
+
|
|
13
|
+
assert(
|
|
14
|
+
NOTHING_UP_MY_SLEEVE ===
|
|
15
|
+
BigInt(
|
|
16
|
+
'8370432830353022751713833565135785980866757267633941821328460903436894336785'
|
|
17
|
+
)
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export const PAD_KEY_HASH = BigInt(
|
|
21
|
+
'1309255631273308531193241901289907343161346846555918942743921933037802809814'
|
|
22
|
+
);
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { poseidonPerm } from '@zk-kit/poseidon-cipher';
|
|
2
|
+
import { solidityPackedSha256 } from 'ethers';
|
|
3
|
+
|
|
4
|
+
import assert from 'assert';
|
|
5
|
+
|
|
6
|
+
import type { Plaintext, PoseidonFuncs } from './types';
|
|
7
|
+
|
|
8
|
+
import { SNARK_FIELD_SIZE } from './constants';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Hash an array of uint256 values the same way that the EVM does.
|
|
12
|
+
* @param input - the array of values to hash
|
|
13
|
+
* @returns a EVM compatible sha256 hash
|
|
14
|
+
*/
|
|
15
|
+
export const sha256Hash = (input: bigint[]): bigint => {
|
|
16
|
+
const types: string[] = [];
|
|
17
|
+
|
|
18
|
+
input.forEach(() => {
|
|
19
|
+
types.push('uint256');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
BigInt(
|
|
24
|
+
solidityPackedSha256(
|
|
25
|
+
types,
|
|
26
|
+
input.map((x) => x.toString())
|
|
27
|
+
)
|
|
28
|
+
) % SNARK_FIELD_SIZE
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Generate the poseidon hash of the inputs provided
|
|
34
|
+
* @param inputs The inputs to hash
|
|
35
|
+
* @returns the hash of the inputs
|
|
36
|
+
*/
|
|
37
|
+
export const poseidon = (inputs: bigint[]): bigint =>
|
|
38
|
+
poseidonPerm([BigInt(0), ...inputs.map((x) => BigInt(x))])[0];
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Hash up to 2 elements
|
|
42
|
+
* @param inputs The elements to hash
|
|
43
|
+
* @returns the hash of the elements
|
|
44
|
+
*/
|
|
45
|
+
export const poseidonT3 = (inputs: bigint[]): bigint => {
|
|
46
|
+
assert(inputs.length === 2);
|
|
47
|
+
return poseidon(inputs);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Hash up to 3 elements
|
|
52
|
+
* @param inputs The elements to hash
|
|
53
|
+
* @returns the hash of the elements
|
|
54
|
+
*/
|
|
55
|
+
export const poseidonT4 = (inputs: bigint[]): bigint => {
|
|
56
|
+
assert(inputs.length === 3);
|
|
57
|
+
return poseidon(inputs);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Hash up to 4 elements
|
|
62
|
+
* @param inputs The elements to hash
|
|
63
|
+
* @returns the hash of the elements
|
|
64
|
+
*/
|
|
65
|
+
export const poseidonT5 = (inputs: bigint[]): bigint => {
|
|
66
|
+
assert(inputs.length === 4);
|
|
67
|
+
return poseidon(inputs);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Hash up to 5 elements
|
|
72
|
+
* @param inputs The elements to hash
|
|
73
|
+
* @returns the hash of the elements
|
|
74
|
+
*/
|
|
75
|
+
export const poseidonT6 = (inputs: bigint[]): bigint => {
|
|
76
|
+
assert(inputs.length === 5);
|
|
77
|
+
return poseidon(inputs);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Hash two BigInts with the Poseidon hash function
|
|
82
|
+
* @param left The left-hand element to hash
|
|
83
|
+
* @param right The right-hand element to hash
|
|
84
|
+
* @returns The hash of the two elements
|
|
85
|
+
*/
|
|
86
|
+
export const hashLeftRight = (left: bigint, right: bigint): bigint =>
|
|
87
|
+
poseidonT3([left, right]);
|
|
88
|
+
|
|
89
|
+
// hash functions
|
|
90
|
+
const funcs: PoseidonFuncs = {
|
|
91
|
+
2: poseidonT3,
|
|
92
|
+
3: poseidonT4,
|
|
93
|
+
4: poseidonT5,
|
|
94
|
+
5: poseidonT6,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Hash up to N elements
|
|
99
|
+
* @param numElements The number of elements to hash
|
|
100
|
+
* @param elements The elements to hash
|
|
101
|
+
* @returns The hash of the elements
|
|
102
|
+
*/
|
|
103
|
+
export const hashN = (numElements: number, elements: Plaintext): bigint => {
|
|
104
|
+
const elementLength = elements.length;
|
|
105
|
+
|
|
106
|
+
if (elements.length > numElements) {
|
|
107
|
+
throw new TypeError(
|
|
108
|
+
`the length of the elements array should be at most ${numElements}; got ${elements.length}`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
const elementsPadded = elements.slice();
|
|
112
|
+
|
|
113
|
+
if (elementLength < numElements) {
|
|
114
|
+
for (let i = elementLength; i < numElements; i += 1) {
|
|
115
|
+
elementsPadded.push(BigInt(0));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return funcs[numElements](elementsPadded);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// hash functions
|
|
123
|
+
export const hashLeanIMT = (a: bigint, b: bigint): bigint => hashN(2, [a, b]);
|
|
124
|
+
export const hash2 = (elements: Plaintext): bigint => hashN(2, elements);
|
|
125
|
+
export const hash3 = (elements: Plaintext): bigint => hashN(3, elements);
|
|
126
|
+
export const hash4 = (elements: Plaintext): bigint => hashN(4, elements);
|
|
127
|
+
export const hash5 = (elements: Plaintext): bigint => hashN(5, elements);
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* A convenience function to use Poseidon to hash a Plaintext with
|
|
131
|
+
* no more than 13 elements
|
|
132
|
+
* @param elements The elements to hash
|
|
133
|
+
* @returns The hash of the elements
|
|
134
|
+
*/
|
|
135
|
+
export const hash12 = (elements: Plaintext): bigint => {
|
|
136
|
+
const max = 12;
|
|
137
|
+
const elementLength = elements.length;
|
|
138
|
+
|
|
139
|
+
if (elementLength > max) {
|
|
140
|
+
throw new TypeError(
|
|
141
|
+
`the length of the elements array should be at most ${max}; got ${elements.length}`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const elementsPadded = elements.slice();
|
|
146
|
+
|
|
147
|
+
if (elementLength < max) {
|
|
148
|
+
for (let i = elementLength; i < max; i += 1) {
|
|
149
|
+
elementsPadded.push(BigInt(0));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return poseidonT5([
|
|
154
|
+
poseidonT6(elementsPadded.slice(0, 5)),
|
|
155
|
+
poseidonT6(elementsPadded.slice(5, 10)),
|
|
156
|
+
elementsPadded[10],
|
|
157
|
+
elementsPadded[11],
|
|
158
|
+
]);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Hash a single BigInt with the Poseidon hash function
|
|
163
|
+
* @param preImage The element to hash
|
|
164
|
+
* @returns The hash of the element
|
|
165
|
+
*/
|
|
166
|
+
export const hashOne = (preImage: bigint): bigint =>
|
|
167
|
+
poseidonT3([preImage, BigInt(0)]);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './keys';
|
|
2
|
+
export * from './bigintUtils';
|
|
3
|
+
export * from './constants';
|
|
4
|
+
export * from './hashing';
|
|
5
|
+
export * from './sign';
|
|
6
|
+
export * from './types';
|
|
7
|
+
export * from './tree';
|
|
8
|
+
export * from './babyjub';
|
|
9
|
+
export type { Keypair, PubKey, PrivKey } from './types';
|
|
@@ -1,126 +1,113 @@
|
|
|
1
1
|
import { randomBytes } from 'crypto';
|
|
2
|
+
import { bigInt2Buffer, stringizing } from './bigintUtils';
|
|
3
|
+
import { poseidonEncrypt } from '@zk-kit/poseidon-cipher';
|
|
4
|
+
import * as BabyJub from '@zk-kit/baby-jubjub';
|
|
5
|
+
import { Point } from '@zk-kit/baby-jubjub';
|
|
2
6
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Tree,
|
|
8
|
-
} from '@dorafactory/circomlib';
|
|
9
|
-
import { Scalar, utils } from 'ffjavascript';
|
|
10
|
-
import createBlakeHash from 'blake-hash';
|
|
11
|
-
import { solidityPackedSha256 } from 'ethers';
|
|
7
|
+
derivePublicKey,
|
|
8
|
+
signMessage,
|
|
9
|
+
deriveSecretScalar,
|
|
10
|
+
} from '@zk-kit/eddsa-poseidon';
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
import { solidityPackedSha256 } from 'ethers';
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
import { mulPointEscalar } from '@zk-kit/baby-jubjub';
|
|
15
|
+
import { packPublicKey, unpackPublicKey } from '@zk-kit/eddsa-poseidon';
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
17
|
+
import { genRandomBabyJubValue } from './babyjub';
|
|
18
|
+
import { EcdhSharedKey, Keypair, PrivKey, PubKey } from './types';
|
|
19
|
+
import { poseidon } from './hashing';
|
|
20
|
+
import Tree from './tree';
|
|
23
21
|
|
|
24
22
|
const SNARK_FIELD_SIZE =
|
|
25
23
|
21888242871839275222246405745257275088548364400416034343698204186575808495617n;
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Generate a private key
|
|
27
|
+
* @returns A random seed for a private key.
|
|
28
|
+
*/
|
|
29
|
+
export const genPrivKey = (): bigint =>
|
|
30
|
+
BigInt(`0x${randomBytes(32).toString('hex')}`);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Generate a random value
|
|
34
|
+
* @returns A BabyJub-compatible salt.
|
|
35
|
+
*/
|
|
36
|
+
export const genRandomSalt = (): bigint => genRandomBabyJubValue();
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* An internal function which formats a random private key to be compatible
|
|
40
|
+
* with the BabyJub curve. This is the format which should be passed into the
|
|
41
|
+
* PubKey and other circuits.
|
|
42
|
+
* @param privKey A private key generated using genPrivKey()
|
|
43
|
+
* @returns A BabyJub-compatible private key.
|
|
44
|
+
*/
|
|
45
|
+
export const formatPrivKeyForBabyJub = (privKey: PrivKey): bigint =>
|
|
46
|
+
BigInt(deriveSecretScalar(bigInt2Buffer(privKey)));
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Losslessly reduces the size of the representation of a public key
|
|
50
|
+
* @param pubKey The public key to pack
|
|
51
|
+
* @returns A packed public key
|
|
52
|
+
*/
|
|
53
|
+
export const packPubKey = (pubKey: PubKey): bigint =>
|
|
54
|
+
BigInt(packPublicKey(pubKey));
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Restores the original PubKey from its packed representation
|
|
58
|
+
* @param packed The value to unpack
|
|
59
|
+
* @returns The unpacked public key
|
|
60
|
+
*/
|
|
61
|
+
export const unpackPubKey = (packed: bigint): PubKey => {
|
|
62
|
+
const pubKey = unpackPublicKey(packed);
|
|
63
|
+
return pubKey.map((x) => BigInt(x)) as PubKey;
|
|
53
64
|
};
|
|
54
65
|
|
|
55
|
-
|
|
56
|
-
|
|
66
|
+
/**
|
|
67
|
+
* @param privKey A private key generated using genPrivKey()
|
|
68
|
+
* @returns A public key associated with the private key
|
|
69
|
+
*/
|
|
70
|
+
export const genPubKey = (privKey: PrivKey): PubKey => {
|
|
71
|
+
const key = derivePublicKey(bigInt2Buffer(privKey));
|
|
72
|
+
return [BigInt(key[0]), BigInt(key[1])];
|
|
57
73
|
};
|
|
58
74
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
const newPath = [...path, o];
|
|
67
|
-
|
|
68
|
-
if (Array.isArray(o)) {
|
|
69
|
-
return o.map((item) => stringizing(item, newPath));
|
|
70
|
-
} else if (typeof o === 'object') {
|
|
71
|
-
const output: { [key: string]: MixedData<string> } = {};
|
|
72
|
-
for (const key in o) {
|
|
73
|
-
output[key] = stringizing(o[key], newPath);
|
|
74
|
-
}
|
|
75
|
-
return output;
|
|
76
|
-
} else {
|
|
77
|
-
return o.toString();
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
export const genKeypair = (pkey?: PrivateKey): Account => {
|
|
82
|
-
const privKey = pkey ? pkey % SNARK_FIELD_SIZE : genRandomKey();
|
|
75
|
+
/**
|
|
76
|
+
* Generates a keypair.
|
|
77
|
+
* @returns a keypair
|
|
78
|
+
*/
|
|
79
|
+
export const genKeypair = (pkey?: PrivKey): Keypair => {
|
|
80
|
+
const privKey = pkey ? pkey % SNARK_FIELD_SIZE : genPrivKey();
|
|
83
81
|
const pubKey = genPubKey(privKey);
|
|
84
82
|
const formatedPrivKey = formatPrivKeyForBabyJub(privKey);
|
|
85
83
|
|
|
86
|
-
|
|
87
|
-
};
|
|
84
|
+
const keypair: Keypair = { privKey, pubKey, formatedPrivKey };
|
|
88
85
|
|
|
89
|
-
|
|
90
|
-
const sBuff = eddsa.pruneBuffer(
|
|
91
|
-
createBlakeHash('blake512')
|
|
92
|
-
.update(bigInt2Buffer(privKey))
|
|
93
|
-
.digest()
|
|
94
|
-
.slice(0, 32)
|
|
95
|
-
);
|
|
96
|
-
const s = utils.leBuff2int(sBuff);
|
|
97
|
-
return Scalar.shr(s, 3);
|
|
86
|
+
return keypair;
|
|
98
87
|
};
|
|
99
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Generates an Elliptic-Curve Diffie–Hellman (ECDH) shared key given a private
|
|
91
|
+
* key and a public key.
|
|
92
|
+
* @param privKey A private key generated using genPrivKey()
|
|
93
|
+
* @param pubKey A public key generated using genPubKey()
|
|
94
|
+
* @returns The ECDH shared key.
|
|
95
|
+
*/
|
|
100
96
|
export const genEcdhSharedKey = (
|
|
101
|
-
privKey:
|
|
102
|
-
pubKey:
|
|
103
|
-
):
|
|
104
|
-
|
|
105
|
-
pubKey,
|
|
106
|
-
formatPrivKeyForBabyJub(privKey)
|
|
107
|
-
);
|
|
108
|
-
if (sharedKey[0] === 0n) {
|
|
109
|
-
return [0n, 1n];
|
|
110
|
-
} else {
|
|
111
|
-
return sharedKey;
|
|
112
|
-
}
|
|
113
|
-
};
|
|
97
|
+
privKey: PrivKey,
|
|
98
|
+
pubKey: PubKey
|
|
99
|
+
): EcdhSharedKey =>
|
|
100
|
+
mulPointEscalar(pubKey as Point<bigint>, formatPrivKeyForBabyJub(privKey));
|
|
114
101
|
|
|
115
102
|
export const genMessageFactory =
|
|
116
103
|
(
|
|
117
104
|
stateIdx: number,
|
|
118
|
-
signPriKey:
|
|
119
|
-
signPubKey:
|
|
120
|
-
coordPubKey:
|
|
105
|
+
signPriKey: PrivKey,
|
|
106
|
+
signPubKey: PubKey,
|
|
107
|
+
coordPubKey: PubKey
|
|
121
108
|
) =>
|
|
122
109
|
(
|
|
123
|
-
encPriKey:
|
|
110
|
+
encPriKey: PrivKey,
|
|
124
111
|
nonce: number,
|
|
125
112
|
voIdx: number,
|
|
126
113
|
newVotes: number,
|
|
@@ -145,7 +132,7 @@ export const genMessageFactory =
|
|
|
145
132
|
}
|
|
146
133
|
|
|
147
134
|
const hash = poseidon([packaged, ...newPubKey]);
|
|
148
|
-
const signature =
|
|
135
|
+
const signature = signMessage(bigInt2Buffer(signPriKey), hash);
|
|
149
136
|
|
|
150
137
|
const command = [packaged, ...newPubKey, ...signature.R8, signature.S];
|
|
151
138
|
|
|
@@ -167,14 +154,14 @@ export const genMessageFactory =
|
|
|
167
154
|
// and change the public key at command_N
|
|
168
155
|
export const batchGenMessage = (
|
|
169
156
|
stateIdx: number,
|
|
170
|
-
|
|
171
|
-
coordPubKey:
|
|
157
|
+
keypair: Keypair,
|
|
158
|
+
coordPubKey: PubKey,
|
|
172
159
|
plan: [number, number][]
|
|
173
160
|
) => {
|
|
174
161
|
const genMessage = genMessageFactory(
|
|
175
162
|
stateIdx,
|
|
176
|
-
|
|
177
|
-
|
|
163
|
+
BigInt(keypair.privKey),
|
|
164
|
+
keypair.pubKey,
|
|
178
165
|
coordPubKey
|
|
179
166
|
);
|
|
180
167
|
|
|
@@ -183,7 +170,7 @@ export const batchGenMessage = (
|
|
|
183
170
|
const p = plan[i];
|
|
184
171
|
const encAccount = genKeypair();
|
|
185
172
|
const msg = genMessage(
|
|
186
|
-
encAccount.privKey,
|
|
173
|
+
BigInt(encAccount.privKey),
|
|
187
174
|
i + 1,
|
|
188
175
|
p[0],
|
|
189
176
|
p[1],
|
|
@@ -218,16 +205,16 @@ export const privateKeyFromTxt = (txt: string) => {
|
|
|
218
205
|
const rerandomize = (
|
|
219
206
|
pubKey: bigint[],
|
|
220
207
|
ciphertext: { c1: bigint[]; c2: bigint[] },
|
|
221
|
-
randomVal =
|
|
208
|
+
randomVal = genRandomSalt()
|
|
222
209
|
) => {
|
|
223
|
-
const d1 =
|
|
224
|
-
|
|
225
|
-
ciphertext.c1
|
|
210
|
+
const d1 = BabyJub.addPoint(
|
|
211
|
+
BabyJub.mulPointEscalar(BabyJub.Base8, randomVal),
|
|
212
|
+
ciphertext.c1 as Point<bigint>
|
|
226
213
|
);
|
|
227
214
|
|
|
228
|
-
const d2 =
|
|
229
|
-
|
|
230
|
-
ciphertext.c2
|
|
215
|
+
const d2 = BabyJub.addPoint(
|
|
216
|
+
BabyJub.mulPointEscalar(pubKey as Point<bigint>, randomVal),
|
|
217
|
+
ciphertext.c2 as Point<bigint>
|
|
231
218
|
);
|
|
232
219
|
|
|
233
220
|
return {
|
|
@@ -243,14 +230,14 @@ export const genAddKeyProof = async (
|
|
|
243
230
|
oldKey,
|
|
244
231
|
deactivates,
|
|
245
232
|
}: {
|
|
246
|
-
coordPubKey:
|
|
247
|
-
oldKey:
|
|
233
|
+
coordPubKey: PubKey;
|
|
234
|
+
oldKey: Keypair;
|
|
248
235
|
deactivates: bigint[][];
|
|
249
236
|
}
|
|
250
237
|
) => {
|
|
251
238
|
const sharedKeyHash = poseidon(genEcdhSharedKey(oldKey.privKey, coordPubKey));
|
|
252
239
|
|
|
253
|
-
const randomVal =
|
|
240
|
+
const randomVal = genRandomSalt();
|
|
254
241
|
const deactivateIdx = deactivates.findIndex((d) => d[4] === sharedKeyHash);
|
|
255
242
|
if (deactivateIdx < 0) {
|
|
256
243
|
return null;
|
|
@@ -263,7 +250,10 @@ export const genAddKeyProof = async (
|
|
|
263
250
|
|
|
264
251
|
const { d1, d2 } = rerandomize(coordPubKey, { c1, c2 }, randomVal);
|
|
265
252
|
|
|
266
|
-
const nullifier = poseidon([
|
|
253
|
+
const nullifier = poseidon([
|
|
254
|
+
BigInt(oldKey.formatedPrivKey),
|
|
255
|
+
1444992409218394441042n,
|
|
256
|
+
]);
|
|
267
257
|
|
|
268
258
|
const tree = new Tree(5, depth, 0n);
|
|
269
259
|
const leaves = deactivates.map((d) => poseidon(d));
|