@noble/curves 0.5.1 → 0.6.0
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/README.md +49 -8
- package/lib/_shortw_utils.d.ts +11 -26
- package/lib/abstract/bls.d.ts +51 -35
- package/lib/abstract/bls.js +77 -139
- package/lib/abstract/{group.d.ts → curve.d.ts} +31 -1
- package/lib/abstract/{group.js → curve.js} +39 -2
- package/lib/abstract/edwards.d.ts +30 -81
- package/lib/abstract/edwards.js +225 -420
- package/lib/abstract/hash-to-curve.d.ts +25 -6
- package/lib/abstract/hash-to-curve.js +40 -12
- package/lib/abstract/modular.d.ts +20 -7
- package/lib/abstract/modular.js +80 -51
- package/lib/abstract/montgomery.js +3 -4
- package/lib/abstract/poseidon.d.ts +29 -0
- package/lib/abstract/poseidon.js +115 -0
- package/lib/abstract/utils.d.ts +5 -34
- package/lib/abstract/utils.js +23 -63
- package/lib/abstract/weierstrass.d.ts +56 -79
- package/lib/abstract/weierstrass.js +509 -641
- package/lib/bls12-381.d.ts +1 -0
- package/lib/bls12-381.js +75 -65
- package/lib/bn.js +1 -1
- package/lib/ed25519.d.ts +7 -5
- package/lib/ed25519.js +87 -84
- package/lib/ed448.d.ts +3 -0
- package/lib/ed448.js +88 -84
- package/lib/esm/abstract/bls.js +77 -139
- package/lib/esm/abstract/{group.js → curve.js} +37 -1
- package/lib/esm/abstract/edwards.js +223 -418
- package/lib/esm/abstract/hash-to-curve.js +38 -11
- package/lib/esm/abstract/modular.js +77 -50
- package/lib/esm/abstract/montgomery.js +4 -7
- package/lib/esm/abstract/poseidon.js +109 -0
- package/lib/esm/abstract/utils.js +21 -59
- package/lib/esm/abstract/weierstrass.js +508 -640
- package/lib/esm/bls12-381.js +86 -76
- package/lib/esm/bn.js +1 -1
- package/lib/esm/ed25519.js +85 -83
- package/lib/esm/ed448.js +86 -83
- package/lib/esm/jubjub.js +6 -5
- package/lib/esm/p256.js +11 -9
- package/lib/esm/p384.js +11 -9
- package/lib/esm/p521.js +13 -12
- package/lib/esm/secp256k1.js +118 -157
- package/lib/esm/stark.js +104 -39
- package/lib/jubjub.d.ts +3 -2
- package/lib/jubjub.js +6 -5
- package/lib/p192.d.ts +22 -52
- package/lib/p224.d.ts +22 -52
- package/lib/p256.d.ts +25 -52
- package/lib/p256.js +13 -10
- package/lib/p384.d.ts +25 -52
- package/lib/p384.js +13 -10
- package/lib/p521.d.ts +25 -52
- package/lib/p521.js +15 -13
- package/lib/secp256k1.d.ts +26 -42
- package/lib/secp256k1.js +118 -157
- package/lib/stark.d.ts +36 -21
- package/lib/stark.js +107 -39
- package/package.json +14 -9
package/lib/esm/secp256k1.js
CHANGED
|
@@ -3,14 +3,12 @@ import { sha256 } from '@noble/hashes/sha256';
|
|
|
3
3
|
import { Fp as Field, mod, pow2 } from './abstract/modular.js';
|
|
4
4
|
import { createCurve } from './_shortw_utils.js';
|
|
5
5
|
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
|
6
|
-
import { ensureBytes, concatBytes,
|
|
6
|
+
import { ensureBytes, concatBytes, bytesToNumberBE as bytesToNum, numberToBytesBE, } from './abstract/utils.js';
|
|
7
7
|
import { randomBytes } from '@noble/hashes/utils';
|
|
8
|
-
import
|
|
8
|
+
import * as htf from './abstract/hash-to-curve.js';
|
|
9
9
|
/**
|
|
10
|
-
* secp256k1 belongs to Koblitz curves: it has
|
|
11
|
-
*
|
|
12
|
-
* Endomorphism improves efficiency:
|
|
13
|
-
* Uses 2x less RAM, speeds up precomputation by 2x and ECDH / sign key recovery by 20%.
|
|
10
|
+
* secp256k1 belongs to Koblitz curves: it has efficiently computable endomorphism.
|
|
11
|
+
* Endomorphism uses 2x less RAM, speeds up precomputation by 2x and ECDH / key recovery by 20%.
|
|
14
12
|
* Should always be used for Projective's double-and-add multiplication.
|
|
15
13
|
* For affines cached multiplication, it trades off 1/2 init time & 1/3 ram for 20% perf hit.
|
|
16
14
|
* https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
|
|
@@ -21,10 +19,7 @@ const _1n = BigInt(1);
|
|
|
21
19
|
const _2n = BigInt(2);
|
|
22
20
|
const divNearest = (a, b) => (a + b / _2n) / b;
|
|
23
21
|
/**
|
|
24
|
-
*
|
|
25
|
-
* To calculate √y, we need to exponentiate it to a very big number:
|
|
26
|
-
* `y² = x³ + ax + b; y = y² ^ (p+1)/4`
|
|
27
|
-
* We are unwrapping the loop and multiplying it bit-by-bit.
|
|
22
|
+
* √n = n^((p+1)/4) for fields p = 3 mod 4. We unwrap the loop and multiply bit-by-bit.
|
|
28
23
|
* (P+1n/4n).toString(2) would produce bits [223x 1, 0, 22x 1, 4x 0, 11, 00]
|
|
29
24
|
*/
|
|
30
25
|
function sqrtMod(y) {
|
|
@@ -47,45 +42,11 @@ function sqrtMod(y) {
|
|
|
47
42
|
const t1 = (pow2(b223, _23n, P) * b22) % P;
|
|
48
43
|
const t2 = (pow2(t1, _6n, P) * b2) % P;
|
|
49
44
|
const root = pow2(t2, _2n, P);
|
|
50
|
-
if (!Fp.
|
|
45
|
+
if (!Fp.eql(Fp.sqr(root), y))
|
|
51
46
|
throw new Error('Cannot find square root');
|
|
52
47
|
return root;
|
|
53
48
|
}
|
|
54
49
|
const Fp = Field(secp256k1P, undefined, undefined, { sqrt: sqrtMod });
|
|
55
|
-
const isoMap = isogenyMap(Fp, [
|
|
56
|
-
// xNum
|
|
57
|
-
[
|
|
58
|
-
'0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c7',
|
|
59
|
-
'0x7d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c6581',
|
|
60
|
-
'0x534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262',
|
|
61
|
-
'0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c',
|
|
62
|
-
],
|
|
63
|
-
// xDen
|
|
64
|
-
[
|
|
65
|
-
'0xd35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b',
|
|
66
|
-
'0xedadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d14',
|
|
67
|
-
'0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
|
|
68
|
-
],
|
|
69
|
-
// yNum
|
|
70
|
-
[
|
|
71
|
-
'0x4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c',
|
|
72
|
-
'0xc75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a3',
|
|
73
|
-
'0x29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931',
|
|
74
|
-
'0x2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d84',
|
|
75
|
-
],
|
|
76
|
-
// yDen
|
|
77
|
-
[
|
|
78
|
-
'0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffff93b',
|
|
79
|
-
'0x7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c2573',
|
|
80
|
-
'0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f',
|
|
81
|
-
'0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
|
|
82
|
-
],
|
|
83
|
-
].map((i) => i.map((j) => BigInt(j))));
|
|
84
|
-
const mapSWU = mapToCurveSimpleSWU(Fp, {
|
|
85
|
-
A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'),
|
|
86
|
-
B: BigInt('1771'),
|
|
87
|
-
Z: Fp.create(BigInt('-11')),
|
|
88
|
-
});
|
|
89
50
|
export const secp256k1 = createCurve({
|
|
90
51
|
// Params: a, b
|
|
91
52
|
// Seem to be rigid https://bitcointalk.org/index.php?topic=289795.msg3183975#msg3183975
|
|
@@ -111,7 +72,7 @@ export const secp256k1 = createCurve({
|
|
|
111
72
|
const b1 = -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3');
|
|
112
73
|
const a2 = BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8');
|
|
113
74
|
const b2 = a1;
|
|
114
|
-
const POW_2_128 = BigInt('0x100000000000000000000000000000000');
|
|
75
|
+
const POW_2_128 = BigInt('0x100000000000000000000000000000000'); // (2n**128n).toString(16)
|
|
115
76
|
const c1 = divNearest(b2 * k, n);
|
|
116
77
|
const c2 = divNearest(-b1 * k, n);
|
|
117
78
|
let k1 = mod(k - c1 * a1 - c2 * a2, n);
|
|
@@ -128,53 +89,13 @@ export const secp256k1 = createCurve({
|
|
|
128
89
|
return { k1neg, k1, k2neg, k2 };
|
|
129
90
|
},
|
|
130
91
|
},
|
|
131
|
-
mapToCurve: (scalars) => {
|
|
132
|
-
const { x, y } = mapSWU(Fp.create(scalars[0]));
|
|
133
|
-
return isoMap(x, y);
|
|
134
|
-
},
|
|
135
|
-
htfDefaults: {
|
|
136
|
-
DST: 'secp256k1_XMD:SHA-256_SSWU_RO_',
|
|
137
|
-
p: Fp.ORDER,
|
|
138
|
-
m: 1,
|
|
139
|
-
k: 128,
|
|
140
|
-
expand: 'xmd',
|
|
141
|
-
hash: sha256,
|
|
142
|
-
},
|
|
143
92
|
}, sha256);
|
|
144
|
-
// Schnorr
|
|
93
|
+
// Schnorr signatures are superior to ECDSA from above.
|
|
94
|
+
// Below is Schnorr-specific code as per BIP0340.
|
|
95
|
+
// https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
|
|
145
96
|
const _0n = BigInt(0);
|
|
146
|
-
const
|
|
147
|
-
const
|
|
148
|
-
const normalizePrivateKey = secp256k1.utils._normalizePrivateKey;
|
|
149
|
-
// TODO: export?
|
|
150
|
-
function normalizePublicKey(publicKey) {
|
|
151
|
-
if (publicKey instanceof secp256k1.Point) {
|
|
152
|
-
publicKey.assertValidity();
|
|
153
|
-
return publicKey;
|
|
154
|
-
}
|
|
155
|
-
else {
|
|
156
|
-
const bytes = ensureBytes(publicKey);
|
|
157
|
-
// Schnorr is 32 bytes
|
|
158
|
-
if (bytes.length === 32) {
|
|
159
|
-
const x = bytesToNumberBE(bytes);
|
|
160
|
-
if (!isValidFieldElement(x))
|
|
161
|
-
throw new Error('Point is not on curve');
|
|
162
|
-
const y2 = secp256k1.utils._weierstrassEquation(x); // y² = x³ + ax + b
|
|
163
|
-
let y = sqrtMod(y2); // y = y² ^ (p+1)/4
|
|
164
|
-
const isYOdd = (y & _1n) === _1n;
|
|
165
|
-
// Schnorr
|
|
166
|
-
if (isYOdd)
|
|
167
|
-
y = secp256k1.CURVE.Fp.negate(y);
|
|
168
|
-
const point = new secp256k1.Point(x, y);
|
|
169
|
-
point.assertValidity();
|
|
170
|
-
return point;
|
|
171
|
-
}
|
|
172
|
-
// Do we need that in schnorr at all?
|
|
173
|
-
return secp256k1.Point.fromHex(publicKey);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
const isWithinCurveOrder = secp256k1.utils._isWithinCurveOrder;
|
|
177
|
-
const isValidFieldElement = secp256k1.utils._isValidFieldElement;
|
|
97
|
+
const fe = (x) => typeof x === 'bigint' && _0n < x && x < secp256k1P;
|
|
98
|
+
const ge = (x) => typeof x === 'bigint' && _0n < x && x < secp256k1N;
|
|
178
99
|
const TAGS = {
|
|
179
100
|
challenge: 'BIP0340/challenge',
|
|
180
101
|
aux: 'BIP0340/aux',
|
|
@@ -182,7 +103,7 @@ const TAGS = {
|
|
|
182
103
|
};
|
|
183
104
|
/** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */
|
|
184
105
|
const TAGGED_HASH_PREFIXES = {};
|
|
185
|
-
|
|
106
|
+
function taggedHash(tag, ...messages) {
|
|
186
107
|
let tagP = TAGGED_HASH_PREFIXES[tag];
|
|
187
108
|
if (tagP === undefined) {
|
|
188
109
|
const tagH = sha256(Uint8Array.from(tag, (c) => c.charCodeAt(0)));
|
|
@@ -192,43 +113,37 @@ export function taggedHash(tag, ...messages) {
|
|
|
192
113
|
return sha256(concatBytes(tagP, ...messages));
|
|
193
114
|
}
|
|
194
115
|
const toRawX = (point) => point.toRawBytes(true).slice(1);
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
// Do we need this at all for Schnorr?
|
|
201
|
-
class SchnorrSignature {
|
|
202
|
-
constructor(r, s) {
|
|
203
|
-
this.r = r;
|
|
204
|
-
this.s = s;
|
|
205
|
-
this.assertValidity();
|
|
206
|
-
}
|
|
207
|
-
static fromHex(hex) {
|
|
208
|
-
const bytes = ensureBytes(hex);
|
|
209
|
-
if (bytes.length !== 64)
|
|
210
|
-
throw new TypeError(`SchnorrSignature.fromHex: expected 64 bytes, not ${bytes.length}`);
|
|
211
|
-
const r = bytesToNumberBE(bytes.subarray(0, 32));
|
|
212
|
-
const s = bytesToNumberBE(bytes.subarray(32, 64));
|
|
213
|
-
return new SchnorrSignature(r, s);
|
|
214
|
-
}
|
|
215
|
-
assertValidity() {
|
|
216
|
-
const { r, s } = this;
|
|
217
|
-
if (!isValidFieldElement(r) || !isWithinCurveOrder(s))
|
|
218
|
-
throw new Error('Invalid signature');
|
|
219
|
-
}
|
|
220
|
-
toHex() {
|
|
221
|
-
return numTo32bStr(this.r) + numTo32bStr(this.s);
|
|
222
|
-
}
|
|
223
|
-
toRawBytes() {
|
|
224
|
-
return hexToBytes(this.toHex());
|
|
225
|
-
}
|
|
226
|
-
}
|
|
116
|
+
const numTo32b = (n) => numberToBytesBE(n, 32);
|
|
117
|
+
const modN = (x) => mod(x, secp256k1N);
|
|
118
|
+
const _Point = secp256k1.ProjectivePoint;
|
|
119
|
+
const Gmul = (priv) => _Point.fromPrivateKey(priv);
|
|
120
|
+
const GmulAdd = (Q, a, b) => _Point.BASE.multiplyAndAddUnsafe(Q, a, b);
|
|
227
121
|
function schnorrGetScalar(priv) {
|
|
228
|
-
|
|
229
|
-
|
|
122
|
+
// Let d' = int(sk)
|
|
123
|
+
// Fail if d' = 0 or d' ≥ n
|
|
124
|
+
// Let P = d'⋅G
|
|
125
|
+
// Let d = d' if has_even_y(P), otherwise let d = n - d' .
|
|
126
|
+
const point = Gmul(priv);
|
|
127
|
+
const scalar = point.hasEvenY() ? priv : modN(-priv);
|
|
230
128
|
return { point, scalar, x: toRawX(point) };
|
|
231
129
|
}
|
|
130
|
+
function lift_x(x) {
|
|
131
|
+
if (!fe(x))
|
|
132
|
+
throw new Error('bad x: need 0 < x < p'); // Fail if x ≥ p.
|
|
133
|
+
const c = mod(x * x * x + BigInt(7), secp256k1P); // Let c = x³ + 7 mod p.
|
|
134
|
+
let y = sqrtMod(c); // Let y = c^(p+1)/4 mod p.
|
|
135
|
+
if (y % 2n !== 0n)
|
|
136
|
+
y = mod(-y, secp256k1P); // Return the unique point P such that x(P) = x and
|
|
137
|
+
const p = new _Point(x, y, _1n); // y(P) = y if y mod 2 = 0 or y(P) = p-y otherwise.
|
|
138
|
+
p.assertValidity();
|
|
139
|
+
return p;
|
|
140
|
+
}
|
|
141
|
+
function challenge(...args) {
|
|
142
|
+
return modN(bytesToNum(taggedHash(TAGS.challenge, ...args)));
|
|
143
|
+
}
|
|
144
|
+
function schnorrGetPublicKey(privateKey) {
|
|
145
|
+
return toRawX(Gmul(privateKey)); // Let d' = int(sk). Fail if d' = 0 or d' ≥ n. Return bytes(d'⋅G)
|
|
146
|
+
}
|
|
232
147
|
/**
|
|
233
148
|
* Synchronously creates Schnorr signature. Improved security: verifies itself before
|
|
234
149
|
* producing an output.
|
|
@@ -238,23 +153,23 @@ function schnorrGetScalar(priv) {
|
|
|
238
153
|
*/
|
|
239
154
|
function schnorrSign(message, privateKey, auxRand = randomBytes(32)) {
|
|
240
155
|
if (message == null)
|
|
241
|
-
throw new
|
|
156
|
+
throw new Error(`sign: Expected valid message, not "${message}"`);
|
|
242
157
|
const m = ensureBytes(message);
|
|
243
158
|
// checks for isWithinCurveOrder
|
|
244
|
-
const { x: px, scalar: d } = schnorrGetScalar(
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
const
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
159
|
+
const { x: px, scalar: d } = schnorrGetScalar(bytesToNum(ensureBytes(privateKey, 32)));
|
|
160
|
+
const a = ensureBytes(auxRand, 32); // Auxiliary random data a: a 32-byte array
|
|
161
|
+
// TODO: replace with proper xor?
|
|
162
|
+
const t = numTo32b(d ^ bytesToNum(taggedHash(TAGS.aux, a))); // Let t be the byte-wise xor of bytes(d) and hash/aux(a)
|
|
163
|
+
const rand = taggedHash(TAGS.nonce, t, px, m); // Let rand = hash/nonce(t || bytes(P) || m)
|
|
164
|
+
const k_ = modN(bytesToNum(rand)); // Let k' = int(rand) mod n
|
|
165
|
+
if (k_ === _0n)
|
|
166
|
+
throw new Error('sign failed: k is zero'); // Fail if k' = 0.
|
|
167
|
+
const { point: R, x: rx, scalar: k } = schnorrGetScalar(k_); // Let R = k'⋅G.
|
|
168
|
+
const e = challenge(rx, px, m); // Let e = int(hash/challenge(bytes(R) || bytes(P) || m)) mod n.
|
|
169
|
+
const sig = new Uint8Array(64); // Let sig = bytes(R) || bytes((k + ed) mod n).
|
|
170
|
+
sig.set(numTo32b(R.px), 0);
|
|
171
|
+
sig.set(numTo32b(modN(k + e * d)), 32);
|
|
172
|
+
// If Verify(bytes(P), m, sig) (see below) returns failure, abort
|
|
258
173
|
if (!schnorrVerify(sig, m, px))
|
|
259
174
|
throw new Error('sign: Invalid signature produced');
|
|
260
175
|
return sig;
|
|
@@ -264,30 +179,76 @@ function schnorrSign(message, privateKey, auxRand = randomBytes(32)) {
|
|
|
264
179
|
*/
|
|
265
180
|
function schnorrVerify(signature, message, publicKey) {
|
|
266
181
|
try {
|
|
267
|
-
const
|
|
268
|
-
const sig =
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
const e = schnorrChallengeFinalize(taggedHash(TAGS.challenge, numTo32b(r), toRawX(P), m));
|
|
275
|
-
// Finalize
|
|
276
|
-
// R = s⋅G - e⋅P
|
|
277
|
-
// -eP == (n-e)P
|
|
278
|
-
const R = secp256k1.Point.BASE.multiplyAndAddUnsafe(P, normalizePrivateKey(s), mod(-e, secp256k1.CURVE.n));
|
|
279
|
-
if (!R || !R.hasEvenY() || R.x !== r)
|
|
182
|
+
const P = lift_x(bytesToNum(ensureBytes(publicKey, 32))); // P = lift_x(int(pk)); fail if that fails
|
|
183
|
+
const sig = ensureBytes(signature, 64);
|
|
184
|
+
const r = bytesToNum(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p.
|
|
185
|
+
if (!fe(r))
|
|
186
|
+
return false;
|
|
187
|
+
const s = bytesToNum(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n.
|
|
188
|
+
if (!ge(s))
|
|
280
189
|
return false;
|
|
281
|
-
|
|
190
|
+
const m = ensureBytes(message);
|
|
191
|
+
const e = challenge(numTo32b(r), toRawX(P), m); // int(challenge(bytes(r)||bytes(P)||m)) mod n
|
|
192
|
+
const R = GmulAdd(P, s, modN(-e)); // R = s⋅G - e⋅P
|
|
193
|
+
if (!R || !R.hasEvenY() || R.toAffine().x !== r)
|
|
194
|
+
return false; // -eP == (n-e)P
|
|
195
|
+
return true; // Fail if is_infinite(R) / not has_even_y(R) / x(R) ≠ r.
|
|
282
196
|
}
|
|
283
197
|
catch (error) {
|
|
284
198
|
return false;
|
|
285
199
|
}
|
|
286
200
|
}
|
|
287
201
|
export const schnorr = {
|
|
288
|
-
Signature: SchnorrSignature,
|
|
289
202
|
// Schnorr's pubkey is just `x` of Point (BIP340)
|
|
290
|
-
getPublicKey:
|
|
203
|
+
getPublicKey: schnorrGetPublicKey,
|
|
291
204
|
sign: schnorrSign,
|
|
292
205
|
verify: schnorrVerify,
|
|
206
|
+
utils: { lift_x, int: bytesToNum, taggedHash },
|
|
293
207
|
};
|
|
208
|
+
const isoMap = htf.isogenyMap(Fp, [
|
|
209
|
+
// xNum
|
|
210
|
+
[
|
|
211
|
+
'0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c7',
|
|
212
|
+
'0x7d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c6581',
|
|
213
|
+
'0x534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262',
|
|
214
|
+
'0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c',
|
|
215
|
+
],
|
|
216
|
+
// xDen
|
|
217
|
+
[
|
|
218
|
+
'0xd35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b',
|
|
219
|
+
'0xedadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d14',
|
|
220
|
+
'0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
|
|
221
|
+
],
|
|
222
|
+
// yNum
|
|
223
|
+
[
|
|
224
|
+
'0x4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c',
|
|
225
|
+
'0xc75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a3',
|
|
226
|
+
'0x29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931',
|
|
227
|
+
'0x2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d84',
|
|
228
|
+
],
|
|
229
|
+
// yDen
|
|
230
|
+
[
|
|
231
|
+
'0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffff93b',
|
|
232
|
+
'0x7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c2573',
|
|
233
|
+
'0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f',
|
|
234
|
+
'0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
|
|
235
|
+
],
|
|
236
|
+
].map((i) => i.map((j) => BigInt(j))));
|
|
237
|
+
const mapSWU = mapToCurveSimpleSWU(Fp, {
|
|
238
|
+
A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'),
|
|
239
|
+
B: BigInt('1771'),
|
|
240
|
+
Z: Fp.create(BigInt('-11')),
|
|
241
|
+
});
|
|
242
|
+
const { hashToCurve, encodeToCurve } = htf.hashToCurve(secp256k1.ProjectivePoint, (scalars) => {
|
|
243
|
+
const { x, y } = mapSWU(Fp.create(scalars[0]));
|
|
244
|
+
return isoMap(x, y);
|
|
245
|
+
}, {
|
|
246
|
+
DST: 'secp256k1_XMD:SHA-256_SSWU_RO_',
|
|
247
|
+
encodeDST: 'secp256k1_XMD:SHA-256_SSWU_NU_',
|
|
248
|
+
p: Fp.ORDER,
|
|
249
|
+
m: 1,
|
|
250
|
+
k: 128,
|
|
251
|
+
expand: 'xmd',
|
|
252
|
+
hash: sha256,
|
|
253
|
+
});
|
|
254
|
+
export { hashToCurve, encodeToCurve };
|
package/lib/esm/stark.js
CHANGED
|
@@ -3,12 +3,23 @@ import { keccak_256 } from '@noble/hashes/sha3';
|
|
|
3
3
|
import { sha256 } from '@noble/hashes/sha256';
|
|
4
4
|
import { weierstrass } from './abstract/weierstrass.js';
|
|
5
5
|
import * as cutils from './abstract/utils.js';
|
|
6
|
-
import { Fp } from './abstract/modular.js';
|
|
6
|
+
import { Fp, mod, validateField } from './abstract/modular.js';
|
|
7
7
|
import { getHash } from './_shortw_utils.js';
|
|
8
|
+
import * as poseidon from './abstract/poseidon.js';
|
|
9
|
+
import { utf8ToBytes } from '@noble/hashes/utils';
|
|
8
10
|
// Stark-friendly elliptic curve
|
|
9
11
|
// https://docs.starkware.co/starkex/stark-curve.html
|
|
10
12
|
const CURVE_N = BigInt('3618502788666131213697322783095070105526743751716087489154079457884512865583');
|
|
11
13
|
const nBitLength = 252;
|
|
14
|
+
// Copy-pasted from weierstrass.ts
|
|
15
|
+
function bits2int(bytes) {
|
|
16
|
+
const delta = bytes.length * 8 - nBitLength;
|
|
17
|
+
const num = cutils.bytesToNumberBE(bytes);
|
|
18
|
+
return delta > 0 ? num >> BigInt(delta) : num;
|
|
19
|
+
}
|
|
20
|
+
function bits2int_modN(bytes) {
|
|
21
|
+
return mod(bits2int(bytes), CURVE_N);
|
|
22
|
+
}
|
|
12
23
|
export const starkCurve = weierstrass({
|
|
13
24
|
// Params: a, b
|
|
14
25
|
a: BigInt(1),
|
|
@@ -26,33 +37,28 @@ export const starkCurve = weierstrass({
|
|
|
26
37
|
// Default options
|
|
27
38
|
lowS: false,
|
|
28
39
|
...getHash(sha256),
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
// Custom truncation routines for stark curve
|
|
41
|
+
bits2int: (bytes) => {
|
|
42
|
+
while (bytes[0] === 0)
|
|
43
|
+
bytes = bytes.subarray(1);
|
|
44
|
+
return bits2int(bytes);
|
|
45
|
+
},
|
|
46
|
+
bits2int_modN: (bytes) => {
|
|
47
|
+
let hashS = cutils.bytesToNumberBE(bytes).toString(16);
|
|
48
|
+
if (hashS.length === 63) {
|
|
49
|
+
hashS += '0';
|
|
50
|
+
bytes = hexToBytes0x(hashS);
|
|
38
51
|
}
|
|
39
52
|
// Truncate zero bytes on left (compat with elliptic)
|
|
40
|
-
while (
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const delta = byteLength * 8 - nBitLength; // size of curve.n (252 bits)
|
|
44
|
-
let h = hash.length ? bytesToNumber0x(hash) : 0n;
|
|
45
|
-
if (delta > 0)
|
|
46
|
-
h = h >> BigInt(delta);
|
|
47
|
-
if (!truncateOnly && h >= CURVE_N)
|
|
48
|
-
h -= CURVE_N;
|
|
49
|
-
return h;
|
|
53
|
+
while (bytes[0] === 0)
|
|
54
|
+
bytes = bytes.subarray(1);
|
|
55
|
+
return bits2int_modN(bytes);
|
|
50
56
|
},
|
|
51
57
|
});
|
|
52
58
|
// Custom Starknet type conversion functions that can handle 0x and unpadded hex
|
|
53
59
|
function hexToBytes0x(hex) {
|
|
54
60
|
if (typeof hex !== 'string') {
|
|
55
|
-
throw new
|
|
61
|
+
throw new Error('hexToBytes: expected string, got ' + typeof hex);
|
|
56
62
|
}
|
|
57
63
|
hex = strip0x(hex);
|
|
58
64
|
if (hex.length & 1)
|
|
@@ -72,7 +78,7 @@ function hexToBytes0x(hex) {
|
|
|
72
78
|
}
|
|
73
79
|
function hexToNumber0x(hex) {
|
|
74
80
|
if (typeof hex !== 'string') {
|
|
75
|
-
throw new
|
|
81
|
+
throw new Error('hexToNumber: expected string, got ' + typeof hex);
|
|
76
82
|
}
|
|
77
83
|
// Big Endian
|
|
78
84
|
// TODO: strip vs no strip?
|
|
@@ -87,7 +93,7 @@ function ensureBytes0x(hex) {
|
|
|
87
93
|
return hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes0x(hex);
|
|
88
94
|
}
|
|
89
95
|
function normalizePrivateKey(privKey) {
|
|
90
|
-
return cutils.bytesToHex(ensureBytes0x(privKey)).padStart(
|
|
96
|
+
return cutils.bytesToHex(ensureBytes0x(privKey)).padStart(64, '0');
|
|
91
97
|
}
|
|
92
98
|
function getPublicKey0x(privKey, isCompressed) {
|
|
93
99
|
return starkCurve.getPublicKey(normalizePrivateKey(privKey), isCompressed);
|
|
@@ -104,9 +110,9 @@ function verify0x(signature, msgHash, pubKey) {
|
|
|
104
110
|
const sig = signature instanceof Signature ? signature : ensureBytes0x(signature);
|
|
105
111
|
return starkCurve.verify(sig, ensureBytes0x(msgHash), ensureBytes0x(pubKey));
|
|
106
112
|
}
|
|
107
|
-
const { CURVE,
|
|
113
|
+
const { CURVE, ProjectivePoint, Signature } = starkCurve;
|
|
108
114
|
export const utils = starkCurve.utils;
|
|
109
|
-
export { CURVE,
|
|
115
|
+
export { CURVE, Signature, ProjectivePoint, getPublicKey0x as getPublicKey, getSharedSecret0x as getSharedSecret, sign0x as sign, verify0x as verify, };
|
|
110
116
|
const stripLeadingZeros = (s) => s.replace(/^0+/gm, '');
|
|
111
117
|
export const bytesToHexEth = (uint8a) => `0x${stripLeadingZeros(cutils.bytesToHex(uint8a))}`;
|
|
112
118
|
export const strip0x = (hex) => hex.replace(/^0x/i, '');
|
|
@@ -116,17 +122,17 @@ function hashKeyWithIndex(key, index) {
|
|
|
116
122
|
let indexHex = cutils.numberToHexUnpadded(index);
|
|
117
123
|
if (indexHex.length & 1)
|
|
118
124
|
indexHex = '0' + indexHex;
|
|
119
|
-
return
|
|
125
|
+
return sha256Num(cutils.concatBytes(key, hexToBytes0x(indexHex)));
|
|
120
126
|
}
|
|
121
127
|
export function grindKey(seed) {
|
|
122
128
|
const _seed = ensureBytes0x(seed);
|
|
123
129
|
const sha256mask = 2n ** 256n;
|
|
124
|
-
const limit = sha256mask -
|
|
130
|
+
const limit = sha256mask - mod(sha256mask, CURVE_N);
|
|
125
131
|
for (let i = 0;; i++) {
|
|
126
132
|
const key = hashKeyWithIndex(_seed, i);
|
|
127
133
|
// key should be in [0, limit)
|
|
128
134
|
if (key < limit)
|
|
129
|
-
return
|
|
135
|
+
return mod(key, CURVE_N).toString(16);
|
|
130
136
|
}
|
|
131
137
|
}
|
|
132
138
|
export function getStarkKey(privateKey) {
|
|
@@ -141,21 +147,21 @@ export function ethSigToPrivate(signature) {
|
|
|
141
147
|
const MASK_31 = 2n ** 31n - 1n;
|
|
142
148
|
const int31 = (n) => Number(n & MASK_31);
|
|
143
149
|
export function getAccountPath(layer, application, ethereumAddress, index) {
|
|
144
|
-
const layerNum = int31(
|
|
145
|
-
const applicationNum = int31(
|
|
150
|
+
const layerNum = int31(sha256Num(layer));
|
|
151
|
+
const applicationNum = int31(sha256Num(application));
|
|
146
152
|
const eth = hexToNumber0x(ethereumAddress);
|
|
147
153
|
return `m/2645'/${layerNum}'/${applicationNum}'/${int31(eth)}'/${int31(eth >> 31n)}'/${index}`;
|
|
148
154
|
}
|
|
149
155
|
// https://docs.starkware.co/starkex/pedersen-hash-function.html
|
|
150
156
|
const PEDERSEN_POINTS_AFFINE = [
|
|
151
|
-
new
|
|
152
|
-
new
|
|
153
|
-
new
|
|
154
|
-
new
|
|
155
|
-
new
|
|
157
|
+
new ProjectivePoint(2089986280348253421170679821480865132823066470938446095505822317253594081284n, 1713931329540660377023406109199410414810705867260802078187082345529207694986n, 1n),
|
|
158
|
+
new ProjectivePoint(996781205833008774514500082376783249102396023663454813447423147977397232763n, 1668503676786377725805489344771023921079126552019160156920634619255970485781n, 1n),
|
|
159
|
+
new ProjectivePoint(2251563274489750535117886426533222435294046428347329203627021249169616184184n, 1798716007562728905295480679789526322175868328062420237419143593021674992973n, 1n),
|
|
160
|
+
new ProjectivePoint(2138414695194151160943305727036575959195309218611738193261179310511854807447n, 113410276730064486255102093846540133784865286929052426931474106396135072156n, 1n),
|
|
161
|
+
new ProjectivePoint(2379962749567351885752724891227938183011949129833673362440656643086021394946n, 776496453633298175483985398648758586525933812536653089401905292063708816422n, 1n),
|
|
156
162
|
];
|
|
157
163
|
// for (const p of PEDERSEN_POINTS) p._setWindowSize(8);
|
|
158
|
-
const PEDERSEN_POINTS = PEDERSEN_POINTS_AFFINE
|
|
164
|
+
const PEDERSEN_POINTS = PEDERSEN_POINTS_AFFINE;
|
|
159
165
|
function pedersenPrecompute(p1, p2) {
|
|
160
166
|
const out = [];
|
|
161
167
|
let p = p1;
|
|
@@ -194,7 +200,7 @@ function pedersenSingle(point, value, constants) {
|
|
|
194
200
|
let x = pedersenArg(value);
|
|
195
201
|
for (let j = 0; j < 252; j++) {
|
|
196
202
|
const pt = constants[j];
|
|
197
|
-
if (pt.
|
|
203
|
+
if (pt.px === point.px)
|
|
198
204
|
throw new Error('Same point');
|
|
199
205
|
if ((x & 1n) !== 0n)
|
|
200
206
|
point = point.add(pt);
|
|
@@ -207,7 +213,7 @@ export function pedersen(x, y) {
|
|
|
207
213
|
let point = PEDERSEN_POINTS[0];
|
|
208
214
|
point = pedersenSingle(point, x, PEDERSEN_POINTS1);
|
|
209
215
|
point = pedersenSingle(point, y, PEDERSEN_POINTS2);
|
|
210
|
-
return bytesToHexEth(point.
|
|
216
|
+
return bytesToHexEth(point.toRawBytes(true).slice(1));
|
|
211
217
|
}
|
|
212
218
|
export function hashChain(data, fn = pedersen) {
|
|
213
219
|
if (!Array.isArray(data) || data.length < 1)
|
|
@@ -220,5 +226,64 @@ export function hashChain(data, fn = pedersen) {
|
|
|
220
226
|
}
|
|
221
227
|
// Same as hashChain, but computes hash even for single element and order is not revesed
|
|
222
228
|
export const computeHashOnElements = (data, fn = pedersen) => [0, ...data, data.length].reduce((x, y) => fn(x, y));
|
|
223
|
-
const MASK_250 =
|
|
229
|
+
const MASK_250 = cutils.bitMask(250);
|
|
224
230
|
export const keccak = (data) => bytesToNumber0x(keccak_256(data)) & MASK_250;
|
|
231
|
+
const sha256Num = (data) => cutils.bytesToNumberBE(sha256(data));
|
|
232
|
+
// Poseidon hash
|
|
233
|
+
export const Fp253 = Fp(BigInt('14474011154664525231415395255581126252639794253786371766033694892385558855681')); // 2^253 + 2^199 + 1
|
|
234
|
+
export const Fp251 = Fp(BigInt('3618502788666131213697322783095070105623107215331596699973092056135872020481')); // 2^251 + 17 * 2^192 + 1
|
|
235
|
+
function poseidonRoundConstant(Fp, name, idx) {
|
|
236
|
+
const val = Fp.fromBytes(sha256(utf8ToBytes(`${name}${idx}`)));
|
|
237
|
+
return Fp.create(val);
|
|
238
|
+
}
|
|
239
|
+
// NOTE: doesn't check eiginvalues and possible can create unsafe matrix. But any filtration here will break compatibility with starknet
|
|
240
|
+
// Please use only if you really know what you doing.
|
|
241
|
+
// https://eprint.iacr.org/2019/458.pdf Section 2.3 (Avoiding Insecure Matrices)
|
|
242
|
+
export function _poseidonMDS(Fp, name, m, attempt = 0) {
|
|
243
|
+
const x_values = [];
|
|
244
|
+
const y_values = [];
|
|
245
|
+
for (let i = 0; i < m; i++) {
|
|
246
|
+
x_values.push(poseidonRoundConstant(Fp, `${name}x`, attempt * m + i));
|
|
247
|
+
y_values.push(poseidonRoundConstant(Fp, `${name}y`, attempt * m + i));
|
|
248
|
+
}
|
|
249
|
+
if (new Set([...x_values, ...y_values]).size !== 2 * m)
|
|
250
|
+
throw new Error('X and Y values are not distinct');
|
|
251
|
+
return x_values.map((x) => y_values.map((y) => Fp.inv(Fp.sub(x, y))));
|
|
252
|
+
}
|
|
253
|
+
const MDS_SMALL = [
|
|
254
|
+
[3, 1, 1],
|
|
255
|
+
[1, -1, 1],
|
|
256
|
+
[1, 1, -2],
|
|
257
|
+
].map((i) => i.map(BigInt));
|
|
258
|
+
export function poseidonBasic(opts, mds) {
|
|
259
|
+
validateField(opts.Fp);
|
|
260
|
+
if (!Number.isSafeInteger(opts.rate) || !Number.isSafeInteger(opts.capacity))
|
|
261
|
+
throw new Error(`Wrong poseidon opts: ${opts}`);
|
|
262
|
+
const m = opts.rate + opts.capacity;
|
|
263
|
+
const rounds = opts.roundsFull + opts.roundsPartial;
|
|
264
|
+
const roundConstants = [];
|
|
265
|
+
for (let i = 0; i < rounds; i++) {
|
|
266
|
+
const row = [];
|
|
267
|
+
for (let j = 0; j < m; j++)
|
|
268
|
+
row.push(poseidonRoundConstant(opts.Fp, 'Hades', m * i + j));
|
|
269
|
+
roundConstants.push(row);
|
|
270
|
+
}
|
|
271
|
+
return poseidon.poseidon({
|
|
272
|
+
...opts,
|
|
273
|
+
t: m,
|
|
274
|
+
sboxPower: 3,
|
|
275
|
+
reversePartialPowIdx: true,
|
|
276
|
+
mds,
|
|
277
|
+
roundConstants,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
export function poseidonCreate(opts, mdsAttempt = 0) {
|
|
281
|
+
const m = opts.rate + opts.capacity;
|
|
282
|
+
if (!Number.isSafeInteger(mdsAttempt))
|
|
283
|
+
throw new Error(`Wrong mdsAttempt=${mdsAttempt}`);
|
|
284
|
+
return poseidonBasic(opts, _poseidonMDS(opts.Fp, 'HadesMDS', m, mdsAttempt));
|
|
285
|
+
}
|
|
286
|
+
export const poseidonSmall = poseidonBasic({ Fp: Fp251, rate: 2, capacity: 1, roundsFull: 8, roundsPartial: 83 }, MDS_SMALL);
|
|
287
|
+
export function poseidonHash(x, y, fn = poseidonSmall) {
|
|
288
|
+
return fn([x, y, 2n])[0];
|
|
289
|
+
}
|
package/lib/jubjub.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* jubjub Twisted Edwards curve.
|
|
3
3
|
* https://neuromancer.sk/std/other/JubJub
|
|
4
|
+
* jubjub does not use EdDSA, so `hash`/sha512 params are passed because interface expects them.
|
|
4
5
|
*/
|
|
5
6
|
export declare const jubjub: import("./abstract/edwards.js").CurveFn;
|
|
6
|
-
export declare function groupHash(tag: Uint8Array, personalization: Uint8Array): import("./abstract/edwards.js").
|
|
7
|
-
export declare function findGroupHash(m: Uint8Array, personalization: Uint8Array): import("./abstract/edwards.js").
|
|
7
|
+
export declare function groupHash(tag: Uint8Array, personalization: Uint8Array): import("./abstract/edwards.js").ExtPointType;
|
|
8
|
+
export declare function findGroupHash(m: Uint8Array, personalization: Uint8Array): import("./abstract/edwards.js").ExtPointType;
|
package/lib/jubjub.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.findGroupHash = exports.groupHash = exports.jubjub = void 0;
|
|
4
4
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
5
|
-
const
|
|
5
|
+
const sha512_1 = require("@noble/hashes/sha512");
|
|
6
6
|
const utils_1 = require("@noble/hashes/utils");
|
|
7
7
|
const edwards_js_1 = require("./abstract/edwards.js");
|
|
8
8
|
const blake2s_1 = require("@noble/hashes/blake2s");
|
|
@@ -10,22 +10,23 @@ const modular_js_1 = require("./abstract/modular.js");
|
|
|
10
10
|
/**
|
|
11
11
|
* jubjub Twisted Edwards curve.
|
|
12
12
|
* https://neuromancer.sk/std/other/JubJub
|
|
13
|
+
* jubjub does not use EdDSA, so `hash`/sha512 params are passed because interface expects them.
|
|
13
14
|
*/
|
|
14
15
|
exports.jubjub = (0, edwards_js_1.twistedEdwards)({
|
|
15
16
|
// Params: a, d
|
|
16
17
|
a: BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000'),
|
|
17
18
|
d: BigInt('0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1'),
|
|
18
19
|
// Finite field 𝔽p over which we'll do calculations
|
|
20
|
+
// Same value as bls12-381 Fr (not Fp)
|
|
19
21
|
Fp: (0, modular_js_1.Fp)(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001')),
|
|
20
|
-
// Subgroup order: how many points
|
|
21
|
-
// 2n ** 252n + 27742317777372353535851937790883648493n;
|
|
22
|
+
// Subgroup order: how many points curve has
|
|
22
23
|
n: BigInt('0xe7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7'),
|
|
23
24
|
// Cofactor
|
|
24
25
|
h: BigInt(8),
|
|
25
26
|
// Base point (x, y) aka generator point
|
|
26
27
|
Gx: BigInt('0x11dafe5d23e1218086a365b99fbf3d3be72f6afd7d1f72623e6b071492d1122b'),
|
|
27
28
|
Gy: BigInt('0x1d523cf1ddab1a1793132e78c866c0c33e26ba5cc220fed7cc3f870e59d292aa'),
|
|
28
|
-
hash:
|
|
29
|
+
hash: sha512_1.sha512,
|
|
29
30
|
randomBytes: utils_1.randomBytes,
|
|
30
31
|
});
|
|
31
32
|
const GH_FIRST_BLOCK = (0, utils_1.utf8ToBytes)('096b36a5804bfacef1691e173c366a47ff5ba84a44f26ddd7e8d9f79d5b42df0');
|
|
@@ -35,7 +36,7 @@ function groupHash(tag, personalization) {
|
|
|
35
36
|
h.update(GH_FIRST_BLOCK);
|
|
36
37
|
h.update(tag);
|
|
37
38
|
// NOTE: returns ExtendedPoint, in case it will be multiplied later
|
|
38
|
-
let p = exports.jubjub.ExtendedPoint.
|
|
39
|
+
let p = exports.jubjub.ExtendedPoint.fromHex(h.digest());
|
|
39
40
|
// NOTE: cannot replace with isSmallOrder, returns Point*8
|
|
40
41
|
p = p.multiply(exports.jubjub.CURVE.h);
|
|
41
42
|
if (p.equals(exports.jubjub.ExtendedPoint.ZERO))
|