Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Greybeard
momiji-node-mainnet
Commits
671a85fd
Commit
671a85fd
authored
11 months ago
by
John Doe
Browse files
Options
Download
Email Patches
Plain Diff
🟣
⛵
🏴
☠
️
parent
aff1f6e0
Pipeline
#49
failed with stages
in 0 seconds
Changes
335
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
3900 additions
and
0 deletions
+3900
-0
momiji-helpers/circuits/helpers/codegen/rollup_transaction.ts
...ji-helpers/circuits/helpers/codegen/rollup_transaction.ts
+51
-0
momiji-helpers/circuits/helpers/codegen/tx_as_hash.ts
momiji-helpers/circuits/helpers/codegen/tx_as_hash.ts
+30
-0
momiji-helpers/circuits/helpers/codegen/utxo_to_commitment.ts
...ji-helpers/circuits/helpers/codegen/utxo_to_commitment.ts
+25
-0
momiji-helpers/circuits/helpers/src/hash.nr
momiji-helpers/circuits/helpers/src/hash.nr
+267
-0
momiji-helpers/circuits/helpers/src/lib.nr
momiji-helpers/circuits/helpers/src/lib.nr
+363
-0
momiji-helpers/circuits/helpers/src/structs.nr
momiji-helpers/circuits/helpers/src/structs.nr
+253
-0
momiji-helpers/circuits/publish/Nargo.toml
momiji-helpers/circuits/publish/Nargo.toml
+8
-0
momiji-helpers/circuits/publish/Prover.toml
momiji-helpers/circuits/publish/Prover.toml
+0
-0
momiji-helpers/circuits/publish/Verifier.toml
momiji-helpers/circuits/publish/Verifier.toml
+0
-0
momiji-helpers/circuits/publish/contract/publish/plonk_vk.sol
...ji-helpers/circuits/publish/contract/publish/plonk_vk.sol
+2777
-0
momiji-helpers/circuits/publish/proofs/publish.proof
momiji-helpers/circuits/publish/proofs/publish.proof
+1
-0
momiji-helpers/circuits/publish/src/main.nr
momiji-helpers/circuits/publish/src/main.nr
+67
-0
momiji-helpers/circuits/recursion/Nargo.toml
momiji-helpers/circuits/recursion/Nargo.toml
+8
-0
momiji-helpers/circuits/recursion/Prover.toml
momiji-helpers/circuits/recursion/Prover.toml
+0
-0
momiji-helpers/circuits/recursion/Verifier.toml
momiji-helpers/circuits/recursion/Verifier.toml
+0
-0
momiji-helpers/circuits/recursion/proofs/proof
momiji-helpers/circuits/recursion/proofs/proof
+0
-0
momiji-helpers/circuits/recursion/src/main.nr
momiji-helpers/circuits/recursion/src/main.nr
+49
-0
momiji-helpers/circuits/recursion/target/acir.gz
momiji-helpers/circuits/recursion/target/acir.gz
+0
-0
momiji-helpers/circuits/recursion/target/vk
momiji-helpers/circuits/recursion/target/vk
+0
-0
momiji-helpers/circuits/recursion/target/vk_fields.json
momiji-helpers/circuits/recursion/target/vk_fields.json
+1
-0
No files found.
Too many changes to show.
To preserve performance only
335 of 335+
files are displayed.
Plain diff
Email patch
momiji-helpers/circuits/helpers/codegen/rollup_transaction.ts
0 → 100644
View file @
671a85fd
/* Autogenerated file, do not edit! */
/* eslint-disable */
import
{
Noir
,
InputMap
,
CompiledCircuit
,
ForeignCallHandler
}
from
"
@noir-lang/noir_js
"
export
type
{
ForeignCallHandler
}
from
"
@noir-lang/noir_js
"
export
type
Field
=
string
;
export
type
VerifierTx
=
{
key_hash
:
Field
;
verification_key
:
Field
[];
proof
:
Field
[];
};
export
type
Verifier
=
{
key_hash
:
Field
;
verification_key
:
Field
[];
proof
:
Field
[];
aggregation_object
:
Field
[];
};
export
type
PublicInputs
=
{
current_root
:
Field
;
utxo_root
:
Field
;
deposit_amount
:
Field
;
withdrawals
:
Field
;
commitment_in
:
Field
[];
commitment_out
:
Field
[];
nullifier_hashes
:
Field
[];
contract_only_inputs
:
Field
;
};
export
type
RecursionInputs
=
{
accumulator
:
Field
;
tx_verifier
:
VerifierTx
;
recursion_verifier
:
Verifier
;
previous_accumulator
:
Field
;
tx
:
PublicInputs
;
};
export
const
rollup_transaction_circuit
:
CompiledCircuit
=
{
"
abi
"
:{
"
parameters
"
:[{
"
name
"
:
"
tx_verifier
"
,
"
type
"
:{
"
kind
"
:
"
struct
"
,
"
path
"
:
"
structs::VerifierTx
"
,
"
fields
"
:[{
"
name
"
:
"
key_hash
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}},{
"
name
"
:
"
verification_key
"
,
"
type
"
:{
"
kind
"
:
"
array
"
,
"
length
"
:
114
,
"
type
"
:{
"
kind
"
:
"
field
"
}}},{
"
name
"
:
"
proof
"
,
"
type
"
:{
"
kind
"
:
"
array
"
,
"
length
"
:
93
,
"
type
"
:{
"
kind
"
:
"
field
"
}}}]},
"
visibility
"
:
"
private
"
},{
"
name
"
:
"
recursion_verifier
"
,
"
type
"
:{
"
kind
"
:
"
struct
"
,
"
path
"
:
"
structs::Verifier
"
,
"
fields
"
:[{
"
name
"
:
"
key_hash
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}},{
"
name
"
:
"
verification_key
"
,
"
type
"
:{
"
kind
"
:
"
array
"
,
"
length
"
:
114
,
"
type
"
:{
"
kind
"
:
"
field
"
}}},{
"
name
"
:
"
proof
"
,
"
type
"
:{
"
kind
"
:
"
array
"
,
"
length
"
:
109
,
"
type
"
:{
"
kind
"
:
"
field
"
}}},{
"
name
"
:
"
aggregation_object
"
,
"
type
"
:{
"
kind
"
:
"
array
"
,
"
length
"
:
16
,
"
type
"
:{
"
kind
"
:
"
field
"
}}}]},
"
visibility
"
:
"
private
"
},{
"
name
"
:
"
previous_accumulator
"
,
"
type
"
:{
"
kind
"
:
"
field
"
},
"
visibility
"
:
"
private
"
},{
"
name
"
:
"
tx
"
,
"
type
"
:{
"
kind
"
:
"
struct
"
,
"
path
"
:
"
structs::PublicInputs
"
,
"
fields
"
:[{
"
name
"
:
"
current_root
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}},{
"
name
"
:
"
utxo_root
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}},{
"
name
"
:
"
deposit_amount
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}},{
"
name
"
:
"
withdrawals
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}},{
"
name
"
:
"
commitment_in
"
,
"
type
"
:{
"
kind
"
:
"
array
"
,
"
length
"
:
16
,
"
type
"
:{
"
kind
"
:
"
field
"
}}},{
"
name
"
:
"
commitment_out
"
,
"
type
"
:{
"
kind
"
:
"
array
"
,
"
length
"
:
16
,
"
type
"
:{
"
kind
"
:
"
field
"
}}},{
"
name
"
:
"
nullifier_hashes
"
,
"
type
"
:{
"
kind
"
:
"
array
"
,
"
length
"
:
16
,
"
type
"
:{
"
kind
"
:
"
field
"
}}},{
"
name
"
:
"
contract_only_inputs
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}}]},
"
visibility
"
:
"
private
"
}],
"
param_witnesses
"
:{
"
previous_accumulator
"
:[{
"
start
"
:
448
,
"
end
"
:
449
}],
"
recursion_verifier
"
:[{
"
start
"
:
208
,
"
end
"
:
448
}],
"
tx
"
:[{
"
start
"
:
449
,
"
end
"
:
502
}],
"
tx_verifier
"
:[{
"
start
"
:
0
,
"
end
"
:
208
}]},
"
return_type
"
:{
"
abi_type
"
:{
"
kind
"
:
"
struct
"
,
"
path
"
:
"
structs::RecursionInputs
"
,
"
fields
"
:[{
"
name
"
:
"
accumulator
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}},{
"
name
"
:
"
tx_verifier
"
,
"
type
"
:{
"
kind
"
:
"
struct
"
,
"
path
"
:
"
structs::VerifierTx
"
,
"
fields
"
:[{
"
name
"
:
"
key_hash
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}},{
"
name
"
:
"
verification_key
"
,
"
type
"
:{
"
kind
"
:
"
array
"
,
"
length
"
:
114
,
"
type
"
:{
"
kind
"
:
"
field
"
}}},{
"
name
"
:
"
proof
"
,
"
type
"
:{
"
kind
"
:
"
array
"
,
"
length
"
:
93
,
"
type
"
:{
"
kind
"
:
"
field
"
}}}]}},{
"
name
"
:
"
recursion_verifier
"
,
"
type
"
:{
"
kind
"
:
"
struct
"
,
"
path
"
:
"
structs::Verifier
"
,
"
fields
"
:[{
"
name
"
:
"
key_hash
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}},{
"
name
"
:
"
verification_key
"
,
"
type
"
:{
"
kind
"
:
"
array
"
,
"
length
"
:
114
,
"
type
"
:{
"
kind
"
:
"
field
"
}}},{
"
name
"
:
"
proof
"
,
"
type
"
:{
"
kind
"
:
"
array
"
,
"
length
"
:
109
,
"
type
"
:{
"
kind
"
:
"
field
"
}}},{
"
name
"
:
"
aggregation_object
"
,
"
type
"
:{
"
kind
"
:
"
array
"
,
"
length
"
:
16
,
"
type
"
:{
"
kind
"
:
"
field
"
}}}]}},{
"
name
"
:
"
previous_accumulator
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}},{
"
name
"
:
"
tx
"
,
"
type
"
:{
"
kind
"
:
"
struct
"
,
"
path
"
:
"
structs::PublicInputs
"
,
"
fields
"
:[{
"
name
"
:
"
current_root
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}},{
"
name
"
:
"
utxo_root
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}},{
"
name
"
:
"
deposit_amount
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}},{
"
name
"
:
"
withdrawals
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}},{
"
name
"
:
"
commitment_in
"
,
"
type
"
:{
"
kind
"
:
"
array
"
,
"
length
"
:
16
,
"
type
"
:{
"
kind
"
:
"
field
"
}}},{
"
name
"
:
"
commitment_out
"
,
"
type
"
:{
"
kind
"
:
"
array
"
,
"
length
"
:
16
,
"
type
"
:{
"
kind
"
:
"
field
"
}}},{
"
name
"
:
"
nullifier_hashes
"
,
"
type
"
:{
"
kind
"
:
"
array
"
,
"
length
"
:
16
,
"
type
"
:{
"
kind
"
:
"
field
"
}}},{
"
name
"
:
"
contract_only_inputs
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}}]}}]},
"
visibility
"
:
"
public
"
},
"
return_witnesses
"
:[
6360
,
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
10
,
11
,
12
,
13
,
14
,
15
,
16
,
17
,
18
,
19
,
20
,
21
,
22
,
23
,
24
,
25
,
26
,
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
,
53
,
54
,
55
,
56
,
57
,
58
,
59
,
60
,
61
,
62
,
63
,
64
,
65
,
66
,
67
,
68
,
69
,
70
,
71
,
72
,
73
,
74
,
75
,
76
,
77
,
78
,
79
,
80
,
81
,
82
,
83
,
84
,
85
,
86
,
87
,
88
,
89
,
90
,
91
,
92
,
93
,
94
,
95
,
96
,
97
,
98
,
99
,
100
,
101
,
102
,
103
,
104
,
105
,
106
,
107
,
108
,
109
,
110
,
111
,
112
,
113
,
114
,
115
,
116
,
117
,
118
,
119
,
120
,
121
,
122
,
123
,
124
,
125
,
126
,
127
,
128
,
129
,
130
,
131
,
132
,
133
,
134
,
135
,
136
,
137
,
138
,
139
,
140
,
141
,
142
,
143
,
144
,
145
,
146
,
147
,
148
,
149
,
150
,
151
,
152
,
153
,
154
,
155
,
156
,
157
,
158
,
159
,
160
,
161
,
162
,
163
,
164
,
165
,
166
,
167
,
168
,
169
,
170
,
171
,
172
,
173
,
174
,
175
,
176
,
177
,
178
,
179
,
180
,
181
,
182
,
183
,
184
,
185
,
186
,
187
,
188
,
189
,
190
,
191
,
192
,
193
,
194
,
195
,
196
,
197
,
198
,
199
,
200
,
201
,
202
,
203
,
204
,
205
,
206
,
207
,
208
,
209
,
210
,
211
,
212
,
213
,
214
,
215
,
216
,
217
,
218
,
219
,
220
,
221
,
222
,
223
,
224
,
225
,
226
,
227
,
228
,
229
,
230
,
231
,
232
,
233
,
234
,
235
,
236
,
237
,
238
,
239
,
240
,
241
,
242
,
243
,
244
,
245
,
246
,
247
,
248
,
249
,
250
,
251
,
252
,
253
,
254
,
255
,
256
,
257
,
258
,
259
,
260
,
261
,
262
,
263
,
264
,
265
,
266
,
267
,
268
,
269
,
270
,
271
,
272
,
273
,
274
,
275
,
276
,
277
,
278
,
279
,
280
,
281
,
282
,
283
,
284
,
285
,
286
,
287
,
288
,
289
,
290
,
291
,
292
,
293
,
294
,
295
,
296
,
297
,
298
,
299
,
300
,
301
,
302
,
303
,
304
,
305
,
306
,
307
,
308
,
309
,
310
,
311
,
312
,
313
,
314
,
315
,
316
,
317
,
318
,
319
,
320
,
321
,
322
,
323
,
324
,
325
,
326
,
327
,
328
,
329
,
330
,
331
,
332
,
333
,
334
,
335
,
336
,
337
,
338
,
339
,
340
,
341
,
342
,
343
,
344
,
345
,
346
,
347
,
348
,
349
,
350
,
351
,
352
,
353
,
354
,
355
,
356
,
357
,
358
,
359
,
360
,
361
,
362
,
363
,
364
,
365
,
366
,
367
,
368
,
369
,
370
,
371
,
372
,
373
,
374
,
375
,
376
,
377
,
378
,
379
,
380
,
381
,
382
,
383
,
384
,
385
,
386
,
387
,
388
,
389
,
390
,
391
,
392
,
393
,
394
,
395
,
396
,
397
,
398
,
399
,
400
,
401
,
402
,
403
,
404
,
405
,
406
,
407
,
408
,
409
,
410
,
411
,
412
,
413
,
414
,
415
,
416
,
417
,
418
,
419
,
420
,
421
,
422
,
423
,
424
,
425
,
426
,
427
,
428
,
429
,
430
,
431
,
432
,
433
,
434
,
435
,
436
,
437
,
438
,
439
,
440
,
441
,
442
,
443
,
444
,
445
,
446
,
447
,
448
,
449
,
450
,
451
,
452
,
453
,
454
,
455
,
456
,
457
,
458
,
459
,
460
,
461
,
462
,
463
,
464
,
465
,
466
,
467
,
468
,
469
,
470
,
471
,
472
,
473
,
474
,
475
,
476
,
477
,
478
,
479
,
480
,
481
,
482
,
483
,
484
,
485
,
486
,
487
,
488
,
489
,
490
,
491
,
492
,
493
,
494
,
495
,
496
,
497
,
498
,
499
,
500
,
501
]},
"
bytecode
"
:
"
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
"
};
export
async
function
rollup_transaction
(
tx_verifier
:
VerifierTx
,
recursion_verifier
:
Verifier
,
previous_accumulator
:
Field
,
tx
:
PublicInputs
,
foreignCallHandler
?:
ForeignCallHandler
):
Promise
<
RecursionInputs
>
{
const
program
=
new
Noir
(
rollup_transaction_circuit
);
const
args
:
InputMap
=
{
tx_verifier
,
recursion_verifier
,
previous_accumulator
,
tx
};
const
{
returnValue
}
=
await
program
.
execute
(
args
,
foreignCallHandler
);
return
returnValue
as
RecursionInputs
;
}
This diff is collapsed.
Click to expand it.
momiji-helpers/circuits/helpers/codegen/tx_as_hash.ts
0 → 100644
View file @
671a85fd
/* Autogenerated file, do not edit! */
/* eslint-disable */
import
{
Noir
,
InputMap
,
CompiledCircuit
,
ForeignCallHandler
}
from
"
@noir-lang/noir_js
"
export
type
{
ForeignCallHandler
}
from
"
@noir-lang/noir_js
"
export
type
Field
=
string
;
export
type
PublicInputs
=
{
current_root
:
Field
;
utxo_root
:
Field
;
deposit_amount
:
Field
;
withdrawals
:
Field
;
commitment_in
:
Field
[];
commitment_out
:
Field
[];
nullifier_hashes
:
Field
[];
contract_only_inputs
:
Field
;
};
export
const
tx_as_hash_circuit
:
CompiledCircuit
=
{
"
abi
"
:{
"
parameters
"
:[{
"
name
"
:
"
tx
"
,
"
type
"
:{
"
kind
"
:
"
struct
"
,
"
path
"
:
"
structs::PublicInputs
"
,
"
fields
"
:[{
"
name
"
:
"
current_root
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}},{
"
name
"
:
"
utxo_root
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}},{
"
name
"
:
"
deposit_amount
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}},{
"
name
"
:
"
withdrawals
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}},{
"
name
"
:
"
commitment_in
"
,
"
type
"
:{
"
kind
"
:
"
array
"
,
"
length
"
:
16
,
"
type
"
:{
"
kind
"
:
"
field
"
}}},{
"
name
"
:
"
commitment_out
"
,
"
type
"
:{
"
kind
"
:
"
array
"
,
"
length
"
:
16
,
"
type
"
:{
"
kind
"
:
"
field
"
}}},{
"
name
"
:
"
nullifier_hashes
"
,
"
type
"
:{
"
kind
"
:
"
array
"
,
"
length
"
:
16
,
"
type
"
:{
"
kind
"
:
"
field
"
}}},{
"
name
"
:
"
contract_only_inputs
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}}]},
"
visibility
"
:
"
private
"
}],
"
param_witnesses
"
:{
"
tx
"
:[{
"
start
"
:
0
,
"
end
"
:
53
}]},
"
return_type
"
:{
"
abi_type
"
:{
"
kind
"
:
"
field
"
},
"
visibility
"
:
"
private
"
},
"
return_witnesses
"
:[
53
]},
"
bytecode
"
:
"
H4sIAAAAAAAA/13R1QpCURRFUdtrd3d36///mag4FPGADLlve65w6P1uz1/Y/5eBb5939/1lhFHGGGeCSQZMMc0Ms8wxzwKLLLHMCqussc4Gm2yxzQ677LHPAYccccwJp5xxzgWXXHHNDbfccc8DjzzxzAuv/Oz4a8huEXvF7JSwT2CXtD2ydsjrX9S9rHdV57q+TV3benZ17Os31G2s11SnuT5LXdZ6bHXYu//o7rN7r393/d/6fQ/p5LXj2QIAAA==
"
};
export
async
function
tx_as_hash
(
tx
:
PublicInputs
,
foreignCallHandler
?:
ForeignCallHandler
):
Promise
<
Field
>
{
const
program
=
new
Noir
(
tx_as_hash_circuit
);
const
args
:
InputMap
=
{
tx
};
const
{
returnValue
}
=
await
program
.
execute
(
args
,
foreignCallHandler
);
return
returnValue
as
Field
;
}
This diff is collapsed.
Click to expand it.
momiji-helpers/circuits/helpers/codegen/utxo_to_commitment.ts
0 → 100644
View file @
671a85fd
/* Autogenerated file, do not edit! */
/* eslint-disable */
import
{
Noir
,
InputMap
,
CompiledCircuit
,
ForeignCallHandler
}
from
"
@noir-lang/noir_js
"
export
type
{
ForeignCallHandler
}
from
"
@noir-lang/noir_js
"
export
type
Field
=
string
;
export
type
UTXO_New
=
{
secret
:
Field
;
amount
:
Field
;
asset_type
:
Field
;
};
export
const
utxo_to_commitment_circuit
:
CompiledCircuit
=
{
"
abi
"
:{
"
parameters
"
:[{
"
name
"
:
"
utxo
"
,
"
type
"
:{
"
kind
"
:
"
struct
"
,
"
path
"
:
"
structs::UTXO_New
"
,
"
fields
"
:[{
"
name
"
:
"
secret
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}},{
"
name
"
:
"
amount
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}},{
"
name
"
:
"
asset_type
"
,
"
type
"
:{
"
kind
"
:
"
field
"
}}]},
"
visibility
"
:
"
private
"
}],
"
param_witnesses
"
:{
"
utxo
"
:[{
"
start
"
:
0
,
"
end
"
:
3
}]},
"
return_type
"
:{
"
abi_type
"
:{
"
kind
"
:
"
field
"
},
"
visibility
"
:
"
private
"
},
"
return_witnesses
"
:[
4
]},
"
bytecode
"
:
"
H4sIAAAAAAAA/11MWwoAMAjqsY/d/8CyYAaWIJaKbh+nGLy9eKkNUFPyFA/0wR3Irkm392P92h14YkM2CqEAAAA=
"
};
export
async
function
utxo_to_commitment
(
utxo
:
UTXO_New
,
foreignCallHandler
?:
ForeignCallHandler
):
Promise
<
Field
>
{
const
program
=
new
Noir
(
utxo_to_commitment_circuit
);
const
args
:
InputMap
=
{
utxo
};
const
{
returnValue
}
=
await
program
.
execute
(
args
,
foreignCallHandler
);
return
returnValue
as
Field
;
}
This diff is collapsed.
Click to expand it.
momiji-helpers/circuits/helpers/src/hash.nr
0 → 100644
View file @
671a85fd
use dep::std;
use crate::structs;
fn field_to_u8(_value: Field) -> [u8; 32] {
let _array = _value.to_be_bytes(32);
let mut array: [u8; 32] = [0; 32];
for i in 0 .. 32 {
array[i] = _array[i];
}
array
}
fn hash<N>(data: [Field; N]) -> Field {
std::hash::pedersen_hash(data)
}
#[export]
fn utxo_to_commitment(utxo: structs::UTXO_New) -> Field {
utxo.to_commitment()
}
#[export]
fn pedersen_left_right(left: Field, right: Field) -> Field {
std::hash::pedersen_hash([left, right])
}
#[export]
fn keccak_tx(tx: structs::PublicInputs) -> Field {
let mut hash_array: [Field; 53] = [0; 53];
hash_array[0] = tx.current_root;
hash_array[1] = tx.utxo_root;
hash_array[2] = tx.deposit_amount;
hash_array[3] = tx.contract_only_inputs;
hash_array[4] = tx.withdrawals;
for i in 0..16 {
hash_array[5 + i] = tx.commitment_in[i];
hash_array[21 + i] = tx.commitment_out[i];
hash_array[37 + i] = tx.nullifier_hashes[i];
}
let u8_array = tx_to_u8(hash_array);
hash_to_field(std::hash::keccak256(u8_array, u8_array.len() as u32))
}
fn bytes_tx_without_deposit(tx: structs::PublicInputs) -> [u8; 1696] {
let mut hash_array: [Field; 53] = [0; 53];
hash_array[0] = tx.current_root;
hash_array[1] = tx.utxo_root;
hash_array[2] = tx.deposit_amount;
hash_array[3] = tx.contract_only_inputs;
hash_array[4] = tx.withdrawals;
for i in 0..16 {
hash_array[5 + i] = tx.commitment_in[i];
hash_array[21 + i] = tx.commitment_out[i];
hash_array[37 + i] = tx.nullifier_hashes[i];
}
tx_to_u8(hash_array)
}
#[export]
fn keccak_contract_only_inputs(contract_only_inputs: structs::ContractOnlyInputs) -> Field {
let mut hash_array: [Field; 100] = [0; 100];
hash_array[0] = contract_only_inputs.timestamp;
hash_array[1] = contract_only_inputs.deadline;
hash_array[2] = contract_only_inputs.signature_hash;
hash_array[3] = contract_only_inputs.price_limit;
for i in 0..16 {
hash_array[4 + i] = contract_only_inputs.recipients[i];
hash_array[20 + i] = contract_only_inputs.swap_amounts[i];
hash_array[36 + i] = contract_only_inputs.uids[i];
hash_array[52 + (i * 3)] = contract_only_inputs.encrypted_utxo[i].secret;
hash_array[53 + (i * 3)] = contract_only_inputs.encrypted_utxo[i].amount;
hash_array[54 + (i * 3)] = contract_only_inputs.encrypted_utxo[i].data;
}
let u8_array = contract_only_to_u8(hash_array);
hash_to_field(std::hash::keccak256(u8_array, u8_array.len() as u32))
}
#[export]
fn keccak_contract_only_inputs_without_deposit(contract_only_inputs: structs::ContractOnlyInputs) -> Field {
let mut hash_array: [Field; 99] = [0; 99];
hash_array[0] = contract_only_inputs.timestamp;
hash_array[1] = contract_only_inputs.deadline;
hash_array[2] = contract_only_inputs.price_limit;
for i in 0..16 {
hash_array[3 + i] = contract_only_inputs.recipients[i];
hash_array[19 + i] = contract_only_inputs.swap_amounts[i];
hash_array[35 + i] = contract_only_inputs.uids[i];
hash_array[51 + (i * 3)] = contract_only_inputs.encrypted_utxo[i].secret;
hash_array[52 + (i * 3)] = contract_only_inputs.encrypted_utxo[i].amount;
hash_array[53 + (i * 3)] = contract_only_inputs.encrypted_utxo[i].data;
}
let u8_array = contract_only_without_deposit_to_u8(hash_array);
hash_to_field(std::hash::keccak256(u8_array, u8_array.len() as u32))
}
fn bytes_contract_only_inputs_without_deposit(contract_only_inputs: structs::ContractOnlyInputs) -> [u8; 3168] {
let mut hash_array: [Field; 99] = [0; 99];
hash_array[0] = contract_only_inputs.timestamp;
hash_array[1] = contract_only_inputs.deadline;
hash_array[2] = contract_only_inputs.price_limit;
for i in 0..16 {
hash_array[3 + i] = contract_only_inputs.recipients[i];
hash_array[19 + i] = contract_only_inputs.swap_amounts[i];
hash_array[35 + i] = contract_only_inputs.uids[i];
hash_array[51 + (i * 3)] = contract_only_inputs.encrypted_utxo[i].secret;
hash_array[52 + (i * 3)] = contract_only_inputs.encrypted_utxo[i].amount;
hash_array[53 + (i * 3)] = contract_only_inputs.encrypted_utxo[i].data;
}
contract_only_without_deposit_to_u8(hash_array)
}
#[export]
fn contract_only_inputs_with_signature_hash(contract_only_inputs: structs::ContractOnlyInputs) -> structs::ContractOnlyInputs {
let mut hash_array: [Field; 99] = [0; 99];
hash_array[0] = contract_only_inputs.timestamp;
hash_array[1] = contract_only_inputs.deadline;
hash_array[2] = contract_only_inputs.price_limit;
for i in 0..16 {
hash_array[3 + i] = contract_only_inputs.recipients[i];
hash_array[19 + i] = contract_only_inputs.swap_amounts[i];
hash_array[35 + i] = contract_only_inputs.uids[i];
hash_array[51 + (i * 3)] = contract_only_inputs.encrypted_utxo[i].secret;
hash_array[52 + (i * 3)] = contract_only_inputs.encrypted_utxo[i].amount;
hash_array[53 + (i * 3)] = contract_only_inputs.encrypted_utxo[i].data;
}
let u8_array = contract_only_without_deposit_to_u8(hash_array);
let contract_only_inputs_with_hash = structs::ContractOnlyInputs {
timestamp: contract_only_inputs.timestamp,
deadline: contract_only_inputs.deadline,
signature_hash: hash_to_field(std::hash::keccak256(u8_array, u8_array.len() as u32)),
price_limit: contract_only_inputs.price_limit,
recipients: contract_only_inputs.recipients,
swap_amounts: contract_only_inputs.swap_amounts,
uids: contract_only_inputs.uids,
encrypted_utxo: contract_only_inputs.encrypted_utxo
};
contract_only_inputs_with_hash
}
fn hash_tx(tx: structs::PublicInputs) -> Field {
let mut hash_array: [Field; 53] = [0; 53];
hash_array[0] = tx.current_root;
hash_array[1] = tx.utxo_root;
hash_array[2] = tx.deposit_amount;
hash_array[3] = tx.withdrawals;
for i in 0..16 {
hash_array[4 + i] = tx.commitment_in[i];
hash_array[20 + i] = tx.commitment_out[i];
hash_array[36 + i] = tx.nullifier_hashes[i];
}
hash_array[52] = tx.contract_only_inputs;
hash(hash_array)
}
fn hash_to_field(hash: [u8; 32]) -> Field {
let mut keccak_field: Field = 0;
for p in 0..32 {
let bytes_field: Field = hash[31 - p] as Field;
keccak_field += bytes_field * 256.pow_32(p as Field);
}
keccak_field
}
fn tx_to_u8(pi_fields: [Field; 53]) -> [u8; 1696] {
let mut keccak_array: [u8; 1696] = [0; 1696];
for i in 0..pi_fields.len() {
let mut byte_slice = pi_fields[i].to_be_bytes(32);
for j in 0..32 {
keccak_array[32*i + j] = byte_slice[j];
}
}
keccak_array
}
fn contract_only_to_u8(pi_fields: [Field; 100]) -> [u8; 3200] {
let mut keccak_array: [u8; 3200] = [0; 3200];
for i in 0..pi_fields.len() {
let mut byte_slice = pi_fields[i].to_be_bytes(32);
for j in 0..32 {
keccak_array[32*i + j] = byte_slice[j];
}
}
keccak_array
}
fn contract_only_without_deposit_to_u8(pi_fields: [Field; 99]) -> [u8; 3168] {
let mut keccak_array: [u8; 3168] = [0; 3168];
for i in 0..pi_fields.len() {
let mut byte_slice = pi_fields[i].to_be_bytes(32);
for j in 0..32 {
keccak_array[32*i + j] = byte_slice[j];
}
}
keccak_array
}
fn batch_to_u8(pi_fields: [Field; 19]) -> [u8; 608] {
let mut keccak_array: [u8; 608] = [0; 608];
for i in 0..pi_fields.len() {
let mut byte_slice = pi_fields[i].to_be_bytes(32);
for j in 0..32 {
keccak_array[32*i + j] = byte_slice[j];
}
}
keccak_array
}
fn hash_tree_four(leaves: [Field; 16]) -> Field {
let mut tx_tree: [Field; 16] = leaves;
for l in 0..8 { tx_tree[l] = hash([tx_tree[2*l], tx_tree[2*l + 1]]); }
for l in 0..4 { tx_tree[l] = hash([tx_tree[2*l], tx_tree[2*l + 1]]); }
for l in 0..2 { tx_tree[l] = hash([tx_tree[2*l], tx_tree[2*l + 1]]); }
hash([tx_tree[0], tx_tree[1]])
}
fn compute_merkle_root<N>(leaf: Field, index: Field, hash_path: [Field; N]) -> Field {
let n = hash_path.len();
let index_bits = index.to_le_bits(n as u32);
let mut current = leaf;
for i in 0..n {
let path_bit = index_bits[i] as bool;
let (hash_left, hash_right) = if path_bit {
(hash_path[i], current)
} else {
(current, hash_path[i])
};
current = hash([hash_left, hash_right]);
}
current
}
fn compute_sibling_path<N>(sibling_path: [Field; N], new_leaf: Field, insertion_index: Field) -> [Field; N] {
let index_bits: [u1] = insertion_index.to_le_bits(N as u32);
let mut new_sibling_path: [Field; N] = [0; N];
let mut current_hash: Field = new_leaf;
let mut zero_found: bool = false;
for i in 0..N {
let path_bit = index_bits[i] as bool;
if (!zero_found) {
if (!path_bit) {
zero_found = true;
new_sibling_path[i] = current_hash;
} else {
new_sibling_path[i] = structs::zero_hashes[i];
}
} else {
new_sibling_path[i] = sibling_path[i];
}
if (path_bit) {
current_hash = hash([sibling_path[i], current_hash]);
} else {
current_hash = hash([current_hash, sibling_path[i]]);
}
}
new_sibling_path
}
\ No newline at end of file
This diff is collapsed.
Click to expand it.
momiji-helpers/circuits/helpers/src/lib.nr
0 → 100644
View file @
671a85fd
use dep::std;
mod structs;
mod hash;
global utxo_depth = 4;
global batch_depth = 4;
global state_depth = 20;
global ZERO_VALUE = 0x016a430aa58685aba1311244a973a3bc358859da86784be51094368e8fb6f720;
// Levels of an empty pedersen Merkle tree with zero_leaf = sha256("Momiji") % Field Modulus
global zero_hashes: [Field; 20] = [
0x016a430aa58685aba1311244a973a3bc358859da86784be51094368e8fb6f720,
0x018d39625c19fa2cfbebdb940a66d7040bb0ef1b59ff6afd92a13a6b9b2d9865,
0x096c00ebc0c52478316b6b9fd16d0cd94c5f45bbe45bbfa8c606197c6119d41f,
0x2eaefd3bdd1bfbddd8fc5d972ded58617f752b3e88bd17b791e85e7b8eaacb47,
0x11d25ff6aa8a431fbce8e8d9a87a2d7986adf38e724fbe47f15752d0931f14d8,
0x01e8677aa02546bd7105a7a9fd31c4ef16b69d1bde90f36e97585d7cc31d50e4,
0x2520a755a532994c78f35b89938fbc864ec31ec4fc00363f83f9b12f04980c6a,
0x21a666842842d5323e51fdda10300c763d6b07e1b797ef3b0bd3690d667445bc,
0x1ce681d6f42b70668c369e7f786166e421dc840f0529bbd065d6b02d14ae0fe8,
0x1819b90a43ee28f652735708b2ef01428e21b84872bb3c7576de6e35d107c8ed,
0x063d7001431110a5949f5946a501fd28f64159f36ab4b80601ca305ac107b3db,
0x09aeef7a06f7daf368d797c2e686c7e9884f000de0bd9d8e73392378b0b1be38,
0x137121fd5d795eeecc61ec6a6de66abc589d241540139178cd5408b6ccb32a6e,
0x01a93f70a1b7482e0b32e76ce49a1e3c1fc2b5cd4789b6af749d78c42791c21a,
0x217bf2cc1f1b505a29b162a7889866af2728f5c6708560c9cc5e987b194c9c81,
0x1461dae57d7df7e580279637e5d94e0d734b908dc22aec5c52ed00187050a030,
0x295933dd65294cbf4b2c64d8a0daa6983d35d1f64d5adc2c44bd9d370086d7dc,
0x24650084f0db0fa4e8234fb251ad046b3ddcb7d6f59160b35cc4086e9e196b80,
0x15086d0394bd68847e3e36241cc7b3966f96efdd30a37e9e1e85027a6dacbed2,
0x1f87a17564f06581c1e1b2d716120990f898893ecc0e076363c56c5e3b56ef7b
];
global zero_root: Field = 0x06f93f503e77fcdcacfe622e66adc639b63e8c0083f5cab5d71d461aa4562c92;
#[export]
fn keccak_tx_with_deposit(
current_root: Field,
deposit_amount: Field,
withdrawals: [Field; 16],
utxo_spendable: [structs::UTXO_Spendable; 16],
utxo_new: [structs::UTXO_New; 16],
contract_only_inputs: structs::ContractOnlyInputs
) -> Field {
let withdrawal_amount: Field = withdrawals.reduce(|a,b| a + b);
let mut commitment_in: [Field; 16] = [ZERO_VALUE; 16];
let mut commitment_out: [Field; 16] = [ZERO_VALUE; 16];
let mut utxo_root: Field = 0;
let empty_utxo = structs::UTXO {owner: 0, amount: ZERO_VALUE, asset_type: 0};
let mut utxo_out: [structs::UTXO; 16] = [empty_utxo; 16];
let mut secrets: [Field; 16] = [0; 16];
let mut oracle: [Field; 16] = [ZERO_VALUE; 16];
let mut old_root_proof: [Field; 16] = [0; 16];
let mut utxo_in: [structs::UTXO; 16] = [empty_utxo; 16];
let mut nullifier_hashes: [Field; 16] = [ZERO_VALUE; 16];
let empty_merkle_proofs = structs::MerkleProof {
path_utxo: [0; utxo_depth],
path_tx: [0; batch_depth],
path_historic: [0; state_depth],
index_utxo: 0,
index_tx: 0,
index_historic: 0
};
let mut merkle_proofs: [structs::MerkleProof; 16] = [empty_merkle_proofs; 16];
for i in 0..16 {
if (utxo_spendable[i].amount != ZERO_VALUE) {
utxo_in[i] = structs::UTXO {owner: utxo_spendable[i].owner(), amount: utxo_spendable[i].amount, asset_type: utxo_spendable[i].asset_type};
secrets[i] = utxo_spendable[i].secret;
nullifier_hashes[i] = utxo_spendable[i].nullifier_hash();
commitment_in[i] = utxo_spendable[i].to_commitment();
if (utxo_spendable[i].spend_in_same_batch == false) {
commitment_in[i] = ZERO_VALUE;
old_root_proof[i] = utxo_spendable[i].old_root_proof;
merkle_proofs[i] = utxo_spendable[i].merkle_proof;
}
}
if (utxo_new[i].secret != 0) {
commitment_out[i] = utxo_new[i].to_commitment();
utxo_out[i] = structs::UTXO { owner: utxo_new[i].owner(), amount: utxo_new[i].amount, asset_type: utxo_new[i].asset_type };
}
}
utxo_root = hash::hash_tree_four(commitment_out);
let public_inputs = structs::PublicInputs {
current_root: current_root,
utxo_root: utxo_root,
deposit_amount: deposit_amount,
withdrawals: withdrawal_amount,
commitment_in: commitment_in,
commitment_out: commitment_out,
nullifier_hashes: nullifier_hashes,
contract_only_inputs: contract_only_inputs.as_keccak()
};
public_inputs.as_keccak()
}
#[export]
fn keccak_tx_no_deposit(
current_root: Field,
deposit_amount: Field,
withdrawals: [Field; 16],
utxo_spendable: [structs::UTXO_Spendable; 16],
utxo_new: [structs::UTXO_New; 16],
contract_only_inputs: structs::ContractOnlyInputs
) -> Field {
let withdrawal_amount: Field = withdrawals.reduce(|a,b| a + b);
let mut commitment_in: [Field; 16] = [ZERO_VALUE; 16];
let mut commitment_out: [Field; 16] = [ZERO_VALUE; 16];
let mut utxo_root: Field = 0;
let empty_utxo = structs::UTXO {owner: 0, amount: ZERO_VALUE, asset_type: 0};
let mut utxo_out: [structs::UTXO; 16] = [empty_utxo; 16];
let mut secrets: [Field; 16] = [0; 16];
let mut oracle: [Field; 16] = [ZERO_VALUE; 16];
let mut old_root_proof: [Field; 16] = [0; 16];
let mut utxo_in: [structs::UTXO; 16] = [empty_utxo; 16];
let mut nullifier_hashes: [Field; 16] = [ZERO_VALUE; 16];
let empty_merkle_proofs = structs::MerkleProof {
path_utxo: [0; utxo_depth],
path_tx: [0; batch_depth],
path_historic: [0; state_depth],
index_utxo: 0,
index_tx: 0,
index_historic: 0
};
let mut merkle_proofs: [structs::MerkleProof; 16] = [empty_merkle_proofs; 16];
for i in 0..16 {
if (utxo_spendable[i].amount != ZERO_VALUE) {
utxo_in[i] = structs::UTXO {owner: utxo_spendable[i].owner(), amount: utxo_spendable[i].amount, asset_type: utxo_spendable[i].asset_type};
secrets[i] = utxo_spendable[i].secret;
nullifier_hashes[i] = utxo_spendable[i].nullifier_hash();
commitment_in[i] = utxo_spendable[i].to_commitment();
if (utxo_spendable[i].spend_in_same_batch == false) {
commitment_in[i] = ZERO_VALUE;
old_root_proof[i] = utxo_spendable[i].old_root_proof;
merkle_proofs[i] = utxo_spendable[i].merkle_proof;
}
}
if (utxo_new[i].secret != 0) {
commitment_out[i] = utxo_new[i].to_commitment();
utxo_out[i] = structs::UTXO { owner: utxo_new[i].owner(), amount: utxo_new[i].amount, asset_type: utxo_new[i].asset_type };
}
}
utxo_root = hash::hash_tree_four(commitment_out);
let public_inputs = structs::PublicInputs {
current_root: current_root,
utxo_root: utxo_root,
deposit_amount: deposit_amount,
withdrawals: withdrawal_amount,
commitment_in: commitment_in,
commitment_out: commitment_out,
nullifier_hashes: nullifier_hashes,
contract_only_inputs: contract_only_inputs.as_keccak_without_deposit()
};
public_inputs.as_keccak()
}
#[export]
fn create_transaction(
current_root: Field,
deposit_amount: Field,
withdrawals: [Field; 16],
utxo_spendable: [structs::UTXO_Spendable; 16],
utxo_new: [structs::UTXO_New; 16],
contract_only_inputs: structs::ContractOnlyInputs
) -> pub structs::TransactionInputs {
let withdrawal_amount: Field = withdrawals.reduce(|a,b| a + b);
let mut commitment_in: [Field; 16] = [ZERO_VALUE; 16];
let mut commitment_out: [Field; 16] = [ZERO_VALUE; 16];
let mut utxo_root: Field = 0;
let empty_utxo = structs::UTXO {owner: 0, amount: ZERO_VALUE, asset_type: 0};
let mut utxo_out: [structs::UTXO; 16] = [empty_utxo; 16];
let mut secrets: [Field; 16] = [0; 16];
let mut oracle: [Field; 16] = [ZERO_VALUE; 16];
let mut old_root_proof: [Field; 16] = [0; 16];
let mut utxo_in: [structs::UTXO; 16] = [empty_utxo; 16];
let mut nullifier_hashes: [Field; 16] = [ZERO_VALUE; 16];
let empty_merkle_proofs = structs::MerkleProof {
path_utxo: [0; utxo_depth],
path_tx: [0; batch_depth],
path_historic: [0; state_depth],
index_utxo: 0,
index_tx: 0,
index_historic: 0
};
let mut merkle_proofs: [structs::MerkleProof; 16] = [empty_merkle_proofs; 16];
for i in 0..16 {
if (utxo_spendable[i].amount != ZERO_VALUE) {
utxo_in[i] = structs::UTXO {owner: utxo_spendable[i].owner(), amount: utxo_spendable[i].amount, asset_type: utxo_spendable[i].asset_type};
secrets[i] = utxo_spendable[i].secret;
nullifier_hashes[i] = utxo_spendable[i].nullifier_hash();
commitment_in[i] = utxo_spendable[i].to_commitment();
if (utxo_spendable[i].spend_in_same_batch == false) {
commitment_in[i] = ZERO_VALUE;
old_root_proof[i] = utxo_spendable[i].old_root_proof;
merkle_proofs[i] = utxo_spendable[i].merkle_proof;
}
}
if (utxo_new[i].secret != 0) {
commitment_out[i] = utxo_new[i].to_commitment();
utxo_out[i] = structs::UTXO { owner: utxo_new[i].owner(), amount: utxo_new[i].amount, asset_type: utxo_new[i].asset_type };
}
}
utxo_root = hash::hash_tree_four(commitment_out);
let public_inputs = structs::PublicInputs {
current_root: current_root,
utxo_root: utxo_root,
deposit_amount: deposit_amount,
withdrawals: withdrawal_amount,
commitment_in: commitment_in,
commitment_out: commitment_out,
nullifier_hashes: nullifier_hashes,
contract_only_inputs: contract_only_inputs.as_keccak()
};
let private_inputs = structs::PrivateInputs {
oracle: oracle,
old_root_proof: old_root_proof,
secrets: secrets,
utxo_in: utxo_in,
merkle_proofs: merkle_proofs,
utxo_out: utxo_out
};
let public_inputs_hash: Field = hash::hash_tx(public_inputs);
let transaction_inputs = structs::TransactionInputs {
public_inputs_hash: public_inputs_hash,
public_inputs: public_inputs,
private_inputs: private_inputs
};
transaction_inputs
}
#[export]
fn tx_as_hash(
tx: structs::PublicInputs
) -> Field {
tx.as_hash()
}
#[export]
fn rollup_transaction(
tx_verifier: structs::VerifierTx,
recursion_verifier: structs::Verifier,
previous_accumulator: Field,
tx: structs::PublicInputs
) -> pub structs::RecursionInputs {
let tx_as_keccak: Field = tx.as_keccak();
let mut accumulator_preimage: [u8; 128] = [0; 128];
for i in 0..32 {
accumulator_preimage[i] = hash::field_to_u8(previous_accumulator)[i];
accumulator_preimage[i + 32] = hash::field_to_u8(tx_as_keccak)[i];
accumulator_preimage[i + 64] = hash::field_to_u8(tx_verifier.key_hash)[i];
accumulator_preimage[i + 96] = hash::field_to_u8(recursion_verifier.key_hash)[i];
}
let accumulator: Field = hash::hash_to_field(std::hash::keccak256(accumulator_preimage, accumulator_preimage.len() as u32));
let recursion_inputs = structs::RecursionInputs {
accumulator: accumulator,
tx_verifier: tx_verifier,
recursion_verifier: recursion_verifier,
previous_accumulator: previous_accumulator,
tx: tx
};
recursion_inputs
}
#[export]
fn publish_batch(
accumulator: Field,
hist_tree_input: structs::HistoricTreeInput,
tx_verifier: structs::VerifierTx,
recursion_verifier: structs::Verifier
) -> pub structs::PublishInputs {
let old_state_root: Field = hist_tree_input.leaf;
let tx_root: Field = hash::hash_tree_four(hist_tree_input.utxo_roots);
let batch_oracle: Field = ZERO_VALUE;
let batch_root: Field = hash::hash([tx_root, batch_oracle]);
let new_state_root: Field = hash::hash([batch_root, old_state_root]);
let new_historic_path: [Field; state_depth] = hash::compute_sibling_path(
hist_tree_input.path,
hist_tree_input.leaf,
hist_tree_input.index
);
let new_historic_root: Field = hash::compute_merkle_root(
new_state_root,
hist_tree_input.index + 1,
new_historic_path
);
let hist_tree = structs::HistoricTree {
root: hist_tree_input.root,
new_root: new_historic_root,
leaf: hist_tree_input.leaf,
index: hist_tree_input.index,
old_path: hist_tree_input.path,
new_path: new_historic_path
};
let batch = structs::Batch {
hist_tree: hist_tree,
old_state_root: old_state_root,
new_state_root: new_state_root,
batch_oracle: batch_oracle,
utxo_roots: hist_tree_input.utxo_roots
};
let mut hash_validation: [u8; 832] = [0; 832];
for i in 0..32 {
hash_validation[i] = hash::field_to_u8(batch.new_state_root)[i];
hash_validation[i + 32] = hash::field_to_u8(batch.hist_tree.root)[i];
hash_validation[i + 64] = hash::field_to_u8(batch.hist_tree.new_root)[i];
hash_validation[i + 96] = hash::field_to_u8(accumulator)[i];
hash_validation[i + 128] = hash::field_to_u8(tx_verifier.key_hash)[i];
hash_validation[i + 160] = hash::field_to_u8(recursion_verifier.key_hash)[i];
for j in 0..20 {
hash_validation[i + 192 + (32 * j)] = hash::field_to_u8(batch.hist_tree.new_path[j])[i];
}
}
let pi_contract_hash: Field = hash::hash_to_field(std::hash::keccak256(hash_validation, hash_validation.len() as u32));
let publish_inputs = structs::PublishInputs {
pi_contract_hash: pi_contract_hash,
accumulator: accumulator,
batch: batch,
tx_verifier: tx_verifier,
recursion_verifier: recursion_verifier
};
publish_inputs
}
\ No newline at end of file
This diff is collapsed.
Click to expand it.
momiji-helpers/circuits/helpers/src/structs.nr
0 → 100644
View file @
671a85fd
use crate::hash;
global utxo_depth = 4;
global batch_depth = 4;
global state_depth = 20;
global ZERO_VALUE = 0x016a430aa58685aba1311244a973a3bc358859da86784be51094368e8fb6f720;
// Levels of an empty pedersen Merkle tree with zero_leaf = sha256("Momiji") % Field Modulus
global zero_hashes: [Field; 20] = [
0x016a430aa58685aba1311244a973a3bc358859da86784be51094368e8fb6f720,
0x018d39625c19fa2cfbebdb940a66d7040bb0ef1b59ff6afd92a13a6b9b2d9865,
0x096c00ebc0c52478316b6b9fd16d0cd94c5f45bbe45bbfa8c606197c6119d41f,
0x2eaefd3bdd1bfbddd8fc5d972ded58617f752b3e88bd17b791e85e7b8eaacb47,
0x11d25ff6aa8a431fbce8e8d9a87a2d7986adf38e724fbe47f15752d0931f14d8,
0x01e8677aa02546bd7105a7a9fd31c4ef16b69d1bde90f36e97585d7cc31d50e4,
0x2520a755a532994c78f35b89938fbc864ec31ec4fc00363f83f9b12f04980c6a,
0x21a666842842d5323e51fdda10300c763d6b07e1b797ef3b0bd3690d667445bc,
0x1ce681d6f42b70668c369e7f786166e421dc840f0529bbd065d6b02d14ae0fe8,
0x1819b90a43ee28f652735708b2ef01428e21b84872bb3c7576de6e35d107c8ed,
0x063d7001431110a5949f5946a501fd28f64159f36ab4b80601ca305ac107b3db,
0x09aeef7a06f7daf368d797c2e686c7e9884f000de0bd9d8e73392378b0b1be38,
0x137121fd5d795eeecc61ec6a6de66abc589d241540139178cd5408b6ccb32a6e,
0x01a93f70a1b7482e0b32e76ce49a1e3c1fc2b5cd4789b6af749d78c42791c21a,
0x217bf2cc1f1b505a29b162a7889866af2728f5c6708560c9cc5e987b194c9c81,
0x1461dae57d7df7e580279637e5d94e0d734b908dc22aec5c52ed00187050a030,
0x295933dd65294cbf4b2c64d8a0daa6983d35d1f64d5adc2c44bd9d370086d7dc,
0x24650084f0db0fa4e8234fb251ad046b3ddcb7d6f59160b35cc4086e9e196b80,
0x15086d0394bd68847e3e36241cc7b3966f96efdd30a37e9e1e85027a6dacbed2,
0x1f87a17564f06581c1e1b2d716120990f898893ecc0e076363c56c5e3b56ef7b
];
// sha256("Momiji") % Field Modulus
global zero_root: Field = 0x06f93f503e77fcdcacfe622e66adc639b63e8c0083f5cab5d71d461aa4562c92;
struct Verifier {
key_hash: Field,
verification_key: [Field; 114],
proof: [Field; 109],
aggregation_object: [Field; 16]
}
struct VerifierTx {
key_hash: Field,
verification_key: [Field; 114],
proof: [Field; 93]
}
impl Verifier {
fn as_fields(self) -> [Field; 208] {
let mut verifier_as_fields: [Field; 208] = [0; 208];
verifier_as_fields[0] = self.key_hash;
for i in 0..114 {
verifier_as_fields[i + 1] = self.verification_key[i];
}
for j in 0..93 {
verifier_as_fields[j + 115] = self.proof[j];
}
verifier_as_fields
}
fn concatenate(self) -> [Field; 109] {
let mut proof_with_agg: [Field; 109] = [0; 109];
for i in 0..16 {
proof_with_agg[i] = self.aggregation_object[i];
}
for j in 0..93 {
proof_with_agg[j + 16] = self.proof[j];
}
proof_with_agg
}
}
struct UTXO {
owner: Field,
amount: Field,
asset_type: Field
}
struct UTXO_New {
secret: Field,
amount: Field,
asset_type: Field
}
impl UTXO_New {
fn owner(self) -> Field {
hash::hash([self.secret])
}
fn nullifier_hash(self) -> Field {
hash::hash([self.secret, self.secret])
}
fn to_commitment(self) -> Field {
hash::hash([self.owner(), self.amount, self.asset_type])
}
}
struct UTXO_Spendable {
secret: Field,
amount: Field,
asset_type: Field,
oracle: Field,
old_root_proof: Field,
merkle_proof: MerkleProof,
spend_in_same_batch: bool
}
impl UTXO_Spendable {
fn owner(self) -> Field {
hash::hash([self.secret])
}
fn nullifier_hash(self) -> Field {
hash::hash([self.secret, self.secret])
}
fn to_commitment(self) -> Field {
hash::hash([self.owner(), self.amount, self.asset_type])
}
}
struct PublicInputs {
current_root: Field,
utxo_root: Field,
deposit_amount: Field,
withdrawals: Field,
commitment_in: [Field; 16],
commitment_out: [Field; 16],
nullifier_hashes: [Field; 16],
contract_only_inputs: Field
}
impl PublicInputs {
fn as_fields(self) -> [Field; 53] {
let mut public_fields: [Field; 53] = [0; 53];
public_fields[0] = self.current_root;
public_fields[1] = self.utxo_root;
public_fields[2] = self.deposit_amount;
public_fields[3] = self.withdrawals;
for i in 0..16 {
public_fields[4 + i] = self.commitment_in[i];
public_fields[20 + i] = self.commitment_out[i];
public_fields[36 + i] = self.nullifier_hashes[i];
}
public_fields[50] = self.contract_only_inputs;
public_fields
}
fn as_hash(self) -> Field {
hash::hash_tx(self)
}
fn as_keccak(self) -> Field {
hash::keccak_tx(self)
}
fn as_u8(self) -> [u8; 1696] {
hash::tx_to_u8(self.as_fields())
}
}
struct PrivateInputs {
oracle: [Field; 16],
old_root_proof: [Field; 16],
secrets: [Field; 16],
utxo_in: [UTXO; 16],
merkle_proofs: [MerkleProof; 16],
utxo_out: [UTXO; 16],
}
struct TransactionInputs {
public_inputs_hash: Field,
public_inputs: PublicInputs,
private_inputs: PrivateInputs
}
struct RecursionInputs {
accumulator: Field,
tx_verifier: VerifierTx,
recursion_verifier: Verifier,
previous_accumulator: Field,
tx: PublicInputs
}
struct PublishInputs {
pi_contract_hash: Field,
accumulator: Field,
batch: Batch,
tx_verifier: VerifierTx,
recursion_verifier: Verifier
}
struct Batch {
hist_tree: HistoricTree,
old_state_root: Field,
new_state_root: Field,
batch_oracle: Field,
utxo_roots: [Field; 16]
}
struct EncryptedUTXO {
secret: Field,
amount: Field,
data: Field
}
struct ContractOnlyInputs {
timestamp: Field,
deadline: Field,
signature_hash: Field,
price_limit: Field,
recipients: [Field; 16],
swap_amounts: [Field; 16],
uids: [Field; 16],
encrypted_utxo: [EncryptedUTXO; 16]
}
impl ContractOnlyInputs {
fn as_keccak_without_deposit(self) -> Field {
hash::keccak_contract_only_inputs_without_deposit(self)
}
fn as_keccak(self) -> Field {
hash::keccak_contract_only_inputs(self)
}
}
struct MerkleProof {
path_utxo: [Field; utxo_depth],
path_tx: [Field; batch_depth],
path_historic: [Field; state_depth],
index_utxo: Field,
index_tx: Field,
index_historic: Field
}
struct HistoricTree {
root: Field,
new_root: Field,
leaf: Field,
index: Field,
old_path: [Field; state_depth],
new_path: [Field; state_depth],
}
struct HistoricTreeInput {
root: Field,
leaf: Field,
index: Field,
path: [Field; state_depth],
utxo_roots: [Field; 16],
}
\ No newline at end of file
This diff is collapsed.
Click to expand it.
momiji-helpers/circuits/publish/Nargo.toml
0 → 100644
View file @
671a85fd
[package]
name
=
"publish"
type
=
"bin"
authors
=
[""]
compiler_version
=
">=0.19.2"
[dependencies]
helpers
=
{
path
=
"../helpers"
}
\ No newline at end of file
This diff is collapsed.
Click to expand it.
momiji-helpers/circuits/publish/Prover.toml
0 → 100644
View file @
671a85fd
This diff is collapsed.
Click to expand it.
momiji-helpers/circuits/publish/Verifier.toml
0 → 100644
View file @
671a85fd
This diff is collapsed.
Click to expand it.
momiji-helpers/circuits/publish/contract/publish/plonk_vk.sol
0 → 100644
View file @
671a85fd
// Verification Key Hash: 066e0b06b13ec4c45dbeb0b625dbfab874ecdc411cdb43bae4346e880b09e16c
// SPDX-License-Identifier: Apache-2.0
// Copyright 2022 Aztec
pragma
solidity
>=
0.8
.
4
;
library
UltraVerificationKey
{
function
verificationKeyHash
()
internal
pure
returns
(
bytes32
)
{
return
0x066e0b06b13ec4c45dbeb0b625dbfab874ecdc411cdb43bae4346e880b09e16c
;
}
function
loadVerificationKey
(
uint256
_vk
,
uint256
_omegaInverseLoc
)
internal
pure
{
assembly
{
mstore
(
add
(
_vk
,
0x00
),
0x0000000000000000000000000000000000000000000000000000000000080000
)
// vk.circuit_size
mstore
(
add
(
_vk
,
0x20
),
0x0000000000000000000000000000000000000000000000000000000000000011
)
// vk.num_inputs
mstore
(
add
(
_vk
,
0x40
),
0x2260e724844bca5251829353968e4915305258418357473a5c1d597f613f6cbd
)
// vk.work_root
mstore
(
add
(
_vk
,
0x60
),
0x3064486657634403844b0eac78ca882cfd284341fcb0615a15cfcd17b14d8201
)
// vk.domain_inverse
mstore
(
add
(
_vk
,
0x80
),
0x174c5c19a31444028bb6368ff05acd095502e7d022af18c746a364dab4c7346e
)
// vk.Q1.x
mstore
(
add
(
_vk
,
0xa0
),
0x2e6d6b74f09bdcf37e2be6f0a05552f34840f8693d1c73edce05639d0698ea36
)
// vk.Q1.y
mstore
(
add
(
_vk
,
0xc0
),
0x243678b1fa4062f90887453567ef78c256421a5907513427b5c52aa3cb1e6a17
)
// vk.Q2.x
mstore
(
add
(
_vk
,
0xe0
),
0x10425e273dc8dd43dbb983a295f7f990b9475fca570f56f1d7f972166e5bf0d8
)
// vk.Q2.y
mstore
(
add
(
_vk
,
0x100
),
0x23b6836088c547e90ddfdb8146a5f7b3608aad75f4d0d5a050f119e6eea92c22
)
// vk.Q3.x
mstore
(
add
(
_vk
,
0x120
),
0x0cafbde5fed53b2d75343797c5718363f54c3b391730f7b49a8ae09cd79b8b20
)
// vk.Q3.y
mstore
(
add
(
_vk
,
0x140
),
0x06acf52ef347e83eae253c24631c08d66d76669d97c0cce87b0320363e0188bd
)
// vk.Q4.x
mstore
(
add
(
_vk
,
0x160
),
0x1f8257e9bfac704d9b0171c74f4f35ec94c608ae5ff55962db5bc4fd9e1aba89
)
// vk.Q4.y
mstore
(
add
(
_vk
,
0x180
),
0x11931c54c38fba5131f7a317e782a12a8e3e3726dc2eaa490049ce2ca8ca1cbb
)
// vk.Q_M.x
mstore
(
add
(
_vk
,
0x1a0
),
0x0565f2552174d90b7b0e9645969affd0015d483dffec9d93ede1b63be6dc3c16
)
// vk.Q_M.y
mstore
(
add
(
_vk
,
0x1c0
),
0x0bca92d0bac2e3d19be0265337ec7f4574f8195e313af04d651f342bb3159fb8
)
// vk.Q_C.x
mstore
(
add
(
_vk
,
0x1e0
),
0x1ecb4bbed3bfe4b4e91afe37b7871694eda583ff1d1111be20aa9eb8edf50a77
)
// vk.Q_C.y
mstore
(
add
(
_vk
,
0x200
),
0x0c1be01afc0073d9a1f1e23184a1d005a89d087265ea3a85f523ecd062212260
)
// vk.Q_ARITHMETIC.x
mstore
(
add
(
_vk
,
0x220
),
0x25a9e732ab54e2ebe8d031c166997a26a5d5dc143dc246b4b046b170406da4f1
)
// vk.Q_ARITHMETIC.y
mstore
(
add
(
_vk
,
0x240
),
0x2498b2e14394f700cac156f42ef63ba194ed99764e03f2e433cb0be94d0fc89f
)
// vk.QSORT.x
mstore
(
add
(
_vk
,
0x260
),
0x206103c2ff58564c9a4746efd3fff7637a46052eb129af96844c2d850d11d8f3
)
// vk.QSORT.y
mstore
(
add
(
_vk
,
0x280
),
0x212dfe13df5b9cfb846570ecf8269ed3648c24827e7dceafdeca6235e1dd5e38
)
// vk.Q_ELLIPTIC.x
mstore
(
add
(
_vk
,
0x2a0
),
0x284d15304c372e9c33d76628c66bd6c48ef526d08146e9b401dc7d1e373ccd3a
)
// vk.Q_ELLIPTIC.y
mstore
(
add
(
_vk
,
0x2c0
),
0x126a3a8e4c678d476c3c864cde50f9776d880443976f62a438b9db897b433ffa
)
// vk.Q_AUX.x
mstore
(
add
(
_vk
,
0x2e0
),
0x2866186a79faf6dde910250342628d6a7a1c6d32f88b1991b276852874bd73dc
)
// vk.Q_AUX.y
mstore
(
add
(
_vk
,
0x300
),
0x2bd3d50520f90ab39921a0f2ce6c8f5d5cc8a989720e939dbb4a8951d878f579
)
// vk.SIGMA1.x
mstore
(
add
(
_vk
,
0x320
),
0x0b5652d8de0be47d4a6496260931f15984d6cc0eb21e61c15991554eee8bfe91
)
// vk.SIGMA1.y
mstore
(
add
(
_vk
,
0x340
),
0x135cdf64147af6820b82eff62e1e62efb848f20c81dffc2ed36cc4d3ef9d6ee3
)
// vk.SIGMA2.x
mstore
(
add
(
_vk
,
0x360
),
0x16ce92a14e25ca8f353781ad6bf4c105139a2029c682b0e62606b721e9d8c848
)
// vk.SIGMA2.y
mstore
(
add
(
_vk
,
0x380
),
0x0c9177240c1a64d39bbdc9113347b71c8ad5899a9a617b70450e43ca0f08c0b9
)
// vk.SIGMA3.x
mstore
(
add
(
_vk
,
0x3a0
),
0x26486bfd9b1989300948793829dda7ebd3c783b85ef797363b15bdf9932d624f
)
// vk.SIGMA3.y
mstore
(
add
(
_vk
,
0x3c0
),
0x0933b476f487753e6b6a6c07579f099b3bd03e1d4fad080cdf864233e378e6f7
)
// vk.SIGMA4.x
mstore
(
add
(
_vk
,
0x3e0
),
0x19fa964521f482600ad8f48f2d74204aa35665cefec49c73255bcd7bb9cf1d2e
)
// vk.SIGMA4.y
mstore
(
add
(
_vk
,
0x400
),
0x1cadeb85ec1d913dbbebd04a2e9c216fffb782a908252de688cb3f87a9960239
)
// vk.TABLE1.x
mstore
(
add
(
_vk
,
0x420
),
0x140ce35846522600428e89cedfc30ef4f906e759e4b262f982f41b11e232bef0
)
// vk.TABLE1.y
mstore
(
add
(
_vk
,
0x440
),
0x186c47255bd1cac8572abe17f302a4a778dc042d05b5ad10ec72ccf87db72cbd
)
// vk.TABLE2.x
mstore
(
add
(
_vk
,
0x460
),
0x2696b8ea9abe2e21817826ff8ffdd70455306100f722aee5047d7b61d23be470
)
// vk.TABLE2.y
mstore
(
add
(
_vk
,
0x480
),
0x203c78601f28e6739d2031447af9f5015f00e4a92298c8e0af4906929ce4aa23
)
// vk.TABLE3.x
mstore
(
add
(
_vk
,
0x4a0
),
0x0b82d8af3657b8dacf80c03e267415e27c1b22c5b110041433f5f7a0077d6163
)
// vk.TABLE3.y
mstore
(
add
(
_vk
,
0x4c0
),
0x1ca5d2fc76408b3d82cd070612b1f00b2910e92b606943f0a66d4064ac05d6f3
)
// vk.TABLE4.x
mstore
(
add
(
_vk
,
0x4e0
),
0x12889680e44151655a6fb88176f0eb0e09e60039ec5cbc978bcb081ca4d41c1e
)
// vk.TABLE4.y
mstore
(
add
(
_vk
,
0x500
),
0x269c3c1e6be15df4cd6e18678103fb6a22fc4e79890d0e7d18259353640bf580
)
// vk.TABLE_TYPE.x
mstore
(
add
(
_vk
,
0x520
),
0x073ef16b8836c3c34ac6e02937c25aaf47728e8aaa50eea7a2a7e2d87b2a051c
)
// vk.TABLE_TYPE.y
mstore
(
add
(
_vk
,
0x540
),
0x0642a0979a2c84f7f2c1ae9c5285e8b54bc1a57e51ec50588ec003e5ccaa1ef0
)
// vk.ID1.x
mstore
(
add
(
_vk
,
0x560
),
0x10759f71ebeb64070c9bde2e55126711646725544ef4273f9a391b9b9b0af13d
)
// vk.ID1.y
mstore
(
add
(
_vk
,
0x580
),
0x05107532383658086d131205993b32158da27ddcf4a41708cf50e4e6ca986205
)
// vk.ID2.x
mstore
(
add
(
_vk
,
0x5a0
),
0x1c67ba6b417eddf6928c56e1fece6ba9c73c4579c446ff0bd86e794121b4957c
)
// vk.ID2.y
mstore
(
add
(
_vk
,
0x5c0
),
0x030c58a082fe2a8a256a3c7596ca585a024396e2377db7e854bf7f14514759b8
)
// vk.ID3.x
mstore
(
add
(
_vk
,
0x5e0
),
0x1606bd0cae6fbb354de28025f6deb12a003c43b7dafa272c94b35c752ceaa696
)
// vk.ID3.y
mstore
(
add
(
_vk
,
0x600
),
0x1e1a0bd62a0746fb76e5d6727d5db536662c58d163cf29375fff76b669856e5f
)
// vk.ID4.x
mstore
(
add
(
_vk
,
0x620
),
0x0f786242d4fd388fd0e83bc4074cef1a36162d3403af146af654e72acb72e4c6
)
// vk.ID4.y
mstore
(
add
(
_vk
,
0x640
),
0x01
)
// vk.contains_recursive_proof
mstore
(
add
(
_vk
,
0x660
),
1
)
// vk.recursive_proof_public_input_indices
mstore
(
add
(
_vk
,
0x680
),
0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1
)
// vk.g2_x.X.c1
mstore
(
add
(
_vk
,
0x6a0
),
0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0
)
// vk.g2_x.X.c0
mstore
(
add
(
_vk
,
0x6c0
),
0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4
)
// vk.g2_x.Y.c1
mstore
(
add
(
_vk
,
0x6e0
),
0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55
)
// vk.g2_x.Y.c0
mstore
(
_omegaInverseLoc
,
0x06e402c0a314fb67a15cf806664ae1b722dbc0efe66e6c81d98f9924ca535321
)
// vk.work_root_inverse
}
}
}
/**
* @title Ultra Plonk proof verification contract
* @dev Top level Plonk proof verification contract, which allows Plonk proof to be verified
*/
abstract
contract
BaseUltraVerifier
{
// VERIFICATION KEY MEMORY LOCATIONS
uint256
internal
constant
N_LOC
=
0x380
;
uint256
internal
constant
NUM_INPUTS_LOC
=
0x3a0
;
uint256
internal
constant
OMEGA_LOC
=
0x3c0
;
uint256
internal
constant
DOMAIN_INVERSE_LOC
=
0x3e0
;
uint256
internal
constant
Q1_X_LOC
=
0x400
;
uint256
internal
constant
Q1_Y_LOC
=
0x420
;
uint256
internal
constant
Q2_X_LOC
=
0x440
;
uint256
internal
constant
Q2_Y_LOC
=
0x460
;
uint256
internal
constant
Q3_X_LOC
=
0x480
;
uint256
internal
constant
Q3_Y_LOC
=
0x4a0
;
uint256
internal
constant
Q4_X_LOC
=
0x4c0
;
uint256
internal
constant
Q4_Y_LOC
=
0x4e0
;
uint256
internal
constant
QM_X_LOC
=
0x500
;
uint256
internal
constant
QM_Y_LOC
=
0x520
;
uint256
internal
constant
QC_X_LOC
=
0x540
;
uint256
internal
constant
QC_Y_LOC
=
0x560
;
uint256
internal
constant
QARITH_X_LOC
=
0x580
;
uint256
internal
constant
QARITH_Y_LOC
=
0x5a0
;
uint256
internal
constant
QSORT_X_LOC
=
0x5c0
;
uint256
internal
constant
QSORT_Y_LOC
=
0x5e0
;
uint256
internal
constant
QELLIPTIC_X_LOC
=
0x600
;
uint256
internal
constant
QELLIPTIC_Y_LOC
=
0x620
;
uint256
internal
constant
QAUX_X_LOC
=
0x640
;
uint256
internal
constant
QAUX_Y_LOC
=
0x660
;
uint256
internal
constant
SIGMA1_X_LOC
=
0x680
;
uint256
internal
constant
SIGMA1_Y_LOC
=
0x6a0
;
uint256
internal
constant
SIGMA2_X_LOC
=
0x6c0
;
uint256
internal
constant
SIGMA2_Y_LOC
=
0x6e0
;
uint256
internal
constant
SIGMA3_X_LOC
=
0x700
;
uint256
internal
constant
SIGMA3_Y_LOC
=
0x720
;
uint256
internal
constant
SIGMA4_X_LOC
=
0x740
;
uint256
internal
constant
SIGMA4_Y_LOC
=
0x760
;
uint256
internal
constant
TABLE1_X_LOC
=
0x780
;
uint256
internal
constant
TABLE1_Y_LOC
=
0x7a0
;
uint256
internal
constant
TABLE2_X_LOC
=
0x7c0
;
uint256
internal
constant
TABLE2_Y_LOC
=
0x7e0
;
uint256
internal
constant
TABLE3_X_LOC
=
0x800
;
uint256
internal
constant
TABLE3_Y_LOC
=
0x820
;
uint256
internal
constant
TABLE4_X_LOC
=
0x840
;
uint256
internal
constant
TABLE4_Y_LOC
=
0x860
;
uint256
internal
constant
TABLE_TYPE_X_LOC
=
0x880
;
uint256
internal
constant
TABLE_TYPE_Y_LOC
=
0x8a0
;
uint256
internal
constant
ID1_X_LOC
=
0x8c0
;
uint256
internal
constant
ID1_Y_LOC
=
0x8e0
;
uint256
internal
constant
ID2_X_LOC
=
0x900
;
uint256
internal
constant
ID2_Y_LOC
=
0x920
;
uint256
internal
constant
ID3_X_LOC
=
0x940
;
uint256
internal
constant
ID3_Y_LOC
=
0x960
;
uint256
internal
constant
ID4_X_LOC
=
0x980
;
uint256
internal
constant
ID4_Y_LOC
=
0x9a0
;
uint256
internal
constant
CONTAINS_RECURSIVE_PROOF_LOC
=
0x9c0
;
uint256
internal
constant
RECURSIVE_PROOF_PUBLIC_INPUT_INDICES_LOC
=
0x9e0
;
uint256
internal
constant
G2X_X0_LOC
=
0xa00
;
uint256
internal
constant
G2X_X1_LOC
=
0xa20
;
uint256
internal
constant
G2X_Y0_LOC
=
0xa40
;
uint256
internal
constant
G2X_Y1_LOC
=
0xa60
;
// ### PROOF DATA MEMORY LOCATIONS
uint256
internal
constant
W1_X_LOC
=
0x1200
;
uint256
internal
constant
W1_Y_LOC
=
0x1220
;
uint256
internal
constant
W2_X_LOC
=
0x1240
;
uint256
internal
constant
W2_Y_LOC
=
0x1260
;
uint256
internal
constant
W3_X_LOC
=
0x1280
;
uint256
internal
constant
W3_Y_LOC
=
0x12a0
;
uint256
internal
constant
W4_X_LOC
=
0x12c0
;
uint256
internal
constant
W4_Y_LOC
=
0x12e0
;
uint256
internal
constant
S_X_LOC
=
0x1300
;
uint256
internal
constant
S_Y_LOC
=
0x1320
;
uint256
internal
constant
Z_X_LOC
=
0x1340
;
uint256
internal
constant
Z_Y_LOC
=
0x1360
;
uint256
internal
constant
Z_LOOKUP_X_LOC
=
0x1380
;
uint256
internal
constant
Z_LOOKUP_Y_LOC
=
0x13a0
;
uint256
internal
constant
T1_X_LOC
=
0x13c0
;
uint256
internal
constant
T1_Y_LOC
=
0x13e0
;
uint256
internal
constant
T2_X_LOC
=
0x1400
;
uint256
internal
constant
T2_Y_LOC
=
0x1420
;
uint256
internal
constant
T3_X_LOC
=
0x1440
;
uint256
internal
constant
T3_Y_LOC
=
0x1460
;
uint256
internal
constant
T4_X_LOC
=
0x1480
;
uint256
internal
constant
T4_Y_LOC
=
0x14a0
;
uint256
internal
constant
W1_EVAL_LOC
=
0x1600
;
uint256
internal
constant
W2_EVAL_LOC
=
0x1620
;
uint256
internal
constant
W3_EVAL_LOC
=
0x1640
;
uint256
internal
constant
W4_EVAL_LOC
=
0x1660
;
uint256
internal
constant
S_EVAL_LOC
=
0x1680
;
uint256
internal
constant
Z_EVAL_LOC
=
0x16a0
;
uint256
internal
constant
Z_LOOKUP_EVAL_LOC
=
0x16c0
;
uint256
internal
constant
Q1_EVAL_LOC
=
0x16e0
;
uint256
internal
constant
Q2_EVAL_LOC
=
0x1700
;
uint256
internal
constant
Q3_EVAL_LOC
=
0x1720
;
uint256
internal
constant
Q4_EVAL_LOC
=
0x1740
;
uint256
internal
constant
QM_EVAL_LOC
=
0x1760
;
uint256
internal
constant
QC_EVAL_LOC
=
0x1780
;
uint256
internal
constant
QARITH_EVAL_LOC
=
0x17a0
;
uint256
internal
constant
QSORT_EVAL_LOC
=
0x17c0
;
uint256
internal
constant
QELLIPTIC_EVAL_LOC
=
0x17e0
;
uint256
internal
constant
QAUX_EVAL_LOC
=
0x1800
;
uint256
internal
constant
TABLE1_EVAL_LOC
=
0x1840
;
uint256
internal
constant
TABLE2_EVAL_LOC
=
0x1860
;
uint256
internal
constant
TABLE3_EVAL_LOC
=
0x1880
;
uint256
internal
constant
TABLE4_EVAL_LOC
=
0x18a0
;
uint256
internal
constant
TABLE_TYPE_EVAL_LOC
=
0x18c0
;
uint256
internal
constant
ID1_EVAL_LOC
=
0x18e0
;
uint256
internal
constant
ID2_EVAL_LOC
=
0x1900
;
uint256
internal
constant
ID3_EVAL_LOC
=
0x1920
;
uint256
internal
constant
ID4_EVAL_LOC
=
0x1940
;
uint256
internal
constant
SIGMA1_EVAL_LOC
=
0x1960
;
uint256
internal
constant
SIGMA2_EVAL_LOC
=
0x1980
;
uint256
internal
constant
SIGMA3_EVAL_LOC
=
0x19a0
;
uint256
internal
constant
SIGMA4_EVAL_LOC
=
0x19c0
;
uint256
internal
constant
W1_OMEGA_EVAL_LOC
=
0x19e0
;
uint256
internal
constant
W2_OMEGA_EVAL_LOC
=
0x2000
;
uint256
internal
constant
W3_OMEGA_EVAL_LOC
=
0x2020
;
uint256
internal
constant
W4_OMEGA_EVAL_LOC
=
0x2040
;
uint256
internal
constant
S_OMEGA_EVAL_LOC
=
0x2060
;
uint256
internal
constant
Z_OMEGA_EVAL_LOC
=
0x2080
;
uint256
internal
constant
Z_LOOKUP_OMEGA_EVAL_LOC
=
0x20a0
;
uint256
internal
constant
TABLE1_OMEGA_EVAL_LOC
=
0x20c0
;
uint256
internal
constant
TABLE2_OMEGA_EVAL_LOC
=
0x20e0
;
uint256
internal
constant
TABLE3_OMEGA_EVAL_LOC
=
0x2100
;
uint256
internal
constant
TABLE4_OMEGA_EVAL_LOC
=
0x2120
;
uint256
internal
constant
PI_Z_X_LOC
=
0x2300
;
uint256
internal
constant
PI_Z_Y_LOC
=
0x2320
;
uint256
internal
constant
PI_Z_OMEGA_X_LOC
=
0x2340
;
uint256
internal
constant
PI_Z_OMEGA_Y_LOC
=
0x2360
;
// Used for elliptic widget. These are alias names for wire + shifted wire evaluations
uint256
internal
constant
X1_EVAL_LOC
=
W2_EVAL_LOC
;
uint256
internal
constant
X2_EVAL_LOC
=
W1_OMEGA_EVAL_LOC
;
uint256
internal
constant
X3_EVAL_LOC
=
W2_OMEGA_EVAL_LOC
;
uint256
internal
constant
Y1_EVAL_LOC
=
W3_EVAL_LOC
;
uint256
internal
constant
Y2_EVAL_LOC
=
W4_OMEGA_EVAL_LOC
;
uint256
internal
constant
Y3_EVAL_LOC
=
W3_OMEGA_EVAL_LOC
;
uint256
internal
constant
QBETA_LOC
=
Q3_EVAL_LOC
;
uint256
internal
constant
QBETA_SQR_LOC
=
Q4_EVAL_LOC
;
uint256
internal
constant
QSIGN_LOC
=
Q1_EVAL_LOC
;
// ### CHALLENGES MEMORY OFFSETS
uint256
internal
constant
C_BETA_LOC
=
0x2600
;
uint256
internal
constant
C_GAMMA_LOC
=
0x2620
;
uint256
internal
constant
C_ALPHA_LOC
=
0x2640
;
uint256
internal
constant
C_ETA_LOC
=
0x2660
;
uint256
internal
constant
C_ETA_SQR_LOC
=
0x2680
;
uint256
internal
constant
C_ETA_CUBE_LOC
=
0x26a0
;
uint256
internal
constant
C_ZETA_LOC
=
0x26c0
;
uint256
internal
constant
C_CURRENT_LOC
=
0x26e0
;
uint256
internal
constant
C_V0_LOC
=
0x2700
;
uint256
internal
constant
C_V1_LOC
=
0x2720
;
uint256
internal
constant
C_V2_LOC
=
0x2740
;
uint256
internal
constant
C_V3_LOC
=
0x2760
;
uint256
internal
constant
C_V4_LOC
=
0x2780
;
uint256
internal
constant
C_V5_LOC
=
0x27a0
;
uint256
internal
constant
C_V6_LOC
=
0x27c0
;
uint256
internal
constant
C_V7_LOC
=
0x27e0
;
uint256
internal
constant
C_V8_LOC
=
0x2800
;
uint256
internal
constant
C_V9_LOC
=
0x2820
;
uint256
internal
constant
C_V10_LOC
=
0x2840
;
uint256
internal
constant
C_V11_LOC
=
0x2860
;
uint256
internal
constant
C_V12_LOC
=
0x2880
;
uint256
internal
constant
C_V13_LOC
=
0x28a0
;
uint256
internal
constant
C_V14_LOC
=
0x28c0
;
uint256
internal
constant
C_V15_LOC
=
0x28e0
;
uint256
internal
constant
C_V16_LOC
=
0x2900
;
uint256
internal
constant
C_V17_LOC
=
0x2920
;
uint256
internal
constant
C_V18_LOC
=
0x2940
;
uint256
internal
constant
C_V19_LOC
=
0x2960
;
uint256
internal
constant
C_V20_LOC
=
0x2980
;
uint256
internal
constant
C_V21_LOC
=
0x29a0
;
uint256
internal
constant
C_V22_LOC
=
0x29c0
;
uint256
internal
constant
C_V23_LOC
=
0x29e0
;
uint256
internal
constant
C_V24_LOC
=
0x2a00
;
uint256
internal
constant
C_V25_LOC
=
0x2a20
;
uint256
internal
constant
C_V26_LOC
=
0x2a40
;
uint256
internal
constant
C_V27_LOC
=
0x2a60
;
uint256
internal
constant
C_V28_LOC
=
0x2a80
;
uint256
internal
constant
C_V29_LOC
=
0x2aa0
;
uint256
internal
constant
C_V30_LOC
=
0x2ac0
;
uint256
internal
constant
C_U_LOC
=
0x2b00
;
// ### LOCAL VARIABLES MEMORY OFFSETS
uint256
internal
constant
DELTA_NUMERATOR_LOC
=
0x3000
;
uint256
internal
constant
DELTA_DENOMINATOR_LOC
=
0x3020
;
uint256
internal
constant
ZETA_POW_N_LOC
=
0x3040
;
uint256
internal
constant
PUBLIC_INPUT_DELTA_LOC
=
0x3060
;
uint256
internal
constant
ZERO_POLY_LOC
=
0x3080
;
uint256
internal
constant
L_START_LOC
=
0x30a0
;
uint256
internal
constant
L_END_LOC
=
0x30c0
;
uint256
internal
constant
R_ZERO_EVAL_LOC
=
0x30e0
;
uint256
internal
constant
PLOOKUP_DELTA_NUMERATOR_LOC
=
0x3100
;
uint256
internal
constant
PLOOKUP_DELTA_DENOMINATOR_LOC
=
0x3120
;
uint256
internal
constant
PLOOKUP_DELTA_LOC
=
0x3140
;
uint256
internal
constant
ACCUMULATOR_X_LOC
=
0x3160
;
uint256
internal
constant
ACCUMULATOR_Y_LOC
=
0x3180
;
uint256
internal
constant
ACCUMULATOR2_X_LOC
=
0x31a0
;
uint256
internal
constant
ACCUMULATOR2_Y_LOC
=
0x31c0
;
uint256
internal
constant
PAIRING_LHS_X_LOC
=
0x31e0
;
uint256
internal
constant
PAIRING_LHS_Y_LOC
=
0x3200
;
uint256
internal
constant
PAIRING_RHS_X_LOC
=
0x3220
;
uint256
internal
constant
PAIRING_RHS_Y_LOC
=
0x3240
;
// misc stuff
uint256
internal
constant
OMEGA_INVERSE_LOC
=
0x3300
;
uint256
internal
constant
C_ALPHA_SQR_LOC
=
0x3320
;
uint256
internal
constant
C_ALPHA_CUBE_LOC
=
0x3340
;
uint256
internal
constant
C_ALPHA_QUAD_LOC
=
0x3360
;
uint256
internal
constant
C_ALPHA_BASE_LOC
=
0x3380
;
// ### RECURSION VARIABLE MEMORY LOCATIONS
uint256
internal
constant
RECURSIVE_P1_X_LOC
=
0x3400
;
uint256
internal
constant
RECURSIVE_P1_Y_LOC
=
0x3420
;
uint256
internal
constant
RECURSIVE_P2_X_LOC
=
0x3440
;
uint256
internal
constant
RECURSIVE_P2_Y_LOC
=
0x3460
;
uint256
internal
constant
PUBLIC_INPUTS_HASH_LOCATION
=
0x3480
;
// sub-identity storage
uint256
internal
constant
PERMUTATION_IDENTITY
=
0x3500
;
uint256
internal
constant
PLOOKUP_IDENTITY
=
0x3520
;
uint256
internal
constant
ARITHMETIC_IDENTITY
=
0x3540
;
uint256
internal
constant
SORT_IDENTITY
=
0x3560
;
uint256
internal
constant
ELLIPTIC_IDENTITY
=
0x3580
;
uint256
internal
constant
AUX_IDENTITY
=
0x35a0
;
uint256
internal
constant
AUX_NON_NATIVE_FIELD_EVALUATION
=
0x35c0
;
uint256
internal
constant
AUX_LIMB_ACCUMULATOR_EVALUATION
=
0x35e0
;
uint256
internal
constant
AUX_RAM_CONSISTENCY_EVALUATION
=
0x3600
;
uint256
internal
constant
AUX_ROM_CONSISTENCY_EVALUATION
=
0x3620
;
uint256
internal
constant
AUX_MEMORY_EVALUATION
=
0x3640
;
uint256
internal
constant
QUOTIENT_EVAL_LOC
=
0x3660
;
uint256
internal
constant
ZERO_POLY_INVERSE_LOC
=
0x3680
;
// when hashing public inputs we use memory at NU_CHALLENGE_INPUT_LOC_A, as the hash input size is unknown at compile time
uint256
internal
constant
NU_CHALLENGE_INPUT_LOC_A
=
0x36a0
;
uint256
internal
constant
NU_CHALLENGE_INPUT_LOC_B
=
0x36c0
;
uint256
internal
constant
NU_CHALLENGE_INPUT_LOC_C
=
0x36e0
;
bytes4
internal
constant
INVALID_VERIFICATION_KEY_SELECTOR
=
0x7e5769bf
;
bytes4
internal
constant
POINT_NOT_ON_CURVE_SELECTOR
=
0xa3dad654
;
bytes4
internal
constant
PUBLIC_INPUT_INVALID_BN128_G1_POINT_SELECTOR
=
0xeba9f4a6
;
bytes4
internal
constant
PUBLIC_INPUT_GE_P_SELECTOR
=
0x374a972f
;
bytes4
internal
constant
MOD_EXP_FAILURE_SELECTOR
=
0xf894a7bc
;
bytes4
internal
constant
PAIRING_PREAMBLE_FAILED_SELECTOR
=
0x01882d81
;
bytes4
internal
constant
OPENING_COMMITMENT_FAILED_SELECTOR
=
0x4e719763
;
bytes4
internal
constant
PAIRING_FAILED_SELECTOR
=
0xd71fd263
;
uint256
internal
constant
ETA_INPUT_LENGTH
=
0xc0
;
// W1, W2, W3 = 6 * 0x20 bytes
// We need to hash 41 field elements when generating the NU challenge
// w1, w2, w3, w4, s, z, z_lookup, q1, q2, q3, q4, qm, qc, qarith (14)
// qsort, qelliptic, qaux, sigma1, sigma2, sigma, sigma4, (7)
// table1, table2, table3, table4, tabletype, id1, id2, id3, id4, (9)
// w1_omega, w2_omega, w3_omega, w4_omega, s_omega, z_omega, z_lookup_omega, (7)
// table1_omega, table2_omega, table3_omega, table4_omega (4)
uint256
internal
constant
NU_INPUT_LENGTH
=
0x520
;
// 0x520 = 41 * 0x20
// There are ELEVEN G1 group elements added into the transcript in the `beta` round, that we need to skip over
// W1, W2, W3, W4, S, Z, Z_LOOKUP, T1, T2, T3, T4
uint256
internal
constant
NU_CALLDATA_SKIP_LENGTH
=
0x2c0
;
// 11 * 0x40 = 0x2c0
uint256
internal
constant
NEGATIVE_INVERSE_OF_2_MODULO_P
=
0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000
;
uint256
internal
constant
LIMB_SIZE
=
0x100000000000000000
;
// 2<<68
uint256
internal
constant
SUBLIMB_SHIFT
=
0x4000
;
// 2<<14
// y^2 = x^3 + ax + b
// for Grumpkin, a = 0 and b = -17. We use b in a custom gate relation that evaluates elliptic curve arithmetic
uint256
internal
constant
GRUMPKIN_CURVE_B_PARAMETER_NEGATED
=
17
;
error
INVALID_VERIFICATION_KEY
();
error
POINT_NOT_ON_CURVE
();
error
PUBLIC_INPUT_COUNT_INVALID
(
uint256
expected
,
uint256
actual
);
error
PUBLIC_INPUT_INVALID_BN128_G1_POINT
();
error
PUBLIC_INPUT_GE_P
();
error
MOD_EXP_FAILURE
();
error
PAIRING_PREAMBLE_FAILED
();
error
OPENING_COMMITMENT_FAILED
();
error
PAIRING_FAILED
();
function
getVerificationKeyHash
()
public
pure
virtual
returns
(
bytes32
);
/**
* @dev We assume that the verification key loaded by this function is constant as we only verify it on deployment
*/
function
loadVerificationKey
(
uint256
_vk
,
uint256
_omegaInverseLoc
)
internal
pure
virtual
;
constructor
()
{
loadVerificationKey
(
N_LOC
,
OMEGA_INVERSE_LOC
);
// We verify that all of the EC points in the verification key lie on the bn128 curve.
assembly
{
let
q
:=
21888242871839275222246405745257275088696311157297823662689037894645226208583
// EC group order
let
success
:=
1
// VALIDATE Q1
{
let
x
:=
mload
(
Q1_X_LOC
)
let
y
:=
mload
(
Q1_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
success
:=
and
(
success
,
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
}
// VALIDATE Q2
{
let
x
:=
mload
(
Q2_X_LOC
)
let
y
:=
mload
(
Q2_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
success
:=
and
(
success
,
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
}
// VALIDATE Q3
{
let
x
:=
mload
(
Q3_X_LOC
)
let
y
:=
mload
(
Q3_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
success
:=
and
(
success
,
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
}
// VALIDATE Q4
{
let
x
:=
mload
(
Q4_X_LOC
)
let
y
:=
mload
(
Q4_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
success
:=
and
(
success
,
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
mstore
(
0x00
,
x
)
mstore
(
0x20
,
y
)
}
// VALIDATE QM
{
let
x
:=
mload
(
QM_X_LOC
)
let
y
:=
mload
(
QM_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
success
:=
and
(
success
,
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
}
// VALIDATE QC
{
let
x
:=
mload
(
QC_X_LOC
)
let
y
:=
mload
(
QC_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
success
:=
and
(
success
,
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
}
// VALIDATE QARITH
{
let
x
:=
mload
(
QARITH_X_LOC
)
let
y
:=
mload
(
QARITH_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
success
:=
and
(
success
,
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
}
// VALIDATE QSORT
{
let
x
:=
mload
(
QSORT_X_LOC
)
let
y
:=
mload
(
QSORT_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
success
:=
and
(
success
,
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
}
// VALIDATE QELLIPTIC
{
let
x
:=
mload
(
QELLIPTIC_X_LOC
)
let
y
:=
mload
(
QELLIPTIC_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
success
:=
and
(
success
,
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
}
// VALIDATE QAUX
{
let
x
:=
mload
(
QAUX_X_LOC
)
let
y
:=
mload
(
QAUX_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
success
:=
and
(
success
,
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
}
// VALIDATE SIGMA1
{
let
x
:=
mload
(
SIGMA1_X_LOC
)
let
y
:=
mload
(
SIGMA1_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
success
:=
and
(
success
,
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
}
// VALIDATE SIGMA2
{
let
x
:=
mload
(
SIGMA2_X_LOC
)
let
y
:=
mload
(
SIGMA2_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
success
:=
and
(
success
,
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
}
// VALIDATE SIGMA3
{
let
x
:=
mload
(
SIGMA3_X_LOC
)
let
y
:=
mload
(
SIGMA3_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
success
:=
and
(
success
,
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
}
// VALIDATE SIGMA4
{
let
x
:=
mload
(
SIGMA4_X_LOC
)
let
y
:=
mload
(
SIGMA4_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
success
:=
and
(
success
,
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
}
// VALIDATE TABLE1
{
let
x
:=
mload
(
TABLE1_X_LOC
)
let
y
:=
mload
(
TABLE1_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
success
:=
and
(
success
,
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
}
// VALIDATE TABLE2
{
let
x
:=
mload
(
TABLE2_X_LOC
)
let
y
:=
mload
(
TABLE2_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
success
:=
and
(
success
,
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
}
// VALIDATE TABLE3
{
let
x
:=
mload
(
TABLE3_X_LOC
)
let
y
:=
mload
(
TABLE3_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
success
:=
and
(
success
,
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
}
// VALIDATE TABLE4
{
let
x
:=
mload
(
TABLE4_X_LOC
)
let
y
:=
mload
(
TABLE4_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
success
:=
and
(
success
,
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
}
// VALIDATE TABLE_TYPE
{
let
x
:=
mload
(
TABLE_TYPE_X_LOC
)
let
y
:=
mload
(
TABLE_TYPE_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
success
:=
and
(
success
,
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
}
// VALIDATE ID1
{
let
x
:=
mload
(
ID1_X_LOC
)
let
y
:=
mload
(
ID1_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
success
:=
and
(
success
,
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
}
// VALIDATE ID2
{
let
x
:=
mload
(
ID2_X_LOC
)
let
y
:=
mload
(
ID2_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
success
:=
and
(
success
,
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
}
// VALIDATE ID3
{
let
x
:=
mload
(
ID3_X_LOC
)
let
y
:=
mload
(
ID3_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
success
:=
and
(
success
,
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
}
// VALIDATE ID4
{
let
x
:=
mload
(
ID4_X_LOC
)
let
y
:=
mload
(
ID4_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
success
:=
and
(
success
,
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
}
if
iszero
(
success
)
{
mstore
(
0x0
,
INVALID_VERIFICATION_KEY_SELECTOR
)
revert
(
0x00
,
0x04
)
}
}
}
/**
* @notice Verify a Ultra Plonk proof
* @param _proof - The serialized proof
* @param _publicInputs - An array of the public inputs
* @return True if proof is valid, reverts otherwise
*/
function
verify
(
bytes
calldata
_proof
,
bytes32
[]
calldata
_publicInputs
)
external
view
returns
(
bool
)
{
loadVerificationKey
(
N_LOC
,
OMEGA_INVERSE_LOC
);
uint256
requiredPublicInputCount
;
assembly
{
requiredPublicInputCount
:=
mload
(
NUM_INPUTS_LOC
)
}
if
(
requiredPublicInputCount
!=
_publicInputs
.
length
)
{
revert
PUBLIC_INPUT_COUNT_INVALID
(
requiredPublicInputCount
,
_publicInputs
.
length
);
}
assembly
{
let
q
:=
21888242871839275222246405745257275088696311157297823662689037894645226208583
// EC group order
let
p
:=
21888242871839275222246405745257275088548364400416034343698204186575808495617
// Prime field order
/**
* LOAD PROOF FROM CALLDATA
*/
{
let
data_ptr
:=
add
(
calldataload
(
0x04
),
0x24
)
mstore
(
W1_Y_LOC
,
mod
(
calldataload
(
data_ptr
),
q
))
mstore
(
W1_X_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x20
)),
q
))
mstore
(
W2_Y_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x40
)),
q
))
mstore
(
W2_X_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x60
)),
q
))
mstore
(
W3_Y_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x80
)),
q
))
mstore
(
W3_X_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0xa0
)),
q
))
mstore
(
W4_Y_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0xc0
)),
q
))
mstore
(
W4_X_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0xe0
)),
q
))
mstore
(
S_Y_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x100
)),
q
))
mstore
(
S_X_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x120
)),
q
))
mstore
(
Z_Y_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x140
)),
q
))
mstore
(
Z_X_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x160
)),
q
))
mstore
(
Z_LOOKUP_Y_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x180
)),
q
))
mstore
(
Z_LOOKUP_X_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x1a0
)),
q
))
mstore
(
T1_Y_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x1c0
)),
q
))
mstore
(
T1_X_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x1e0
)),
q
))
mstore
(
T2_Y_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x200
)),
q
))
mstore
(
T2_X_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x220
)),
q
))
mstore
(
T3_Y_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x240
)),
q
))
mstore
(
T3_X_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x260
)),
q
))
mstore
(
T4_Y_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x280
)),
q
))
mstore
(
T4_X_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x2a0
)),
q
))
mstore
(
W1_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x2c0
)),
p
))
mstore
(
W2_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x2e0
)),
p
))
mstore
(
W3_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x300
)),
p
))
mstore
(
W4_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x320
)),
p
))
mstore
(
S_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x340
)),
p
))
mstore
(
Z_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x360
)),
p
))
mstore
(
Z_LOOKUP_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x380
)),
p
))
mstore
(
Q1_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x3a0
)),
p
))
mstore
(
Q2_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x3c0
)),
p
))
mstore
(
Q3_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x3e0
)),
p
))
mstore
(
Q4_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x400
)),
p
))
mstore
(
QM_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x420
)),
p
))
mstore
(
QC_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x440
)),
p
))
mstore
(
QARITH_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x460
)),
p
))
mstore
(
QSORT_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x480
)),
p
))
mstore
(
QELLIPTIC_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x4a0
)),
p
))
mstore
(
QAUX_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x4c0
)),
p
))
mstore
(
SIGMA1_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x4e0
)),
p
))
mstore
(
SIGMA2_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x500
)),
p
))
mstore
(
SIGMA3_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x520
)),
p
))
mstore
(
SIGMA4_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x540
)),
p
))
mstore
(
TABLE1_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x560
)),
p
))
mstore
(
TABLE2_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x580
)),
p
))
mstore
(
TABLE3_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x5a0
)),
p
))
mstore
(
TABLE4_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x5c0
)),
p
))
mstore
(
TABLE_TYPE_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x5e0
)),
p
))
mstore
(
ID1_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x600
)),
p
))
mstore
(
ID2_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x620
)),
p
))
mstore
(
ID3_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x640
)),
p
))
mstore
(
ID4_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x660
)),
p
))
mstore
(
W1_OMEGA_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x680
)),
p
))
mstore
(
W2_OMEGA_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x6a0
)),
p
))
mstore
(
W3_OMEGA_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x6c0
)),
p
))
mstore
(
W4_OMEGA_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x6e0
)),
p
))
mstore
(
S_OMEGA_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x700
)),
p
))
mstore
(
Z_OMEGA_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x720
)),
p
))
mstore
(
Z_LOOKUP_OMEGA_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x740
)),
p
))
mstore
(
TABLE1_OMEGA_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x760
)),
p
))
mstore
(
TABLE2_OMEGA_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x780
)),
p
))
mstore
(
TABLE3_OMEGA_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x7a0
)),
p
))
mstore
(
TABLE4_OMEGA_EVAL_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x7c0
)),
p
))
mstore
(
PI_Z_Y_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x7e0
)),
q
))
mstore
(
PI_Z_X_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x800
)),
q
))
mstore
(
PI_Z_OMEGA_Y_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x820
)),
q
))
mstore
(
PI_Z_OMEGA_X_LOC
,
mod
(
calldataload
(
add
(
data_ptr
,
0x840
)),
q
))
}
/**
* LOAD RECURSIVE PROOF INTO MEMORY
*/
{
if
mload
(
CONTAINS_RECURSIVE_PROOF_LOC
)
{
let
public_inputs_ptr
:=
add
(
calldataload
(
0x24
),
0x24
)
let
index_counter
:=
add
(
shl
(
5
,
mload
(
RECURSIVE_PROOF_PUBLIC_INPUT_INDICES_LOC
)),
public_inputs_ptr
)
let
x0
:=
calldataload
(
index_counter
)
x0
:=
add
(
x0
,
shl
(
68
,
calldataload
(
add
(
index_counter
,
0x20
))))
x0
:=
add
(
x0
,
shl
(
136
,
calldataload
(
add
(
index_counter
,
0x40
))))
x0
:=
add
(
x0
,
shl
(
204
,
calldataload
(
add
(
index_counter
,
0x60
))))
let
y0
:=
calldataload
(
add
(
index_counter
,
0x80
))
y0
:=
add
(
y0
,
shl
(
68
,
calldataload
(
add
(
index_counter
,
0xa0
))))
y0
:=
add
(
y0
,
shl
(
136
,
calldataload
(
add
(
index_counter
,
0xc0
))))
y0
:=
add
(
y0
,
shl
(
204
,
calldataload
(
add
(
index_counter
,
0xe0
))))
let
x1
:=
calldataload
(
add
(
index_counter
,
0x100
))
x1
:=
add
(
x1
,
shl
(
68
,
calldataload
(
add
(
index_counter
,
0x120
))))
x1
:=
add
(
x1
,
shl
(
136
,
calldataload
(
add
(
index_counter
,
0x140
))))
x1
:=
add
(
x1
,
shl
(
204
,
calldataload
(
add
(
index_counter
,
0x160
))))
let
y1
:=
calldataload
(
add
(
index_counter
,
0x180
))
y1
:=
add
(
y1
,
shl
(
68
,
calldataload
(
add
(
index_counter
,
0x1a0
))))
y1
:=
add
(
y1
,
shl
(
136
,
calldataload
(
add
(
index_counter
,
0x1c0
))))
y1
:=
add
(
y1
,
shl
(
204
,
calldataload
(
add
(
index_counter
,
0x1e0
))))
mstore
(
RECURSIVE_P1_X_LOC
,
x0
)
mstore
(
RECURSIVE_P1_Y_LOC
,
y0
)
mstore
(
RECURSIVE_P2_X_LOC
,
x1
)
mstore
(
RECURSIVE_P2_Y_LOC
,
y1
)
// validate these are valid bn128 G1 points
if
iszero
(
and
(
and
(
lt
(
x0
,
q
),
lt
(
x1
,
q
)),
and
(
lt
(
y0
,
q
),
lt
(
y1
,
q
))))
{
mstore
(
0x00
,
PUBLIC_INPUT_INVALID_BN128_G1_POINT_SELECTOR
)
revert
(
0x00
,
0x04
)
}
}
}
{
/**
* Generate initial challenge
*/
mstore
(
0x00
,
shl
(
224
,
mload
(
N_LOC
)))
mstore
(
0x04
,
shl
(
224
,
mload
(
NUM_INPUTS_LOC
)))
let
challenge
:=
keccak256
(
0x00
,
0x08
)
/**
* Generate eta challenge
*/
mstore
(
PUBLIC_INPUTS_HASH_LOCATION
,
challenge
)
// The public input location is stored at 0x24, we then add 0x24 to skip selector and the length of public inputs
let
public_inputs_start
:=
add
(
calldataload
(
0x24
),
0x24
)
// copy the public inputs over
let
public_input_size
:=
mul
(
mload
(
NUM_INPUTS_LOC
),
0x20
)
calldatacopy
(
add
(
PUBLIC_INPUTS_HASH_LOCATION
,
0x20
),
public_inputs_start
,
public_input_size
)
// copy W1, W2, W3 into challenge. Each point is 0x40 bytes, so load 0xc0 = 3 * 0x40 bytes (ETA input length)
let
w_start
:=
add
(
calldataload
(
0x04
),
0x24
)
calldatacopy
(
add
(
add
(
PUBLIC_INPUTS_HASH_LOCATION
,
0x20
),
public_input_size
),
w_start
,
ETA_INPUT_LENGTH
)
// Challenge is the old challenge + public inputs + W1, W2, W3 (0x20 + public_input_size + 0xc0)
let
challenge_bytes_size
:=
add
(
0x20
,
add
(
public_input_size
,
ETA_INPUT_LENGTH
))
challenge
:=
keccak256
(
PUBLIC_INPUTS_HASH_LOCATION
,
challenge_bytes_size
)
{
let
eta
:=
mod
(
challenge
,
p
)
mstore
(
C_ETA_LOC
,
eta
)
mstore
(
C_ETA_SQR_LOC
,
mulmod
(
eta
,
eta
,
p
))
mstore
(
C_ETA_CUBE_LOC
,
mulmod
(
mload
(
C_ETA_SQR_LOC
),
eta
,
p
))
}
/**
* Generate beta challenge
*/
mstore
(
0x00
,
challenge
)
mstore
(
0x20
,
mload
(
W4_Y_LOC
))
mstore
(
0x40
,
mload
(
W4_X_LOC
))
mstore
(
0x60
,
mload
(
S_Y_LOC
))
mstore
(
0x80
,
mload
(
S_X_LOC
))
challenge
:=
keccak256
(
0x00
,
0xa0
)
mstore
(
C_BETA_LOC
,
mod
(
challenge
,
p
))
/**
* Generate gamma challenge
*/
mstore
(
0x00
,
challenge
)
mstore8
(
0x20
,
0x01
)
challenge
:=
keccak256
(
0x00
,
0x21
)
mstore
(
C_GAMMA_LOC
,
mod
(
challenge
,
p
))
/**
* Generate alpha challenge
*/
mstore
(
0x00
,
challenge
)
mstore
(
0x20
,
mload
(
Z_Y_LOC
))
mstore
(
0x40
,
mload
(
Z_X_LOC
))
mstore
(
0x60
,
mload
(
Z_LOOKUP_Y_LOC
))
mstore
(
0x80
,
mload
(
Z_LOOKUP_X_LOC
))
challenge
:=
keccak256
(
0x00
,
0xa0
)
mstore
(
C_ALPHA_LOC
,
mod
(
challenge
,
p
))
/**
* Compute and store some powers of alpha for future computations
*/
let
alpha
:=
mload
(
C_ALPHA_LOC
)
mstore
(
C_ALPHA_SQR_LOC
,
mulmod
(
alpha
,
alpha
,
p
))
mstore
(
C_ALPHA_CUBE_LOC
,
mulmod
(
mload
(
C_ALPHA_SQR_LOC
),
alpha
,
p
))
mstore
(
C_ALPHA_QUAD_LOC
,
mulmod
(
mload
(
C_ALPHA_CUBE_LOC
),
alpha
,
p
))
mstore
(
C_ALPHA_BASE_LOC
,
alpha
)
/**
* Generate zeta challenge
*/
mstore
(
0x00
,
challenge
)
mstore
(
0x20
,
mload
(
T1_Y_LOC
))
mstore
(
0x40
,
mload
(
T1_X_LOC
))
mstore
(
0x60
,
mload
(
T2_Y_LOC
))
mstore
(
0x80
,
mload
(
T2_X_LOC
))
mstore
(
0xa0
,
mload
(
T3_Y_LOC
))
mstore
(
0xc0
,
mload
(
T3_X_LOC
))
mstore
(
0xe0
,
mload
(
T4_Y_LOC
))
mstore
(
0x100
,
mload
(
T4_X_LOC
))
challenge
:=
keccak256
(
0x00
,
0x120
)
mstore
(
C_ZETA_LOC
,
mod
(
challenge
,
p
))
mstore
(
C_CURRENT_LOC
,
challenge
)
}
/**
* EVALUATE FIELD OPERATIONS
*/
/**
* COMPUTE PUBLIC INPUT DELTA
* ΔPI = ∏ᵢ∈ℓ(wᵢ + β σ(i) + γ) / ∏ᵢ∈ℓ(wᵢ + β σ'(i) + γ)
*/
{
let
beta
:=
mload
(
C_BETA_LOC
)
// β
let
gamma
:=
mload
(
C_GAMMA_LOC
)
// γ
let
work_root
:=
mload
(
OMEGA_LOC
)
// ω
let
numerator_value
:=
1
let
denominator_value
:=
1
let
p_clone
:=
p
// move p to the front of the stack
let
valid_inputs
:=
true
// Load the starting point of the public inputs (jump over the selector and the length of public inputs [0x24])
let
public_inputs_ptr
:=
add
(
calldataload
(
0x24
),
0x24
)
// endpoint_ptr = public_inputs_ptr + num_inputs * 0x20. // every public input is 0x20 bytes
let
endpoint_ptr
:=
add
(
public_inputs_ptr
,
mul
(
mload
(
NUM_INPUTS_LOC
),
0x20
))
// root_1 = β * 0x05
let
root_1
:=
mulmod
(
beta
,
0x05
,
p_clone
)
// k1.β
// root_2 = β * 0x0c
let
root_2
:=
mulmod
(
beta
,
0x0c
,
p_clone
)
// @note 0x05 + 0x07 == 0x0c == external coset generator
for
{}
lt
(
public_inputs_ptr
,
endpoint_ptr
)
{
public_inputs_ptr
:=
add
(
public_inputs_ptr
,
0x20
)
}
{
/**
* input = public_input[i]
* valid_inputs &= input < p
* temp = input + gamma
* numerator_value *= (β.σ(i) + wᵢ + γ) // σ(i) = 0x05.ωⁱ
* denominator_value *= (β.σ'(i) + wᵢ + γ) // σ'(i) = 0x0c.ωⁱ
* root_1 *= ω
* root_2 *= ω
*/
let
input
:=
calldataload
(
public_inputs_ptr
)
valid_inputs
:=
and
(
valid_inputs
,
lt
(
input
,
p_clone
))
let
temp
:=
addmod
(
input
,
gamma
,
p_clone
)
numerator_value
:=
mulmod
(
numerator_value
,
add
(
root_1
,
temp
),
p_clone
)
denominator_value
:=
mulmod
(
denominator_value
,
add
(
root_2
,
temp
),
p_clone
)
root_1
:=
mulmod
(
root_1
,
work_root
,
p_clone
)
root_2
:=
mulmod
(
root_2
,
work_root
,
p_clone
)
}
// Revert if not all public inputs are field elements (i.e. < p)
if
iszero
(
valid_inputs
)
{
mstore
(
0x00
,
PUBLIC_INPUT_GE_P_SELECTOR
)
revert
(
0x00
,
0x04
)
}
mstore
(
DELTA_NUMERATOR_LOC
,
numerator_value
)
mstore
(
DELTA_DENOMINATOR_LOC
,
denominator_value
)
}
/**
* Compute Plookup delta factor [γ(1 + β)]^{n-k}
* k = num roots cut out of Z_H = 4
*/
{
let
delta_base
:=
mulmod
(
mload
(
C_GAMMA_LOC
),
addmod
(
mload
(
C_BETA_LOC
),
1
,
p
),
p
)
let
delta_numerator
:=
delta_base
{
let
exponent
:=
mload
(
N_LOC
)
let
count
:=
1
for
{}
lt
(
count
,
exponent
)
{
count
:=
add
(
count
,
count
)
}
{
delta_numerator
:=
mulmod
(
delta_numerator
,
delta_numerator
,
p
)
}
}
mstore
(
PLOOKUP_DELTA_NUMERATOR_LOC
,
delta_numerator
)
let
delta_denominator
:=
mulmod
(
delta_base
,
delta_base
,
p
)
delta_denominator
:=
mulmod
(
delta_denominator
,
delta_denominator
,
p
)
mstore
(
PLOOKUP_DELTA_DENOMINATOR_LOC
,
delta_denominator
)
}
/**
* Compute lagrange poly and vanishing poly fractions
*/
{
/**
* vanishing_numerator = zeta
* ZETA_POW_N = zeta^n
* vanishing_numerator -= 1
* accumulating_root = omega_inverse
* work_root = p - accumulating_root
* domain_inverse = domain_inverse
* vanishing_denominator = zeta + work_root
* work_root *= accumulating_root
* vanishing_denominator *= (zeta + work_root)
* work_root *= accumulating_root
* vanishing_denominator *= (zeta + work_root)
* vanishing_denominator *= (zeta + (zeta + accumulating_root))
* work_root = omega
* lagrange_numerator = vanishing_numerator * domain_inverse
* l_start_denominator = zeta - 1
* accumulating_root = work_root^2
* l_end_denominator = accumulating_root^2 * work_root * zeta - 1
* Note: l_end_denominator term contains a term \omega^5 to cut out 5 roots of unity from vanishing poly
*/
let
zeta
:=
mload
(
C_ZETA_LOC
)
// compute zeta^n, where n is a power of 2
let
vanishing_numerator
:=
zeta
{
// pow_small
let
exponent
:=
mload
(
N_LOC
)
let
count
:=
1
for
{}
lt
(
count
,
exponent
)
{
count
:=
add
(
count
,
count
)
}
{
vanishing_numerator
:=
mulmod
(
vanishing_numerator
,
vanishing_numerator
,
p
)
}
}
mstore
(
ZETA_POW_N_LOC
,
vanishing_numerator
)
vanishing_numerator
:=
addmod
(
vanishing_numerator
,
sub
(
p
,
1
),
p
)
let
accumulating_root
:=
mload
(
OMEGA_INVERSE_LOC
)
let
work_root
:=
sub
(
p
,
accumulating_root
)
let
domain_inverse
:=
mload
(
DOMAIN_INVERSE_LOC
)
let
vanishing_denominator
:=
addmod
(
zeta
,
work_root
,
p
)
work_root
:=
mulmod
(
work_root
,
accumulating_root
,
p
)
vanishing_denominator
:=
mulmod
(
vanishing_denominator
,
addmod
(
zeta
,
work_root
,
p
),
p
)
work_root
:=
mulmod
(
work_root
,
accumulating_root
,
p
)
vanishing_denominator
:=
mulmod
(
vanishing_denominator
,
addmod
(
zeta
,
work_root
,
p
),
p
)
vanishing_denominator
:=
mulmod
(
vanishing_denominator
,
addmod
(
zeta
,
mulmod
(
work_root
,
accumulating_root
,
p
),
p
),
p
)
work_root
:=
mload
(
OMEGA_LOC
)
let
lagrange_numerator
:=
mulmod
(
vanishing_numerator
,
domain_inverse
,
p
)
let
l_start_denominator
:=
addmod
(
zeta
,
sub
(
p
,
1
),
p
)
accumulating_root
:=
mulmod
(
work_root
,
work_root
,
p
)
let
l_end_denominator
:=
addmod
(
mulmod
(
mulmod
(
mulmod
(
accumulating_root
,
accumulating_root
,
p
),
work_root
,
p
),
zeta
,
p
),
sub
(
p
,
1
),
p
)
/**
* Compute inversions using Montgomery's batch inversion trick
*/
let
accumulator
:=
mload
(
DELTA_DENOMINATOR_LOC
)
let
t0
:=
accumulator
accumulator
:=
mulmod
(
accumulator
,
vanishing_denominator
,
p
)
let
t1
:=
accumulator
accumulator
:=
mulmod
(
accumulator
,
vanishing_numerator
,
p
)
let
t2
:=
accumulator
accumulator
:=
mulmod
(
accumulator
,
l_start_denominator
,
p
)
let
t3
:=
accumulator
accumulator
:=
mulmod
(
accumulator
,
mload
(
PLOOKUP_DELTA_DENOMINATOR_LOC
),
p
)
let
t4
:=
accumulator
{
mstore
(
0
,
0x20
)
mstore
(
0x20
,
0x20
)
mstore
(
0x40
,
0x20
)
mstore
(
0x60
,
mulmod
(
accumulator
,
l_end_denominator
,
p
))
mstore
(
0x80
,
sub
(
p
,
2
))
mstore
(
0xa0
,
p
)
if
iszero
(
staticcall
(
gas
(),
0x05
,
0x00
,
0xc0
,
0x00
,
0x20
))
{
mstore
(
0x0
,
MOD_EXP_FAILURE_SELECTOR
)
revert
(
0x00
,
0x04
)
}
accumulator
:=
mload
(
0x00
)
}
t4
:=
mulmod
(
accumulator
,
t4
,
p
)
accumulator
:=
mulmod
(
accumulator
,
l_end_denominator
,
p
)
t3
:=
mulmod
(
accumulator
,
t3
,
p
)
accumulator
:=
mulmod
(
accumulator
,
mload
(
PLOOKUP_DELTA_DENOMINATOR_LOC
),
p
)
t2
:=
mulmod
(
accumulator
,
t2
,
p
)
accumulator
:=
mulmod
(
accumulator
,
l_start_denominator
,
p
)
t1
:=
mulmod
(
accumulator
,
t1
,
p
)
accumulator
:=
mulmod
(
accumulator
,
vanishing_numerator
,
p
)
t0
:=
mulmod
(
accumulator
,
t0
,
p
)
accumulator
:=
mulmod
(
accumulator
,
vanishing_denominator
,
p
)
accumulator
:=
mulmod
(
mulmod
(
accumulator
,
accumulator
,
p
),
mload
(
DELTA_DENOMINATOR_LOC
),
p
)
mstore
(
PUBLIC_INPUT_DELTA_LOC
,
mulmod
(
mload
(
DELTA_NUMERATOR_LOC
),
accumulator
,
p
))
mstore
(
ZERO_POLY_LOC
,
mulmod
(
vanishing_numerator
,
t0
,
p
))
mstore
(
ZERO_POLY_INVERSE_LOC
,
mulmod
(
vanishing_denominator
,
t1
,
p
))
mstore
(
L_START_LOC
,
mulmod
(
lagrange_numerator
,
t2
,
p
))
mstore
(
PLOOKUP_DELTA_LOC
,
mulmod
(
mload
(
PLOOKUP_DELTA_NUMERATOR_LOC
),
t3
,
p
))
mstore
(
L_END_LOC
,
mulmod
(
lagrange_numerator
,
t4
,
p
))
}
/**
* UltraPlonk Widget Ordering:
*
* 1. Permutation widget
* 2. Plookup widget
* 3. Arithmetic widget
* 4. Fixed base widget (?)
* 5. GenPermSort widget
* 6. Elliptic widget
* 7. Auxiliary widget
*/
/**
* COMPUTE PERMUTATION WIDGET EVALUATION
*/
{
let
alpha
:=
mload
(
C_ALPHA_LOC
)
let
beta
:=
mload
(
C_BETA_LOC
)
let
gamma
:=
mload
(
C_GAMMA_LOC
)
/**
* t1 = (W1 + gamma + beta * ID1) * (W2 + gamma + beta * ID2)
* t2 = (W3 + gamma + beta * ID3) * (W4 + gamma + beta * ID4)
* result = alpha_base * z_eval * t1 * t2
* t1 = (W1 + gamma + beta * sigma_1_eval) * (W2 + gamma + beta * sigma_2_eval)
* t2 = (W2 + gamma + beta * sigma_3_eval) * (W3 + gamma + beta * sigma_4_eval)
* result -= (alpha_base * z_omega_eval * t1 * t2)
*/
let
t1
:=
mulmod
(
add
(
add
(
mload
(
W1_EVAL_LOC
),
gamma
),
mulmod
(
beta
,
mload
(
ID1_EVAL_LOC
),
p
)),
add
(
add
(
mload
(
W2_EVAL_LOC
),
gamma
),
mulmod
(
beta
,
mload
(
ID2_EVAL_LOC
),
p
)),
p
)
let
t2
:=
mulmod
(
add
(
add
(
mload
(
W3_EVAL_LOC
),
gamma
),
mulmod
(
beta
,
mload
(
ID3_EVAL_LOC
),
p
)),
add
(
add
(
mload
(
W4_EVAL_LOC
),
gamma
),
mulmod
(
beta
,
mload
(
ID4_EVAL_LOC
),
p
)),
p
)
let
result
:=
mulmod
(
mload
(
C_ALPHA_BASE_LOC
),
mulmod
(
mload
(
Z_EVAL_LOC
),
mulmod
(
t1
,
t2
,
p
),
p
),
p
)
t1
:=
mulmod
(
add
(
add
(
mload
(
W1_EVAL_LOC
),
gamma
),
mulmod
(
beta
,
mload
(
SIGMA1_EVAL_LOC
),
p
)),
add
(
add
(
mload
(
W2_EVAL_LOC
),
gamma
),
mulmod
(
beta
,
mload
(
SIGMA2_EVAL_LOC
),
p
)),
p
)
t2
:=
mulmod
(
add
(
add
(
mload
(
W3_EVAL_LOC
),
gamma
),
mulmod
(
beta
,
mload
(
SIGMA3_EVAL_LOC
),
p
)),
add
(
add
(
mload
(
W4_EVAL_LOC
),
gamma
),
mulmod
(
beta
,
mload
(
SIGMA4_EVAL_LOC
),
p
)),
p
)
result
:=
addmod
(
result
,
sub
(
p
,
mulmod
(
mload
(
C_ALPHA_BASE_LOC
),
mulmod
(
mload
(
Z_OMEGA_EVAL_LOC
),
mulmod
(
t1
,
t2
,
p
),
p
),
p
)),
p
)
/**
* alpha_base *= alpha
* result += alpha_base . (L_{n-k}(ʓ) . (z(ʓ.ω) - ∆_{PI}))
* alpha_base *= alpha
* result += alpha_base . (L_1(ʓ)(Z(ʓ) - 1))
* alpha_Base *= alpha
*/
mstore
(
C_ALPHA_BASE_LOC
,
mulmod
(
mload
(
C_ALPHA_BASE_LOC
),
mload
(
C_ALPHA_LOC
),
p
))
result
:=
addmod
(
result
,
mulmod
(
mload
(
C_ALPHA_BASE_LOC
),
mulmod
(
mload
(
L_END_LOC
),
addmod
(
mload
(
Z_OMEGA_EVAL_LOC
),
sub
(
p
,
mload
(
PUBLIC_INPUT_DELTA_LOC
)),
p
),
p
),
p
),
p
)
mstore
(
C_ALPHA_BASE_LOC
,
mulmod
(
mload
(
C_ALPHA_BASE_LOC
),
mload
(
C_ALPHA_LOC
),
p
))
mstore
(
PERMUTATION_IDENTITY
,
addmod
(
result
,
mulmod
(
mload
(
C_ALPHA_BASE_LOC
),
mulmod
(
mload
(
L_START_LOC
),
addmod
(
mload
(
Z_EVAL_LOC
),
sub
(
p
,
1
),
p
),
p
),
p
),
p
)
)
mstore
(
C_ALPHA_BASE_LOC
,
mulmod
(
mload
(
C_ALPHA_BASE_LOC
),
mload
(
C_ALPHA_LOC
),
p
))
}
/**
* COMPUTE PLOOKUP WIDGET EVALUATION
*/
{
/**
* Goal: f = (w1(z) + q2.w1(zω)) + η(w2(z) + qm.w2(zω)) + η²(w3(z) + qc.w_3(zω)) + q3(z).η³
* f = η.q3(z)
* f += (w3(z) + qc.w_3(zω))
* f *= η
* f += (w2(z) + qm.w2(zω))
* f *= η
* f += (w1(z) + q2.w1(zω))
*/
let
f
:=
mulmod
(
mload
(
C_ETA_LOC
),
mload
(
Q3_EVAL_LOC
),
p
)
f
:=
addmod
(
f
,
addmod
(
mload
(
W3_EVAL_LOC
),
mulmod
(
mload
(
QC_EVAL_LOC
),
mload
(
W3_OMEGA_EVAL_LOC
),
p
),
p
),
p
)
f
:=
mulmod
(
f
,
mload
(
C_ETA_LOC
),
p
)
f
:=
addmod
(
f
,
addmod
(
mload
(
W2_EVAL_LOC
),
mulmod
(
mload
(
QM_EVAL_LOC
),
mload
(
W2_OMEGA_EVAL_LOC
),
p
),
p
),
p
)
f
:=
mulmod
(
f
,
mload
(
C_ETA_LOC
),
p
)
f
:=
addmod
(
f
,
addmod
(
mload
(
W1_EVAL_LOC
),
mulmod
(
mload
(
Q2_EVAL_LOC
),
mload
(
W1_OMEGA_EVAL_LOC
),
p
),
p
),
p
)
// t(z) = table4(z).η³ + table3(z).η² + table2(z).η + table1(z)
let
t
:=
addmod
(
addmod
(
addmod
(
mulmod
(
mload
(
TABLE4_EVAL_LOC
),
mload
(
C_ETA_CUBE_LOC
),
p
),
mulmod
(
mload
(
TABLE3_EVAL_LOC
),
mload
(
C_ETA_SQR_LOC
),
p
),
p
),
mulmod
(
mload
(
TABLE2_EVAL_LOC
),
mload
(
C_ETA_LOC
),
p
),
p
),
mload
(
TABLE1_EVAL_LOC
),
p
)
// t(zw) = table4(zw).η³ + table3(zw).η² + table2(zw).η + table1(zw)
let
t_omega
:=
addmod
(
addmod
(
addmod
(
mulmod
(
mload
(
TABLE4_OMEGA_EVAL_LOC
),
mload
(
C_ETA_CUBE_LOC
),
p
),
mulmod
(
mload
(
TABLE3_OMEGA_EVAL_LOC
),
mload
(
C_ETA_SQR_LOC
),
p
),
p
),
mulmod
(
mload
(
TABLE2_OMEGA_EVAL_LOC
),
mload
(
C_ETA_LOC
),
p
),
p
),
mload
(
TABLE1_OMEGA_EVAL_LOC
),
p
)
/**
* Goal: numerator = (TABLE_TYPE_EVAL * f(z) + γ) * (t(z) + βt(zω) + γ(β + 1)) * (β + 1)
* gamma_beta_constant = γ(β + 1)
* numerator = f * TABLE_TYPE_EVAL + gamma
* temp0 = t(z) + t(zω) * β + gamma_beta_constant
* numerator *= temp0
* numerator *= (β + 1)
* temp0 = alpha * l_1
* numerator += temp0
* numerator *= z_lookup(z)
* numerator -= temp0
*/
let
gamma_beta_constant
:=
mulmod
(
mload
(
C_GAMMA_LOC
),
addmod
(
mload
(
C_BETA_LOC
),
1
,
p
),
p
)
let
numerator
:=
addmod
(
mulmod
(
f
,
mload
(
TABLE_TYPE_EVAL_LOC
),
p
),
mload
(
C_GAMMA_LOC
),
p
)
let
temp0
:=
addmod
(
addmod
(
t
,
mulmod
(
t_omega
,
mload
(
C_BETA_LOC
),
p
),
p
),
gamma_beta_constant
,
p
)
numerator
:=
mulmod
(
numerator
,
temp0
,
p
)
numerator
:=
mulmod
(
numerator
,
addmod
(
mload
(
C_BETA_LOC
),
1
,
p
),
p
)
temp0
:=
mulmod
(
mload
(
C_ALPHA_LOC
),
mload
(
L_START_LOC
),
p
)
numerator
:=
addmod
(
numerator
,
temp0
,
p
)
numerator
:=
mulmod
(
numerator
,
mload
(
Z_LOOKUP_EVAL_LOC
),
p
)
numerator
:=
addmod
(
numerator
,
sub
(
p
,
temp0
),
p
)
/**
* Goal: denominator = z_lookup(zω)*[s(z) + βs(zω) + γ(1 + β)] - [z_lookup(zω) - [γ(1 + β)]^{n-k}]*α²L_end(z)
* note: delta_factor = [γ(1 + β)]^{n-k}
* denominator = s(z) + βs(zω) + γ(β + 1)
* temp1 = α²L_end(z)
* denominator -= temp1
* denominator *= z_lookup(zω)
* denominator += temp1 * delta_factor
* PLOOKUP_IDENTITY = (numerator - denominator).alpha_base
* alpha_base *= alpha^3
*/
let
denominator
:=
addmod
(
addmod
(
mload
(
S_EVAL_LOC
),
mulmod
(
mload
(
S_OMEGA_EVAL_LOC
),
mload
(
C_BETA_LOC
),
p
),
p
),
gamma_beta_constant
,
p
)
let
temp1
:=
mulmod
(
mload
(
C_ALPHA_SQR_LOC
),
mload
(
L_END_LOC
),
p
)
denominator
:=
addmod
(
denominator
,
sub
(
p
,
temp1
),
p
)
denominator
:=
mulmod
(
denominator
,
mload
(
Z_LOOKUP_OMEGA_EVAL_LOC
),
p
)
denominator
:=
addmod
(
denominator
,
mulmod
(
temp1
,
mload
(
PLOOKUP_DELTA_LOC
),
p
),
p
)
mstore
(
PLOOKUP_IDENTITY
,
mulmod
(
addmod
(
numerator
,
sub
(
p
,
denominator
),
p
),
mload
(
C_ALPHA_BASE_LOC
),
p
))
// update alpha
mstore
(
C_ALPHA_BASE_LOC
,
mulmod
(
mload
(
C_ALPHA_BASE_LOC
),
mload
(
C_ALPHA_CUBE_LOC
),
p
))
}
/**
* COMPUTE ARITHMETIC WIDGET EVALUATION
*/
{
/**
* The basic arithmetic gate identity in standard plonk is as follows.
* (w_1 . w_2 . q_m) + (w_1 . q_1) + (w_2 . q_2) + (w_3 . q_3) + (w_4 . q_4) + q_c = 0
* However, for Ultraplonk, we extend this to support "passing" wires between rows (shown without alpha scaling below):
* q_arith * ( ( (-1/2) * (q_arith - 3) * q_m * w_1 * w_2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c ) +
* (q_arith - 1)*( α * (q_arith - 2) * (w_1 + w_4 - w_1_omega + q_m) + w_4_omega) ) = 0
*
* This formula results in several cases depending on q_arith:
* 1. q_arith == 0: Arithmetic gate is completely disabled
*
* 2. q_arith == 1: Everything in the minigate on the right is disabled. The equation is just a standard plonk equation
* with extra wires: q_m * w_1 * w_2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c = 0
*
* 3. q_arith == 2: The (w_1 + w_4 - ...) term is disabled. THe equation is:
* (1/2) * q_m * w_1 * w_2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c + w_4_omega = 0
* It allows defining w_4 at next index (w_4_omega) in terms of current wire values
*
* 4. q_arith == 3: The product of w_1 and w_2 is disabled, but a mini addition gate is enabled. α allows us to split
* the equation into two:
*
* q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c + 2 * w_4_omega = 0
* and
* w_1 + w_4 - w_1_omega + q_m = 0 (we are reusing q_m here)
*
* 5. q_arith > 3: The product of w_1 and w_2 is scaled by (q_arith - 3), while the w_4_omega term is scaled by (q_arith - 1).
* The equation can be split into two:
*
* (q_arith - 3)* q_m * w_1 * w_ 2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c + (q_arith - 1) * w_4_omega = 0
* and
* w_1 + w_4 - w_1_omega + q_m = 0
*
* The problem that q_m is used both in both equations can be dealt with by appropriately changing selector values at
* the next gate. Then we can treat (q_arith - 1) as a simulated q_6 selector and scale q_m to handle (q_arith - 3) at
* product.
*/
let
w1q1
:=
mulmod
(
mload
(
W1_EVAL_LOC
),
mload
(
Q1_EVAL_LOC
),
p
)
let
w2q2
:=
mulmod
(
mload
(
W2_EVAL_LOC
),
mload
(
Q2_EVAL_LOC
),
p
)
let
w3q3
:=
mulmod
(
mload
(
W3_EVAL_LOC
),
mload
(
Q3_EVAL_LOC
),
p
)
let
w4q3
:=
mulmod
(
mload
(
W4_EVAL_LOC
),
mload
(
Q4_EVAL_LOC
),
p
)
// @todo - Add a explicit test that hits QARITH == 3
// w1w2qm := (w_1 . w_2 . q_m . (QARITH_EVAL_LOC - 3)) / 2
let
w1w2qm
:=
mulmod
(
mulmod
(
mulmod
(
mulmod
(
mload
(
W1_EVAL_LOC
),
mload
(
W2_EVAL_LOC
),
p
),
mload
(
QM_EVAL_LOC
),
p
),
addmod
(
mload
(
QARITH_EVAL_LOC
),
sub
(
p
,
3
),
p
),
p
),
NEGATIVE_INVERSE_OF_2_MODULO_P
,
p
)
// (w_1 . w_2 . q_m . (q_arith - 3)) / -2) + (w_1 . q_1) + (w_2 . q_2) + (w_3 . q_3) + (w_4 . q_4) + q_c
let
identity
:=
addmod
(
mload
(
QC_EVAL_LOC
),
addmod
(
w4q3
,
addmod
(
w3q3
,
addmod
(
w2q2
,
addmod
(
w1q1
,
w1w2qm
,
p
),
p
),
p
),
p
),
p
)
// if q_arith == 3 we evaluate an additional mini addition gate (on top of the regular one), where:
// w_1 + w_4 - w_1_omega + q_m = 0
// we use this gate to save an addition gate when adding or subtracting non-native field elements
// α * (q_arith - 2) * (w_1 + w_4 - w_1_omega + q_m)
let
extra_small_addition_gate_identity
:=
mulmod
(
mload
(
C_ALPHA_LOC
),
mulmod
(
addmod
(
mload
(
QARITH_EVAL_LOC
),
sub
(
p
,
2
),
p
),
addmod
(
mload
(
QM_EVAL_LOC
),
addmod
(
sub
(
p
,
mload
(
W1_OMEGA_EVAL_LOC
)),
addmod
(
mload
(
W1_EVAL_LOC
),
mload
(
W4_EVAL_LOC
),
p
),
p
),
p
),
p
),
p
)
// if q_arith == 2 OR q_arith == 3 we add the 4th wire of the NEXT gate into the arithmetic identity
// N.B. if q_arith > 2, this wire value will be scaled by (q_arith - 1) relative to the other gate wires!
// alpha_base * q_arith * (identity + (q_arith - 1) * (w_4_omega + extra_small_addition_gate_identity))
mstore
(
ARITHMETIC_IDENTITY
,
mulmod
(
mload
(
C_ALPHA_BASE_LOC
),
mulmod
(
mload
(
QARITH_EVAL_LOC
),
addmod
(
identity
,
mulmod
(
addmod
(
mload
(
QARITH_EVAL_LOC
),
sub
(
p
,
1
),
p
),
addmod
(
mload
(
W4_OMEGA_EVAL_LOC
),
extra_small_addition_gate_identity
,
p
),
p
),
p
),
p
),
p
)
)
// update alpha
mstore
(
C_ALPHA_BASE_LOC
,
mulmod
(
mload
(
C_ALPHA_BASE_LOC
),
mload
(
C_ALPHA_SQR_LOC
),
p
))
}
/**
* COMPUTE GENPERMSORT WIDGET EVALUATION
*/
{
/**
* D1 = (w2 - w1)
* D2 = (w3 - w2)
* D3 = (w4 - w3)
* D4 = (w1_omega - w4)
*
* α_a = alpha_base
* α_b = alpha_base * α
* α_c = alpha_base * α^2
* α_d = alpha_base * α^3
*
* range_accumulator = (
* D1(D1 - 1)(D1 - 2)(D1 - 3).α_a +
* D2(D2 - 1)(D2 - 2)(D2 - 3).α_b +
* D3(D3 - 1)(D3 - 2)(D3 - 3).α_c +
* D4(D4 - 1)(D4 - 2)(D4 - 3).α_d +
* ) . q_sort
*/
let
minus_two
:=
sub
(
p
,
2
)
let
minus_three
:=
sub
(
p
,
3
)
let
d1
:=
addmod
(
mload
(
W2_EVAL_LOC
),
sub
(
p
,
mload
(
W1_EVAL_LOC
)),
p
)
let
d2
:=
addmod
(
mload
(
W3_EVAL_LOC
),
sub
(
p
,
mload
(
W2_EVAL_LOC
)),
p
)
let
d3
:=
addmod
(
mload
(
W4_EVAL_LOC
),
sub
(
p
,
mload
(
W3_EVAL_LOC
)),
p
)
let
d4
:=
addmod
(
mload
(
W1_OMEGA_EVAL_LOC
),
sub
(
p
,
mload
(
W4_EVAL_LOC
)),
p
)
let
range_accumulator
:=
mulmod
(
mulmod
(
mulmod
(
addmod
(
mulmod
(
d1
,
d1
,
p
),
sub
(
p
,
d1
),
p
),
addmod
(
d1
,
minus_two
,
p
),
p
),
addmod
(
d1
,
minus_three
,
p
),
p
),
mload
(
C_ALPHA_BASE_LOC
),
p
)
range_accumulator
:=
addmod
(
range_accumulator
,
mulmod
(
mulmod
(
mulmod
(
addmod
(
mulmod
(
d2
,
d2
,
p
),
sub
(
p
,
d2
),
p
),
addmod
(
d2
,
minus_two
,
p
),
p
),
addmod
(
d2
,
minus_three
,
p
),
p
),
mulmod
(
mload
(
C_ALPHA_BASE_LOC
),
mload
(
C_ALPHA_LOC
),
p
),
p
),
p
)
range_accumulator
:=
addmod
(
range_accumulator
,
mulmod
(
mulmod
(
mulmod
(
addmod
(
mulmod
(
d3
,
d3
,
p
),
sub
(
p
,
d3
),
p
),
addmod
(
d3
,
minus_two
,
p
),
p
),
addmod
(
d3
,
minus_three
,
p
),
p
),
mulmod
(
mload
(
C_ALPHA_BASE_LOC
),
mload
(
C_ALPHA_SQR_LOC
),
p
),
p
),
p
)
range_accumulator
:=
addmod
(
range_accumulator
,
mulmod
(
mulmod
(
mulmod
(
addmod
(
mulmod
(
d4
,
d4
,
p
),
sub
(
p
,
d4
),
p
),
addmod
(
d4
,
minus_two
,
p
),
p
),
addmod
(
d4
,
minus_three
,
p
),
p
),
mulmod
(
mload
(
C_ALPHA_BASE_LOC
),
mload
(
C_ALPHA_CUBE_LOC
),
p
),
p
),
p
)
range_accumulator
:=
mulmod
(
range_accumulator
,
mload
(
QSORT_EVAL_LOC
),
p
)
mstore
(
SORT_IDENTITY
,
range_accumulator
)
// update alpha
mstore
(
C_ALPHA_BASE_LOC
,
mulmod
(
mload
(
C_ALPHA_BASE_LOC
),
mload
(
C_ALPHA_QUAD_LOC
),
p
))
}
/**
* COMPUTE ELLIPTIC WIDGET EVALUATION
*/
{
/**
* endo_term = (-x_2) * x_1 * (x_3 * 2 + x_1) * q_beta
* endo_sqr_term = x_2^2
* endo_sqr_term *= (x_3 - x_1)
* endo_sqr_term *= q_beta^2
* leftovers = x_2^2
* leftovers *= x_2
* leftovers += x_1^2 * (x_3 + x_1) @follow-up Invalid comment in BB widget
* leftovers -= (y_2^2 + y_1^2)
* sign_term = y_2 * y_1
* sign_term += sign_term
* sign_term *= q_sign
*/
// q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0
let
x_diff
:=
addmod
(
mload
(
X2_EVAL_LOC
),
sub
(
p
,
mload
(
X1_EVAL_LOC
)),
p
)
let
y2_sqr
:=
mulmod
(
mload
(
Y2_EVAL_LOC
),
mload
(
Y2_EVAL_LOC
),
p
)
let
y1_sqr
:=
mulmod
(
mload
(
Y1_EVAL_LOC
),
mload
(
Y1_EVAL_LOC
),
p
)
let
y1y2
:=
mulmod
(
mulmod
(
mload
(
Y1_EVAL_LOC
),
mload
(
Y2_EVAL_LOC
),
p
),
mload
(
QSIGN_LOC
),
p
)
let
x_add_identity
:=
addmod
(
mulmod
(
addmod
(
mload
(
X3_EVAL_LOC
),
addmod
(
mload
(
X2_EVAL_LOC
),
mload
(
X1_EVAL_LOC
),
p
),
p
),
mulmod
(
x_diff
,
x_diff
,
p
),
p
),
addmod
(
sub
(
p
,
addmod
(
y2_sqr
,
y1_sqr
,
p
)
),
addmod
(
y1y2
,
y1y2
,
p
),
p
),
p
)
x_add_identity
:=
mulmod
(
mulmod
(
x_add_identity
,
addmod
(
1
,
sub
(
p
,
mload
(
QM_EVAL_LOC
)),
p
),
p
),
mload
(
C_ALPHA_BASE_LOC
),
p
)
// q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0
let
y1_plus_y3
:=
addmod
(
mload
(
Y1_EVAL_LOC
),
mload
(
Y3_EVAL_LOC
),
p
)
let
y_diff
:=
addmod
(
mulmod
(
mload
(
Y2_EVAL_LOC
),
mload
(
QSIGN_LOC
),
p
),
sub
(
p
,
mload
(
Y1_EVAL_LOC
)),
p
)
let
y_add_identity
:=
addmod
(
mulmod
(
y1_plus_y3
,
x_diff
,
p
),
mulmod
(
addmod
(
mload
(
X3_EVAL_LOC
),
sub
(
p
,
mload
(
X1_EVAL_LOC
)),
p
),
y_diff
,
p
),
p
)
y_add_identity
:=
mulmod
(
mulmod
(
y_add_identity
,
addmod
(
1
,
sub
(
p
,
mload
(
QM_EVAL_LOC
)),
p
),
p
),
mulmod
(
mload
(
C_ALPHA_BASE_LOC
),
mload
(
C_ALPHA_LOC
),
p
),
p
)
// ELLIPTIC_IDENTITY = (x_identity + y_identity) * Q_ELLIPTIC_EVAL
mstore
(
ELLIPTIC_IDENTITY
,
mulmod
(
addmod
(
x_add_identity
,
y_add_identity
,
p
),
mload
(
QELLIPTIC_EVAL_LOC
),
p
)
)
}
{
/**
* x_pow_4 = (y_1_sqr - curve_b) * x_1;
* y_1_sqr_mul_4 = y_1_sqr + y_1_sqr;
* y_1_sqr_mul_4 += y_1_sqr_mul_4;
* x_1_pow_4_mul_9 = x_pow_4;
* x_1_pow_4_mul_9 += x_1_pow_4_mul_9;
* x_1_pow_4_mul_9 += x_1_pow_4_mul_9;
* x_1_pow_4_mul_9 += x_1_pow_4_mul_9;
* x_1_pow_4_mul_9 += x_pow_4;
* x_1_sqr_mul_3 = x_1_sqr + x_1_sqr + x_1_sqr;
* x_double_identity = (x_3 + x_1 + x_1) * y_1_sqr_mul_4 - x_1_pow_4_mul_9;
* y_double_identity = x_1_sqr_mul_3 * (x_1 - x_3) - (y_1 + y_1) * (y_1 + y_3);
*/
// (x3 + x1 + x1) (4y1*y1) - 9 * x1 * x1 * x1 * x1 = 0
let
x1_sqr
:=
mulmod
(
mload
(
X1_EVAL_LOC
),
mload
(
X1_EVAL_LOC
),
p
)
let
y1_sqr
:=
mulmod
(
mload
(
Y1_EVAL_LOC
),
mload
(
Y1_EVAL_LOC
),
p
)
let
x_pow_4
:=
mulmod
(
addmod
(
y1_sqr
,
GRUMPKIN_CURVE_B_PARAMETER_NEGATED
,
p
),
mload
(
X1_EVAL_LOC
),
p
)
let
y1_sqr_mul_4
:=
mulmod
(
y1_sqr
,
4
,
p
)
let
x1_pow_4_mul_9
:=
mulmod
(
x_pow_4
,
9
,
p
)
let
x1_sqr_mul_3
:=
mulmod
(
x1_sqr
,
3
,
p
)
let
x_double_identity
:=
addmod
(
mulmod
(
addmod
(
mload
(
X3_EVAL_LOC
),
addmod
(
mload
(
X1_EVAL_LOC
),
mload
(
X1_EVAL_LOC
),
p
),
p
),
y1_sqr_mul_4
,
p
),
sub
(
p
,
x1_pow_4_mul_9
),
p
)
// (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0
let
y_double_identity
:=
addmod
(
mulmod
(
x1_sqr_mul_3
,
addmod
(
mload
(
X1_EVAL_LOC
),
sub
(
p
,
mload
(
X3_EVAL_LOC
)),
p
),
p
),
sub
(
p
,
mulmod
(
addmod
(
mload
(
Y1_EVAL_LOC
),
mload
(
Y1_EVAL_LOC
),
p
),
addmod
(
mload
(
Y1_EVAL_LOC
),
mload
(
Y3_EVAL_LOC
),
p
),
p
)
),
p
)
x_double_identity
:=
mulmod
(
x_double_identity
,
mload
(
C_ALPHA_BASE_LOC
),
p
)
y_double_identity
:=
mulmod
(
y_double_identity
,
mulmod
(
mload
(
C_ALPHA_BASE_LOC
),
mload
(
C_ALPHA_LOC
),
p
),
p
)
x_double_identity
:=
mulmod
(
x_double_identity
,
mload
(
QM_EVAL_LOC
),
p
)
y_double_identity
:=
mulmod
(
y_double_identity
,
mload
(
QM_EVAL_LOC
),
p
)
// ELLIPTIC_IDENTITY += (x_double_identity + y_double_identity) * Q_DOUBLE_EVAL
mstore
(
ELLIPTIC_IDENTITY
,
addmod
(
mload
(
ELLIPTIC_IDENTITY
),
mulmod
(
addmod
(
x_double_identity
,
y_double_identity
,
p
),
mload
(
QELLIPTIC_EVAL_LOC
),
p
),
p
)
)
// update alpha
mstore
(
C_ALPHA_BASE_LOC
,
mulmod
(
mload
(
C_ALPHA_BASE_LOC
),
mload
(
C_ALPHA_QUAD_LOC
),
p
))
}
/**
* COMPUTE AUXILIARY WIDGET EVALUATION
*/
{
{
/**
* Non native field arithmetic gate 2
* _ _
* / _ _ _ 14 \
* q_2 . q_4 | (w_1 . w_2) + (w_1 . w_2) + (w_1 . w_4 + w_2 . w_3 - w_3) . 2 - w_3 - w_4 |
* \_ _/
*
* limb_subproduct = w_1 . w_2_omega + w_1_omega . w_2
* non_native_field_gate_2 = w_1 * w_4 + w_4 * w_3 - w_3_omega
* non_native_field_gate_2 = non_native_field_gate_2 * limb_size
* non_native_field_gate_2 -= w_4_omega
* non_native_field_gate_2 += limb_subproduct
* non_native_field_gate_2 *= q_4
* limb_subproduct *= limb_size
* limb_subproduct += w_1_omega * w_2_omega
* non_native_field_gate_1 = (limb_subproduct + w_3 + w_4) * q_3
* non_native_field_gate_3 = (limb_subproduct + w_4 - (w_3_omega + w_4_omega)) * q_m
* non_native_field_identity = (non_native_field_gate_1 + non_native_field_gate_2 + non_native_field_gate_3) * q_2
*/
let
limb_subproduct
:=
addmod
(
mulmod
(
mload
(
W1_EVAL_LOC
),
mload
(
W2_OMEGA_EVAL_LOC
),
p
),
mulmod
(
mload
(
W1_OMEGA_EVAL_LOC
),
mload
(
W2_EVAL_LOC
),
p
),
p
)
let
non_native_field_gate_2
:=
addmod
(
addmod
(
mulmod
(
mload
(
W1_EVAL_LOC
),
mload
(
W4_EVAL_LOC
),
p
),
mulmod
(
mload
(
W2_EVAL_LOC
),
mload
(
W3_EVAL_LOC
),
p
),
p
),
sub
(
p
,
mload
(
W3_OMEGA_EVAL_LOC
)),
p
)
non_native_field_gate_2
:=
mulmod
(
non_native_field_gate_2
,
LIMB_SIZE
,
p
)
non_native_field_gate_2
:=
addmod
(
non_native_field_gate_2
,
sub
(
p
,
mload
(
W4_OMEGA_EVAL_LOC
)),
p
)
non_native_field_gate_2
:=
addmod
(
non_native_field_gate_2
,
limb_subproduct
,
p
)
non_native_field_gate_2
:=
mulmod
(
non_native_field_gate_2
,
mload
(
Q4_EVAL_LOC
),
p
)
limb_subproduct
:=
mulmod
(
limb_subproduct
,
LIMB_SIZE
,
p
)
limb_subproduct
:=
addmod
(
limb_subproduct
,
mulmod
(
mload
(
W1_OMEGA_EVAL_LOC
),
mload
(
W2_OMEGA_EVAL_LOC
),
p
),
p
)
let
non_native_field_gate_1
:=
mulmod
(
addmod
(
limb_subproduct
,
sub
(
p
,
addmod
(
mload
(
W3_EVAL_LOC
),
mload
(
W4_EVAL_LOC
),
p
)),
p
),
mload
(
Q3_EVAL_LOC
),
p
)
let
non_native_field_gate_3
:=
mulmod
(
addmod
(
addmod
(
limb_subproduct
,
mload
(
W4_EVAL_LOC
),
p
),
sub
(
p
,
addmod
(
mload
(
W3_OMEGA_EVAL_LOC
),
mload
(
W4_OMEGA_EVAL_LOC
),
p
)),
p
),
mload
(
QM_EVAL_LOC
),
p
)
let
non_native_field_identity
:=
mulmod
(
addmod
(
addmod
(
non_native_field_gate_1
,
non_native_field_gate_2
,
p
),
non_native_field_gate_3
,
p
),
mload
(
Q2_EVAL_LOC
),
p
)
mstore
(
AUX_NON_NATIVE_FIELD_EVALUATION
,
non_native_field_identity
)
}
{
/**
* limb_accumulator_1 = w_2_omega;
* limb_accumulator_1 *= SUBLIMB_SHIFT;
* limb_accumulator_1 += w_1_omega;
* limb_accumulator_1 *= SUBLIMB_SHIFT;
* limb_accumulator_1 += w_3;
* limb_accumulator_1 *= SUBLIMB_SHIFT;
* limb_accumulator_1 += w_2;
* limb_accumulator_1 *= SUBLIMB_SHIFT;
* limb_accumulator_1 += w_1;
* limb_accumulator_1 -= w_4;
* limb_accumulator_1 *= q_4;
*/
let
limb_accumulator_1
:=
mulmod
(
mload
(
W2_OMEGA_EVAL_LOC
),
SUBLIMB_SHIFT
,
p
)
limb_accumulator_1
:=
addmod
(
limb_accumulator_1
,
mload
(
W1_OMEGA_EVAL_LOC
),
p
)
limb_accumulator_1
:=
mulmod
(
limb_accumulator_1
,
SUBLIMB_SHIFT
,
p
)
limb_accumulator_1
:=
addmod
(
limb_accumulator_1
,
mload
(
W3_EVAL_LOC
),
p
)
limb_accumulator_1
:=
mulmod
(
limb_accumulator_1
,
SUBLIMB_SHIFT
,
p
)
limb_accumulator_1
:=
addmod
(
limb_accumulator_1
,
mload
(
W2_EVAL_LOC
),
p
)
limb_accumulator_1
:=
mulmod
(
limb_accumulator_1
,
SUBLIMB_SHIFT
,
p
)
limb_accumulator_1
:=
addmod
(
limb_accumulator_1
,
mload
(
W1_EVAL_LOC
),
p
)
limb_accumulator_1
:=
addmod
(
limb_accumulator_1
,
sub
(
p
,
mload
(
W4_EVAL_LOC
)),
p
)
limb_accumulator_1
:=
mulmod
(
limb_accumulator_1
,
mload
(
Q4_EVAL_LOC
),
p
)
/**
* limb_accumulator_2 = w_3_omega;
* limb_accumulator_2 *= SUBLIMB_SHIFT;
* limb_accumulator_2 += w_2_omega;
* limb_accumulator_2 *= SUBLIMB_SHIFT;
* limb_accumulator_2 += w_1_omega;
* limb_accumulator_2 *= SUBLIMB_SHIFT;
* limb_accumulator_2 += w_4;
* limb_accumulator_2 *= SUBLIMB_SHIFT;
* limb_accumulator_2 += w_3;
* limb_accumulator_2 -= w_4_omega;
* limb_accumulator_2 *= q_m;
*/
let
limb_accumulator_2
:=
mulmod
(
mload
(
W3_OMEGA_EVAL_LOC
),
SUBLIMB_SHIFT
,
p
)
limb_accumulator_2
:=
addmod
(
limb_accumulator_2
,
mload
(
W2_OMEGA_EVAL_LOC
),
p
)
limb_accumulator_2
:=
mulmod
(
limb_accumulator_2
,
SUBLIMB_SHIFT
,
p
)
limb_accumulator_2
:=
addmod
(
limb_accumulator_2
,
mload
(
W1_OMEGA_EVAL_LOC
),
p
)
limb_accumulator_2
:=
mulmod
(
limb_accumulator_2
,
SUBLIMB_SHIFT
,
p
)
limb_accumulator_2
:=
addmod
(
limb_accumulator_2
,
mload
(
W4_EVAL_LOC
),
p
)
limb_accumulator_2
:=
mulmod
(
limb_accumulator_2
,
SUBLIMB_SHIFT
,
p
)
limb_accumulator_2
:=
addmod
(
limb_accumulator_2
,
mload
(
W3_EVAL_LOC
),
p
)
limb_accumulator_2
:=
addmod
(
limb_accumulator_2
,
sub
(
p
,
mload
(
W4_OMEGA_EVAL_LOC
)),
p
)
limb_accumulator_2
:=
mulmod
(
limb_accumulator_2
,
mload
(
QM_EVAL_LOC
),
p
)
mstore
(
AUX_LIMB_ACCUMULATOR_EVALUATION
,
mulmod
(
addmod
(
limb_accumulator_1
,
limb_accumulator_2
,
p
),
mload
(
Q3_EVAL_LOC
),
p
)
)
}
{
/**
* memory_record_check = w_3;
* memory_record_check *= eta;
* memory_record_check += w_2;
* memory_record_check *= eta;
* memory_record_check += w_1;
* memory_record_check *= eta;
* memory_record_check += q_c;
*
* partial_record_check = memory_record_check;
*
* memory_record_check -= w_4;
*/
let
memory_record_check
:=
mulmod
(
mload
(
W3_EVAL_LOC
),
mload
(
C_ETA_LOC
),
p
)
memory_record_check
:=
addmod
(
memory_record_check
,
mload
(
W2_EVAL_LOC
),
p
)
memory_record_check
:=
mulmod
(
memory_record_check
,
mload
(
C_ETA_LOC
),
p
)
memory_record_check
:=
addmod
(
memory_record_check
,
mload
(
W1_EVAL_LOC
),
p
)
memory_record_check
:=
mulmod
(
memory_record_check
,
mload
(
C_ETA_LOC
),
p
)
memory_record_check
:=
addmod
(
memory_record_check
,
mload
(
QC_EVAL_LOC
),
p
)
let
partial_record_check
:=
memory_record_check
memory_record_check
:=
addmod
(
memory_record_check
,
sub
(
p
,
mload
(
W4_EVAL_LOC
)),
p
)
mstore
(
AUX_MEMORY_EVALUATION
,
memory_record_check
)
// index_delta = w_1_omega - w_1
let
index_delta
:=
addmod
(
mload
(
W1_OMEGA_EVAL_LOC
),
sub
(
p
,
mload
(
W1_EVAL_LOC
)),
p
)
// record_delta = w_4_omega - w_4
let
record_delta
:=
addmod
(
mload
(
W4_OMEGA_EVAL_LOC
),
sub
(
p
,
mload
(
W4_EVAL_LOC
)),
p
)
// index_is_monotonically_increasing = index_delta * (index_delta - 1)
let
index_is_monotonically_increasing
:=
mulmod
(
index_delta
,
addmod
(
index_delta
,
sub
(
p
,
1
),
p
),
p
)
// adjacent_values_match_if_adjacent_indices_match = record_delta * (1 - index_delta)
let
adjacent_values_match_if_adjacent_indices_match
:=
mulmod
(
record_delta
,
addmod
(
1
,
sub
(
p
,
index_delta
),
p
),
p
)
// AUX_ROM_CONSISTENCY_EVALUATION = ((adjacent_values_match_if_adjacent_indices_match * alpha) + index_is_monotonically_increasing) * alpha + partial_record_check
mstore
(
AUX_ROM_CONSISTENCY_EVALUATION
,
addmod
(
mulmod
(
addmod
(
mulmod
(
adjacent_values_match_if_adjacent_indices_match
,
mload
(
C_ALPHA_LOC
),
p
),
index_is_monotonically_increasing
,
p
),
mload
(
C_ALPHA_LOC
),
p
),
memory_record_check
,
p
)
)
{
/**
* next_gate_access_type = w_3_omega;
* next_gate_access_type *= eta;
* next_gate_access_type += w_2_omega;
* next_gate_access_type *= eta;
* next_gate_access_type += w_1_omega;
* next_gate_access_type *= eta;
* next_gate_access_type = w_4_omega - next_gate_access_type;
*/
let
next_gate_access_type
:=
mulmod
(
mload
(
W3_OMEGA_EVAL_LOC
),
mload
(
C_ETA_LOC
),
p
)
next_gate_access_type
:=
addmod
(
next_gate_access_type
,
mload
(
W2_OMEGA_EVAL_LOC
),
p
)
next_gate_access_type
:=
mulmod
(
next_gate_access_type
,
mload
(
C_ETA_LOC
),
p
)
next_gate_access_type
:=
addmod
(
next_gate_access_type
,
mload
(
W1_OMEGA_EVAL_LOC
),
p
)
next_gate_access_type
:=
mulmod
(
next_gate_access_type
,
mload
(
C_ETA_LOC
),
p
)
next_gate_access_type
:=
addmod
(
mload
(
W4_OMEGA_EVAL_LOC
),
sub
(
p
,
next_gate_access_type
),
p
)
// value_delta = w_3_omega - w_3
let
value_delta
:=
addmod
(
mload
(
W3_OMEGA_EVAL_LOC
),
sub
(
p
,
mload
(
W3_EVAL_LOC
)),
p
)
// adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = (1 - index_delta) * value_delta * (1 - next_gate_access_type);
let
adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation
:=
mulmod
(
addmod
(
1
,
sub
(
p
,
index_delta
),
p
),
mulmod
(
value_delta
,
addmod
(
1
,
sub
(
p
,
next_gate_access_type
),
p
),
p
),
p
)
// AUX_RAM_CONSISTENCY_EVALUATION
/**
* access_type = w_4 - partial_record_check
* access_check = access_type^2 - access_type
* next_gate_access_type_is_boolean = next_gate_access_type^2 - next_gate_access_type
* RAM_consistency_check_identity = adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation;
* RAM_consistency_check_identity *= alpha;
* RAM_consistency_check_identity += index_is_monotonically_increasing;
* RAM_consistency_check_identity *= alpha;
* RAM_consistency_check_identity += next_gate_access_type_is_boolean;
* RAM_consistency_check_identity *= alpha;
* RAM_consistency_check_identity += access_check;
*/
let
access_type
:=
addmod
(
mload
(
W4_EVAL_LOC
),
sub
(
p
,
partial_record_check
),
p
)
let
access_check
:=
mulmod
(
access_type
,
addmod
(
access_type
,
sub
(
p
,
1
),
p
),
p
)
let
next_gate_access_type_is_boolean
:=
mulmod
(
next_gate_access_type
,
addmod
(
next_gate_access_type
,
sub
(
p
,
1
),
p
),
p
)
let
RAM_cci
:=
mulmod
(
adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation
,
mload
(
C_ALPHA_LOC
),
p
)
RAM_cci
:=
addmod
(
RAM_cci
,
index_is_monotonically_increasing
,
p
)
RAM_cci
:=
mulmod
(
RAM_cci
,
mload
(
C_ALPHA_LOC
),
p
)
RAM_cci
:=
addmod
(
RAM_cci
,
next_gate_access_type_is_boolean
,
p
)
RAM_cci
:=
mulmod
(
RAM_cci
,
mload
(
C_ALPHA_LOC
),
p
)
RAM_cci
:=
addmod
(
RAM_cci
,
access_check
,
p
)
mstore
(
AUX_RAM_CONSISTENCY_EVALUATION
,
RAM_cci
)
}
{
// timestamp_delta = w_2_omega - w_2
let
timestamp_delta
:=
addmod
(
mload
(
W2_OMEGA_EVAL_LOC
),
sub
(
p
,
mload
(
W2_EVAL_LOC
)),
p
)
// RAM_timestamp_check_identity = (1 - index_delta) * timestamp_delta - w_3
let
RAM_timestamp_check_identity
:=
addmod
(
mulmod
(
timestamp_delta
,
addmod
(
1
,
sub
(
p
,
index_delta
),
p
),
p
),
sub
(
p
,
mload
(
W3_EVAL_LOC
)),
p
)
/**
* memory_identity = ROM_consistency_check_identity * q_2;
* memory_identity += RAM_timestamp_check_identity * q_4;
* memory_identity += memory_record_check * q_m;
* memory_identity *= q_1;
* memory_identity += (RAM_consistency_check_identity * q_arith);
*
* auxiliary_identity = memory_identity + non_native_field_identity + limb_accumulator_identity;
* auxiliary_identity *= q_aux;
* auxiliary_identity *= alpha_base;
*/
let
memory_identity
:=
mulmod
(
mload
(
AUX_ROM_CONSISTENCY_EVALUATION
),
mload
(
Q2_EVAL_LOC
),
p
)
memory_identity
:=
addmod
(
memory_identity
,
mulmod
(
RAM_timestamp_check_identity
,
mload
(
Q4_EVAL_LOC
),
p
),
p
)
memory_identity
:=
addmod
(
memory_identity
,
mulmod
(
mload
(
AUX_MEMORY_EVALUATION
),
mload
(
QM_EVAL_LOC
),
p
),
p
)
memory_identity
:=
mulmod
(
memory_identity
,
mload
(
Q1_EVAL_LOC
),
p
)
memory_identity
:=
addmod
(
memory_identity
,
mulmod
(
mload
(
AUX_RAM_CONSISTENCY_EVALUATION
),
mload
(
QARITH_EVAL_LOC
),
p
),
p
)
let
auxiliary_identity
:=
addmod
(
memory_identity
,
mload
(
AUX_NON_NATIVE_FIELD_EVALUATION
),
p
)
auxiliary_identity
:=
addmod
(
auxiliary_identity
,
mload
(
AUX_LIMB_ACCUMULATOR_EVALUATION
),
p
)
auxiliary_identity
:=
mulmod
(
auxiliary_identity
,
mload
(
QAUX_EVAL_LOC
),
p
)
auxiliary_identity
:=
mulmod
(
auxiliary_identity
,
mload
(
C_ALPHA_BASE_LOC
),
p
)
mstore
(
AUX_IDENTITY
,
auxiliary_identity
)
// update alpha
mstore
(
C_ALPHA_BASE_LOC
,
mulmod
(
mload
(
C_ALPHA_BASE_LOC
),
mload
(
C_ALPHA_CUBE_LOC
),
p
))
}
}
}
{
/**
* quotient = ARITHMETIC_IDENTITY
* quotient += PERMUTATION_IDENTITY
* quotient += PLOOKUP_IDENTITY
* quotient += SORT_IDENTITY
* quotient += ELLIPTIC_IDENTITY
* quotient += AUX_IDENTITY
* quotient *= ZERO_POLY_INVERSE
*/
mstore
(
QUOTIENT_EVAL_LOC
,
mulmod
(
addmod
(
addmod
(
addmod
(
addmod
(
addmod
(
mload
(
PERMUTATION_IDENTITY
),
mload
(
PLOOKUP_IDENTITY
),
p
),
mload
(
ARITHMETIC_IDENTITY
),
p
),
mload
(
SORT_IDENTITY
),
p
),
mload
(
ELLIPTIC_IDENTITY
),
p
),
mload
(
AUX_IDENTITY
),
p
),
mload
(
ZERO_POLY_INVERSE_LOC
),
p
)
)
}
/**
* GENERATE NU AND SEPARATOR CHALLENGES
*/
{
let
current_challenge
:=
mload
(
C_CURRENT_LOC
)
// get a calldata pointer that points to the start of the data we want to copy
let
calldata_ptr
:=
add
(
calldataload
(
0x04
),
0x24
)
calldata_ptr
:=
add
(
calldata_ptr
,
NU_CALLDATA_SKIP_LENGTH
)
mstore
(
NU_CHALLENGE_INPUT_LOC_A
,
current_challenge
)
mstore
(
NU_CHALLENGE_INPUT_LOC_B
,
mload
(
QUOTIENT_EVAL_LOC
))
calldatacopy
(
NU_CHALLENGE_INPUT_LOC_C
,
calldata_ptr
,
NU_INPUT_LENGTH
)
// hash length = (0x20 + num field elements), we include the previous challenge in the hash
let
challenge
:=
keccak256
(
NU_CHALLENGE_INPUT_LOC_A
,
add
(
NU_INPUT_LENGTH
,
0x40
))
mstore
(
C_V0_LOC
,
mod
(
challenge
,
p
))
// We need THIRTY-ONE independent nu challenges!
mstore
(
0x00
,
challenge
)
mstore8
(
0x20
,
0x01
)
mstore
(
C_V1_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x02
)
mstore
(
C_V2_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x03
)
mstore
(
C_V3_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x04
)
mstore
(
C_V4_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x05
)
mstore
(
C_V5_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x06
)
mstore
(
C_V6_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x07
)
mstore
(
C_V7_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x08
)
mstore
(
C_V8_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x09
)
mstore
(
C_V9_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x0a
)
mstore
(
C_V10_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x0b
)
mstore
(
C_V11_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x0c
)
mstore
(
C_V12_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x0d
)
mstore
(
C_V13_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x0e
)
mstore
(
C_V14_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x0f
)
mstore
(
C_V15_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x10
)
mstore
(
C_V16_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x11
)
mstore
(
C_V17_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x12
)
mstore
(
C_V18_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x13
)
mstore
(
C_V19_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x14
)
mstore
(
C_V20_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x15
)
mstore
(
C_V21_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x16
)
mstore
(
C_V22_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x17
)
mstore
(
C_V23_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x18
)
mstore
(
C_V24_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x19
)
mstore
(
C_V25_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x1a
)
mstore
(
C_V26_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x1b
)
mstore
(
C_V27_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x1c
)
mstore
(
C_V28_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
mstore8
(
0x20
,
0x1d
)
mstore
(
C_V29_LOC
,
mod
(
keccak256
(
0x00
,
0x21
),
p
))
// @follow-up - Why are both v29 and v30 using appending 0x1d to the prior challenge and hashing, should it not change?
mstore8
(
0x20
,
0x1d
)
challenge
:=
keccak256
(
0x00
,
0x21
)
mstore
(
C_V30_LOC
,
mod
(
challenge
,
p
))
// separator
mstore
(
0x00
,
challenge
)
mstore
(
0x20
,
mload
(
PI_Z_Y_LOC
))
mstore
(
0x40
,
mload
(
PI_Z_X_LOC
))
mstore
(
0x60
,
mload
(
PI_Z_OMEGA_Y_LOC
))
mstore
(
0x80
,
mload
(
PI_Z_OMEGA_X_LOC
))
mstore
(
C_U_LOC
,
mod
(
keccak256
(
0x00
,
0xa0
),
p
))
}
let
success
:=
0
// VALIDATE T1
{
let
x
:=
mload
(
T1_X_LOC
)
let
y
:=
mload
(
T1_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
if
iszero
(
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
{
mstore
(
0x0
,
POINT_NOT_ON_CURVE_SELECTOR
)
revert
(
0x00
,
0x04
)
}
mstore
(
ACCUMULATOR_X_LOC
,
x
)
mstore
(
add
(
ACCUMULATOR_X_LOC
,
0x20
),
y
)
}
// VALIDATE T2
{
let
x
:=
mload
(
T2_X_LOC
)
// 0x1400
let
y
:=
mload
(
T2_Y_LOC
)
// 0x1420
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
if
iszero
(
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
{
mstore
(
0x0
,
POINT_NOT_ON_CURVE_SELECTOR
)
revert
(
0x00
,
0x04
)
}
mstore
(
0x00
,
x
)
mstore
(
0x20
,
y
)
}
mstore
(
0x40
,
mload
(
ZETA_POW_N_LOC
))
// accumulator_2 = [T2].zeta^n
success
:=
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
)
// accumulator = [T1] + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// VALIDATE T3
{
let
x
:=
mload
(
T3_X_LOC
)
let
y
:=
mload
(
T3_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
if
iszero
(
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
{
mstore
(
0x0
,
POINT_NOT_ON_CURVE_SELECTOR
)
revert
(
0x00
,
0x04
)
}
mstore
(
0x00
,
x
)
mstore
(
0x20
,
y
)
}
mstore
(
0x40
,
mulmod
(
mload
(
ZETA_POW_N_LOC
),
mload
(
ZETA_POW_N_LOC
),
p
))
// accumulator_2 = [T3].zeta^{2n}
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// VALIDATE T4
{
let
x
:=
mload
(
T4_X_LOC
)
let
y
:=
mload
(
T4_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
if
iszero
(
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
{
mstore
(
0x0
,
POINT_NOT_ON_CURVE_SELECTOR
)
revert
(
0x00
,
0x04
)
}
mstore
(
0x00
,
x
)
mstore
(
0x20
,
y
)
}
mstore
(
0x40
,
mulmod
(
mulmod
(
mload
(
ZETA_POW_N_LOC
),
mload
(
ZETA_POW_N_LOC
),
p
),
mload
(
ZETA_POW_N_LOC
),
p
))
// accumulator_2 = [T4].zeta^{3n}
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// VALIDATE W1
{
let
x
:=
mload
(
W1_X_LOC
)
let
y
:=
mload
(
W1_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
if
iszero
(
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
{
mstore
(
0x0
,
POINT_NOT_ON_CURVE_SELECTOR
)
revert
(
0x00
,
0x04
)
}
mstore
(
0x00
,
x
)
mstore
(
0x20
,
y
)
}
mstore
(
0x40
,
mulmod
(
addmod
(
mload
(
C_U_LOC
),
0x1
,
p
),
mload
(
C_V0_LOC
),
p
))
// accumulator_2 = v0.(u + 1).[W1]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// VALIDATE W2
{
let
x
:=
mload
(
W2_X_LOC
)
let
y
:=
mload
(
W2_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
if
iszero
(
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
{
mstore
(
0x0
,
POINT_NOT_ON_CURVE_SELECTOR
)
revert
(
0x00
,
0x04
)
}
mstore
(
0x00
,
x
)
mstore
(
0x20
,
y
)
}
mstore
(
0x40
,
mulmod
(
addmod
(
mload
(
C_U_LOC
),
0x1
,
p
),
mload
(
C_V1_LOC
),
p
))
// accumulator_2 = v1.(u + 1).[W2]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// VALIDATE W3
{
let
x
:=
mload
(
W3_X_LOC
)
let
y
:=
mload
(
W3_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
if
iszero
(
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
{
mstore
(
0x0
,
POINT_NOT_ON_CURVE_SELECTOR
)
revert
(
0x00
,
0x04
)
}
mstore
(
0x00
,
x
)
mstore
(
0x20
,
y
)
}
mstore
(
0x40
,
mulmod
(
addmod
(
mload
(
C_U_LOC
),
0x1
,
p
),
mload
(
C_V2_LOC
),
p
))
// accumulator_2 = v2.(u + 1).[W3]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// VALIDATE W4
{
let
x
:=
mload
(
W4_X_LOC
)
let
y
:=
mload
(
W4_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
if
iszero
(
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
{
mstore
(
0x0
,
POINT_NOT_ON_CURVE_SELECTOR
)
revert
(
0x00
,
0x04
)
}
mstore
(
0x00
,
x
)
mstore
(
0x20
,
y
)
}
mstore
(
0x40
,
mulmod
(
addmod
(
mload
(
C_U_LOC
),
0x1
,
p
),
mload
(
C_V3_LOC
),
p
))
// accumulator_2 = v3.(u + 1).[W4]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// VALIDATE S
{
let
x
:=
mload
(
S_X_LOC
)
let
y
:=
mload
(
S_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
if
iszero
(
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
{
mstore
(
0x0
,
POINT_NOT_ON_CURVE_SELECTOR
)
revert
(
0x00
,
0x04
)
}
mstore
(
0x00
,
x
)
mstore
(
0x20
,
y
)
}
mstore
(
0x40
,
mulmod
(
addmod
(
mload
(
C_U_LOC
),
0x1
,
p
),
mload
(
C_V4_LOC
),
p
))
// accumulator_2 = v4.(u + 1).[S]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// VALIDATE Z
{
let
x
:=
mload
(
Z_X_LOC
)
let
y
:=
mload
(
Z_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
if
iszero
(
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
{
mstore
(
0x0
,
POINT_NOT_ON_CURVE_SELECTOR
)
revert
(
0x00
,
0x04
)
}
mstore
(
0x00
,
x
)
mstore
(
0x20
,
y
)
}
mstore
(
0x40
,
mulmod
(
addmod
(
mload
(
C_U_LOC
),
0x1
,
p
),
mload
(
C_V5_LOC
),
p
))
// accumulator_2 = v5.(u + 1).[Z]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// VALIDATE Z_LOOKUP
{
let
x
:=
mload
(
Z_LOOKUP_X_LOC
)
let
y
:=
mload
(
Z_LOOKUP_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
if
iszero
(
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
{
mstore
(
0x0
,
POINT_NOT_ON_CURVE_SELECTOR
)
revert
(
0x00
,
0x04
)
}
mstore
(
0x00
,
x
)
mstore
(
0x20
,
y
)
}
mstore
(
0x40
,
mulmod
(
addmod
(
mload
(
C_U_LOC
),
0x1
,
p
),
mload
(
C_V6_LOC
),
p
))
// accumulator_2 = v6.(u + 1).[Z_LOOKUP]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// ACCUMULATE Q1
// Verification key fields verified to be on curve at contract deployment
mstore
(
0x00
,
mload
(
Q1_X_LOC
))
mstore
(
0x20
,
mload
(
Q1_Y_LOC
))
mstore
(
0x40
,
mload
(
C_V7_LOC
))
// accumulator_2 = v7.[Q1]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// ACCUMULATE Q2
// Verification key fields verified to be on curve at contract deployment
mstore
(
0x00
,
mload
(
Q2_X_LOC
))
mstore
(
0x20
,
mload
(
Q2_Y_LOC
))
mstore
(
0x40
,
mload
(
C_V8_LOC
))
// accumulator_2 = v8.[Q2]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// ACCUMULATE Q3
// Verification key fields verified to be on curve at contract deployment
mstore
(
0x00
,
mload
(
Q3_X_LOC
))
mstore
(
0x20
,
mload
(
Q3_Y_LOC
))
mstore
(
0x40
,
mload
(
C_V9_LOC
))
// accumulator_2 = v9.[Q3]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// ACCUMULATE Q4
// Verification key fields verified to be on curve at contract deployment
mstore
(
0x00
,
mload
(
Q4_X_LOC
))
mstore
(
0x20
,
mload
(
Q4_Y_LOC
))
mstore
(
0x40
,
mload
(
C_V10_LOC
))
// accumulator_2 = v10.[Q4]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// ACCUMULATE QM
// Verification key fields verified to be on curve at contract deployment
mstore
(
0x00
,
mload
(
QM_X_LOC
))
mstore
(
0x20
,
mload
(
QM_Y_LOC
))
mstore
(
0x40
,
mload
(
C_V11_LOC
))
// accumulator_2 = v11.[Q;]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// ACCUMULATE QC
// Verification key fields verified to be on curve at contract deployment
mstore
(
0x00
,
mload
(
QC_X_LOC
))
mstore
(
0x20
,
mload
(
QC_Y_LOC
))
mstore
(
0x40
,
mload
(
C_V12_LOC
))
// accumulator_2 = v12.[QC]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// ACCUMULATE QARITH
// Verification key fields verified to be on curve at contract deployment
mstore
(
0x00
,
mload
(
QARITH_X_LOC
))
mstore
(
0x20
,
mload
(
QARITH_Y_LOC
))
mstore
(
0x40
,
mload
(
C_V13_LOC
))
// accumulator_2 = v13.[QARITH]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// ACCUMULATE QSORT
// Verification key fields verified to be on curve at contract deployment
mstore
(
0x00
,
mload
(
QSORT_X_LOC
))
mstore
(
0x20
,
mload
(
QSORT_Y_LOC
))
mstore
(
0x40
,
mload
(
C_V14_LOC
))
// accumulator_2 = v14.[QSORT]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// ACCUMULATE QELLIPTIC
// Verification key fields verified to be on curve at contract deployment
mstore
(
0x00
,
mload
(
QELLIPTIC_X_LOC
))
mstore
(
0x20
,
mload
(
QELLIPTIC_Y_LOC
))
mstore
(
0x40
,
mload
(
C_V15_LOC
))
// accumulator_2 = v15.[QELLIPTIC]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// ACCUMULATE QAUX
// Verification key fields verified to be on curve at contract deployment
mstore
(
0x00
,
mload
(
QAUX_X_LOC
))
mstore
(
0x20
,
mload
(
QAUX_Y_LOC
))
mstore
(
0x40
,
mload
(
C_V16_LOC
))
// accumulator_2 = v15.[Q_AUX]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// ACCUMULATE SIGMA1
// Verification key fields verified to be on curve at contract deployment
mstore
(
0x00
,
mload
(
SIGMA1_X_LOC
))
mstore
(
0x20
,
mload
(
SIGMA1_Y_LOC
))
mstore
(
0x40
,
mload
(
C_V17_LOC
))
// accumulator_2 = v17.[sigma1]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// ACCUMULATE SIGMA2
// Verification key fields verified to be on curve at contract deployment
mstore
(
0x00
,
mload
(
SIGMA2_X_LOC
))
mstore
(
0x20
,
mload
(
SIGMA2_Y_LOC
))
mstore
(
0x40
,
mload
(
C_V18_LOC
))
// accumulator_2 = v18.[sigma2]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// ACCUMULATE SIGMA3
// Verification key fields verified to be on curve at contract deployment
mstore
(
0x00
,
mload
(
SIGMA3_X_LOC
))
mstore
(
0x20
,
mload
(
SIGMA3_Y_LOC
))
mstore
(
0x40
,
mload
(
C_V19_LOC
))
// accumulator_2 = v19.[sigma3]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// ACCUMULATE SIGMA4
// Verification key fields verified to be on curve at contract deployment
mstore
(
0x00
,
mload
(
SIGMA4_X_LOC
))
mstore
(
0x20
,
mload
(
SIGMA4_Y_LOC
))
mstore
(
0x40
,
mload
(
C_V20_LOC
))
// accumulator_2 = v20.[sigma4]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// ACCUMULATE TABLE1
// Verification key fields verified to be on curve at contract deployment
mstore
(
0x00
,
mload
(
TABLE1_X_LOC
))
mstore
(
0x20
,
mload
(
TABLE1_Y_LOC
))
mstore
(
0x40
,
mulmod
(
addmod
(
mload
(
C_U_LOC
),
0x1
,
p
),
mload
(
C_V21_LOC
),
p
))
// accumulator_2 = u.[table1]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// ACCUMULATE TABLE2
// Verification key fields verified to be on curve at contract deployment
mstore
(
0x00
,
mload
(
TABLE2_X_LOC
))
mstore
(
0x20
,
mload
(
TABLE2_Y_LOC
))
mstore
(
0x40
,
mulmod
(
addmod
(
mload
(
C_U_LOC
),
0x1
,
p
),
mload
(
C_V22_LOC
),
p
))
// accumulator_2 = u.[table2]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// ACCUMULATE TABLE3
// Verification key fields verified to be on curve at contract deployment
mstore
(
0x00
,
mload
(
TABLE3_X_LOC
))
mstore
(
0x20
,
mload
(
TABLE3_Y_LOC
))
mstore
(
0x40
,
mulmod
(
addmod
(
mload
(
C_U_LOC
),
0x1
,
p
),
mload
(
C_V23_LOC
),
p
))
// accumulator_2 = u.[table3]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// ACCUMULATE TABLE4
// Verification key fields verified to be on curve at contract deployment
mstore
(
0x00
,
mload
(
TABLE4_X_LOC
))
mstore
(
0x20
,
mload
(
TABLE4_Y_LOC
))
mstore
(
0x40
,
mulmod
(
addmod
(
mload
(
C_U_LOC
),
0x1
,
p
),
mload
(
C_V24_LOC
),
p
))
// accumulator_2 = u.[table4]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// ACCUMULATE TABLE_TYPE
// Verification key fields verified to be on curve at contract deployment
mstore
(
0x00
,
mload
(
TABLE_TYPE_X_LOC
))
mstore
(
0x20
,
mload
(
TABLE_TYPE_Y_LOC
))
mstore
(
0x40
,
mload
(
C_V25_LOC
))
// accumulator_2 = v25.[TableType]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// ACCUMULATE ID1
// Verification key fields verified to be on curve at contract deployment
mstore
(
0x00
,
mload
(
ID1_X_LOC
))
mstore
(
0x20
,
mload
(
ID1_Y_LOC
))
mstore
(
0x40
,
mload
(
C_V26_LOC
))
// accumulator_2 = v26.[ID1]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// ACCUMULATE ID2
// Verification key fields verified to be on curve at contract deployment
mstore
(
0x00
,
mload
(
ID2_X_LOC
))
mstore
(
0x20
,
mload
(
ID2_Y_LOC
))
mstore
(
0x40
,
mload
(
C_V27_LOC
))
// accumulator_2 = v27.[ID2]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// ACCUMULATE ID3
// Verification key fields verified to be on curve at contract deployment
mstore
(
0x00
,
mload
(
ID3_X_LOC
))
mstore
(
0x20
,
mload
(
ID3_Y_LOC
))
mstore
(
0x40
,
mload
(
C_V28_LOC
))
// accumulator_2 = v28.[ID3]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// ACCUMULATE ID4
// Verification key fields verified to be on curve at contract deployment
mstore
(
0x00
,
mload
(
ID4_X_LOC
))
mstore
(
0x20
,
mload
(
ID4_Y_LOC
))
mstore
(
0x40
,
mload
(
C_V29_LOC
))
// accumulator_2 = v29.[ID4]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
/**
* COMPUTE BATCH EVALUATION SCALAR MULTIPLIER
*/
{
/**
* batch_evaluation = v0 * (w_1_omega * u + w_1_eval)
* batch_evaluation += v1 * (w_2_omega * u + w_2_eval)
* batch_evaluation += v2 * (w_3_omega * u + w_3_eval)
* batch_evaluation += v3 * (w_4_omega * u + w_4_eval)
* batch_evaluation += v4 * (s_omega_eval * u + s_eval)
* batch_evaluation += v5 * (z_omega_eval * u + z_eval)
* batch_evaluation += v6 * (z_lookup_omega_eval * u + z_lookup_eval)
*/
let
batch_evaluation
:=
mulmod
(
mload
(
C_V0_LOC
),
addmod
(
mulmod
(
mload
(
W1_OMEGA_EVAL_LOC
),
mload
(
C_U_LOC
),
p
),
mload
(
W1_EVAL_LOC
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V1_LOC
),
addmod
(
mulmod
(
mload
(
W2_OMEGA_EVAL_LOC
),
mload
(
C_U_LOC
),
p
),
mload
(
W2_EVAL_LOC
),
p
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V2_LOC
),
addmod
(
mulmod
(
mload
(
W3_OMEGA_EVAL_LOC
),
mload
(
C_U_LOC
),
p
),
mload
(
W3_EVAL_LOC
),
p
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V3_LOC
),
addmod
(
mulmod
(
mload
(
W4_OMEGA_EVAL_LOC
),
mload
(
C_U_LOC
),
p
),
mload
(
W4_EVAL_LOC
),
p
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V4_LOC
),
addmod
(
mulmod
(
mload
(
S_OMEGA_EVAL_LOC
),
mload
(
C_U_LOC
),
p
),
mload
(
S_EVAL_LOC
),
p
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V5_LOC
),
addmod
(
mulmod
(
mload
(
Z_OMEGA_EVAL_LOC
),
mload
(
C_U_LOC
),
p
),
mload
(
Z_EVAL_LOC
),
p
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V6_LOC
),
addmod
(
mulmod
(
mload
(
Z_LOOKUP_OMEGA_EVAL_LOC
),
mload
(
C_U_LOC
),
p
),
mload
(
Z_LOOKUP_EVAL_LOC
),
p
),
p
),
p
)
/**
* batch_evaluation += v7 * Q1_EVAL
* batch_evaluation += v8 * Q2_EVAL
* batch_evaluation += v9 * Q3_EVAL
* batch_evaluation += v10 * Q4_EVAL
* batch_evaluation += v11 * QM_EVAL
* batch_evaluation += v12 * QC_EVAL
* batch_evaluation += v13 * QARITH_EVAL
* batch_evaluation += v14 * QSORT_EVAL_LOC
* batch_evaluation += v15 * QELLIPTIC_EVAL_LOC
* batch_evaluation += v16 * QAUX_EVAL_LOC
* batch_evaluation += v17 * SIGMA1_EVAL_LOC
* batch_evaluation += v18 * SIGMA2_EVAL_LOC
* batch_evaluation += v19 * SIGMA3_EVAL_LOC
* batch_evaluation += v20 * SIGMA4_EVAL_LOC
*/
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V7_LOC
),
mload
(
Q1_EVAL_LOC
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V8_LOC
),
mload
(
Q2_EVAL_LOC
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V9_LOC
),
mload
(
Q3_EVAL_LOC
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V10_LOC
),
mload
(
Q4_EVAL_LOC
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V11_LOC
),
mload
(
QM_EVAL_LOC
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V12_LOC
),
mload
(
QC_EVAL_LOC
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V13_LOC
),
mload
(
QARITH_EVAL_LOC
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V14_LOC
),
mload
(
QSORT_EVAL_LOC
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V15_LOC
),
mload
(
QELLIPTIC_EVAL_LOC
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V16_LOC
),
mload
(
QAUX_EVAL_LOC
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V17_LOC
),
mload
(
SIGMA1_EVAL_LOC
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V18_LOC
),
mload
(
SIGMA2_EVAL_LOC
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V19_LOC
),
mload
(
SIGMA3_EVAL_LOC
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V20_LOC
),
mload
(
SIGMA4_EVAL_LOC
),
p
),
p
)
/**
* batch_evaluation += v21 * (table1(zw) * u + table1(z))
* batch_evaluation += v22 * (table2(zw) * u + table2(z))
* batch_evaluation += v23 * (table3(zw) * u + table3(z))
* batch_evaluation += v24 * (table4(zw) * u + table4(z))
* batch_evaluation += v25 * table_type_eval
* batch_evaluation += v26 * id1_eval
* batch_evaluation += v27 * id2_eval
* batch_evaluation += v28 * id3_eval
* batch_evaluation += v29 * id4_eval
* batch_evaluation += quotient_eval
*/
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V21_LOC
),
addmod
(
mulmod
(
mload
(
TABLE1_OMEGA_EVAL_LOC
),
mload
(
C_U_LOC
),
p
),
mload
(
TABLE1_EVAL_LOC
),
p
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V22_LOC
),
addmod
(
mulmod
(
mload
(
TABLE2_OMEGA_EVAL_LOC
),
mload
(
C_U_LOC
),
p
),
mload
(
TABLE2_EVAL_LOC
),
p
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V23_LOC
),
addmod
(
mulmod
(
mload
(
TABLE3_OMEGA_EVAL_LOC
),
mload
(
C_U_LOC
),
p
),
mload
(
TABLE3_EVAL_LOC
),
p
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V24_LOC
),
addmod
(
mulmod
(
mload
(
TABLE4_OMEGA_EVAL_LOC
),
mload
(
C_U_LOC
),
p
),
mload
(
TABLE4_EVAL_LOC
),
p
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V25_LOC
),
mload
(
TABLE_TYPE_EVAL_LOC
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V26_LOC
),
mload
(
ID1_EVAL_LOC
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V27_LOC
),
mload
(
ID2_EVAL_LOC
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V28_LOC
),
mload
(
ID3_EVAL_LOC
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mulmod
(
mload
(
C_V29_LOC
),
mload
(
ID4_EVAL_LOC
),
p
),
p
)
batch_evaluation
:=
addmod
(
batch_evaluation
,
mload
(
QUOTIENT_EVAL_LOC
),
p
)
mstore
(
0x00
,
0x01
)
// [1].x
mstore
(
0x20
,
0x02
)
// [1].y
mstore
(
0x40
,
sub
(
p
,
batch_evaluation
))
// accumulator_2 = -[1].(batch_evaluation)
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
if
iszero
(
success
)
{
mstore
(
0x0
,
OPENING_COMMITMENT_FAILED_SELECTOR
)
revert
(
0x00
,
0x04
)
}
}
/**
* PERFORM PAIRING PREAMBLE
*/
{
let
u
:=
mload
(
C_U_LOC
)
let
zeta
:=
mload
(
C_ZETA_LOC
)
// VALIDATE PI_Z
{
let
x
:=
mload
(
PI_Z_X_LOC
)
let
y
:=
mload
(
PI_Z_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
if
iszero
(
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
{
mstore
(
0x0
,
POINT_NOT_ON_CURVE_SELECTOR
)
revert
(
0x00
,
0x04
)
}
mstore
(
0x00
,
x
)
mstore
(
0x20
,
y
)
}
// compute zeta.[PI_Z] and add into accumulator
mstore
(
0x40
,
zeta
)
success
:=
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
)
// accumulator = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
ACCUMULATOR_X_LOC
,
0x40
))
// VALIDATE PI_Z_OMEGA
{
let
x
:=
mload
(
PI_Z_OMEGA_X_LOC
)
let
y
:=
mload
(
PI_Z_OMEGA_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
if
iszero
(
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
{
mstore
(
0x0
,
POINT_NOT_ON_CURVE_SELECTOR
)
revert
(
0x00
,
0x04
)
}
mstore
(
0x00
,
x
)
mstore
(
0x20
,
y
)
}
mstore
(
0x40
,
mulmod
(
mulmod
(
u
,
zeta
,
p
),
mload
(
OMEGA_LOC
),
p
))
// accumulator_2 = u.zeta.omega.[PI_Z_OMEGA]
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
ACCUMULATOR2_X_LOC
,
0x40
))
// PAIRING_RHS = accumulator + accumulator_2
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
ACCUMULATOR_X_LOC
,
0x80
,
PAIRING_RHS_X_LOC
,
0x40
))
mstore
(
0x00
,
mload
(
PI_Z_X_LOC
))
mstore
(
0x20
,
mload
(
PI_Z_Y_LOC
))
mstore
(
0x40
,
mload
(
PI_Z_OMEGA_X_LOC
))
mstore
(
0x60
,
mload
(
PI_Z_OMEGA_Y_LOC
))
mstore
(
0x80
,
u
)
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x40
,
0x60
,
0x40
,
0x40
))
// PAIRING_LHS = [PI_Z] + [PI_Z_OMEGA] * u
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
0x00
,
0x80
,
PAIRING_LHS_X_LOC
,
0x40
))
// negate lhs y-coordinate
mstore
(
PAIRING_LHS_Y_LOC
,
sub
(
q
,
mload
(
PAIRING_LHS_Y_LOC
)))
if
mload
(
CONTAINS_RECURSIVE_PROOF_LOC
)
{
// VALIDATE RECURSIVE P1
{
let
x
:=
mload
(
RECURSIVE_P1_X_LOC
)
let
y
:=
mload
(
RECURSIVE_P1_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
if
iszero
(
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
{
mstore
(
0x0
,
POINT_NOT_ON_CURVE_SELECTOR
)
revert
(
0x00
,
0x04
)
}
mstore
(
0x00
,
x
)
mstore
(
0x20
,
y
)
}
// compute u.u.[recursive_p1] and write into 0x60
mstore
(
0x40
,
mulmod
(
u
,
u
,
p
))
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
0x60
,
0x40
))
// VALIDATE RECURSIVE P2
{
let
x
:=
mload
(
RECURSIVE_P2_X_LOC
)
let
y
:=
mload
(
RECURSIVE_P2_Y_LOC
)
let
xx
:=
mulmod
(
x
,
x
,
q
)
// validate on curve
if
iszero
(
eq
(
mulmod
(
y
,
y
,
q
),
addmod
(
mulmod
(
x
,
xx
,
q
),
3
,
q
)))
{
mstore
(
0x0
,
POINT_NOT_ON_CURVE_SELECTOR
)
revert
(
0x00
,
0x04
)
}
mstore
(
0x00
,
x
)
mstore
(
0x20
,
y
)
}
// compute u.u.[recursive_p2] and write into 0x00
// 0x40 still contains u*u
success
:=
and
(
success
,
staticcall
(
gas
(),
7
,
0x00
,
0x60
,
0x00
,
0x40
))
// compute u.u.[recursiveP1] + rhs and write into rhs
mstore
(
0xa0
,
mload
(
PAIRING_RHS_X_LOC
))
mstore
(
0xc0
,
mload
(
PAIRING_RHS_Y_LOC
))
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
0x60
,
0x80
,
PAIRING_RHS_X_LOC
,
0x40
))
// compute u.u.[recursiveP2] + lhs and write into lhs
mstore
(
0x40
,
mload
(
PAIRING_LHS_X_LOC
))
mstore
(
0x60
,
mload
(
PAIRING_LHS_Y_LOC
))
success
:=
and
(
success
,
staticcall
(
gas
(),
6
,
0x00
,
0x80
,
PAIRING_LHS_X_LOC
,
0x40
))
}
if
iszero
(
success
)
{
mstore
(
0x0
,
PAIRING_PREAMBLE_FAILED_SELECTOR
)
revert
(
0x00
,
0x04
)
}
}
/**
* PERFORM PAIRING
*/
{
// rhs paired with [1]_2
// lhs paired with [x]_2
mstore
(
0x00
,
mload
(
PAIRING_RHS_X_LOC
))
mstore
(
0x20
,
mload
(
PAIRING_RHS_Y_LOC
))
mstore
(
0x40
,
0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2
)
// this is [1]_2
mstore
(
0x60
,
0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed
)
mstore
(
0x80
,
0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b
)
mstore
(
0xa0
,
0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa
)
mstore
(
0xc0
,
mload
(
PAIRING_LHS_X_LOC
))
mstore
(
0xe0
,
mload
(
PAIRING_LHS_Y_LOC
))
mstore
(
0x100
,
mload
(
G2X_X0_LOC
))
mstore
(
0x120
,
mload
(
G2X_X1_LOC
))
mstore
(
0x140
,
mload
(
G2X_Y0_LOC
))
mstore
(
0x160
,
mload
(
G2X_Y1_LOC
))
success
:=
staticcall
(
gas
(),
8
,
0x00
,
0x180
,
0x00
,
0x20
)
if
iszero
(
and
(
success
,
mload
(
0x00
)))
{
mstore
(
0x0
,
PAIRING_FAILED_SELECTOR
)
revert
(
0x00
,
0x04
)
}
}
{
mstore
(
0x00
,
0x01
)
return
(
0x00
,
0x20
)
// Proof succeeded!
}
}
}
}
contract
UltraVerifier
is
BaseUltraVerifier
{
function
getVerificationKeyHash
()
public
pure
override
(
BaseUltraVerifier
)
returns
(
bytes32
)
{
return
UltraVerificationKey
.
verificationKeyHash
();
}
function
loadVerificationKey
(
uint256
vk
,
uint256
_omegaInverseLoc
)
internal
pure
virtual
override
(
BaseUltraVerifier
)
{
UltraVerificationKey
.
loadVerificationKey
(
vk
,
_omegaInverseLoc
);
}
}
This diff is collapsed.
Click to expand it.
momiji-helpers/circuits/publish/proofs/publish.proof
0 → 100644
View file @
671a85fd
0x
\ No newline at end of file
This diff is collapsed.
Click to expand it.
momiji-helpers/circuits/publish/src/main.nr
0 → 100644
View file @
671a85fd
use dep::std;
use dep::helpers;
use dep::helpers::hash;
use dep::helpers::structs;
fn main(
pi_contract_hash: pub Field,
accumulator: Field,
batch: structs::Batch,
tx_verifier: structs::VerifierTx,
recursion_verifier: structs::Verifier
) {
let tx_root_calc: Field = hash::hash_tree_four(batch.utxo_roots);
assert(batch.batch_oracle == helpers::ZERO_VALUE);
let batch_root_calc: Field = hash::hash([tx_root_calc, batch.batch_oracle]);
let new_root_calc: Field = hash::hash([batch_root_calc, batch.old_state_root]);
let new_path_calc: [Field; 20] = hash::compute_sibling_path(
batch.hist_tree.old_path,
batch.hist_tree.leaf,
batch.hist_tree.index
);
assert(batch.new_state_root == new_root_calc);
let calc_hist_root = hash::compute_merkle_root(
batch.hist_tree.leaf,
batch.hist_tree.index,
batch.hist_tree.old_path
);
assert(calc_hist_root == batch.hist_tree.root);
let hist_root_calc = hash::compute_merkle_root(
new_root_calc,
batch.hist_tree.index + 1,
new_path_calc
);
assert(hist_root_calc == batch.hist_tree.new_root);
let mut hash_validation: [u8; 832] = [0; 832];
for i in 0..32 {
hash_validation[i] = hash::field_to_u8(batch.new_state_root)[i];
hash_validation[i + 32] = hash::field_to_u8(batch.hist_tree.root)[i];
hash_validation[i + 64] = hash::field_to_u8(batch.hist_tree.new_root)[i];
hash_validation[i + 96] = hash::field_to_u8(accumulator)[i];
hash_validation[i + 128] = hash::field_to_u8(tx_verifier.key_hash)[i];
hash_validation[i + 160] = hash::field_to_u8(recursion_verifier.key_hash)[i];
for j in 0..20 {
hash_validation[i + 192 + (32 * j)] = hash::field_to_u8(new_path_calc[j])[i];
}
}
let hash_generated: Field = hash::hash_to_field(std::hash::keccak256(hash_validation, hash_validation.len() as u32));
assert(pi_contract_hash == hash_generated);
std::verify_proof(
recursion_verifier.verification_key.as_slice(),
recursion_verifier.proof.as_slice(),
[accumulator].as_slice(),
recursion_verifier.key_hash
)
}
\ No newline at end of file
This diff is collapsed.
Click to expand it.
momiji-helpers/circuits/recursion/Nargo.toml
0 → 100644
View file @
671a85fd
[package]
name
=
"recursion"
type
=
"bin"
authors
=
[""]
compiler_version
=
">=0.19.2"
[dependencies]
helpers
=
{
path
=
"../helpers"
}
This diff is collapsed.
Click to expand it.
momiji-helpers/circuits/recursion/Prover.toml
0 → 100644
View file @
671a85fd
This diff is collapsed.
Click to expand it.
momiji-helpers/circuits/recursion/Verifier.toml
0 → 100644
View file @
671a85fd
This diff is collapsed.
Click to expand it.
momiji-helpers/circuits/recursion/proofs/proof
0 → 100644
View file @
671a85fd
This diff is collapsed.
Click to expand it.
momiji-helpers/circuits/recursion/src/main.nr
0 → 100644
View file @
671a85fd
use dep::std;
use dep::helpers;
use dep::helpers::structs;
use dep::helpers::hash;
#[recursive]
fn main(
accumulator: pub Field,
tx_verifier: structs::VerifierTx,
recursion_verifier: structs::Verifier,
previous_accumulator: Field,
tx: structs::PublicInputs
) {
let tx_as_keccak = tx.as_keccak();
let mut generated_accumulator_preimage: [u8; 128] = [0; 128];
for i in 0..32 {
generated_accumulator_preimage[i] = hash::field_to_u8(previous_accumulator)[i];
generated_accumulator_preimage[i + 32] = hash::field_to_u8(tx_as_keccak)[i];
generated_accumulator_preimage[i + 64] = hash::field_to_u8(tx_verifier.key_hash)[i];
generated_accumulator_preimage[i + 96] = hash::field_to_u8(recursion_verifier.key_hash)[i];
}
let generated_accumulator = hash::hash_to_field(std::hash::keccak256(generated_accumulator_preimage, generated_accumulator_preimage.len() as u32));
assert(accumulator == generated_accumulator);
let mut recursion_pi: Field = previous_accumulator;
let mut recursive_proof: [Field] = recursion_verifier.proof.as_slice();
if (previous_accumulator == helpers::ZERO_VALUE) {
assert(recursion_verifier.key_hash == 0x083764da4a71646a7c2d27cf8f17adc4f9e4f3d2c5a574b643f79864c280b4ce);
recursion_pi = tx.as_hash();
}
std::verify_proof(
recursion_verifier.verification_key.as_slice(),
recursive_proof,
[recursion_pi].as_slice(),
recursion_verifier.key_hash
);
std::verify_proof(
tx_verifier.verification_key.as_slice(),
tx_verifier.proof.as_slice(),
[tx.as_hash()].as_slice(),
tx_verifier.key_hash
);
}
\ No newline at end of file
This diff is collapsed.
Click to expand it.
momiji-helpers/circuits/recursion/target/acir.gz
0 → 100644
View file @
671a85fd
File added
This diff is collapsed.
Click to expand it.
momiji-helpers/circuits/recursion/target/vk
0 → 100644
View file @
671a85fd
File added
This diff is collapsed.
Click to expand it.
momiji-helpers/circuits/recursion/target/vk_fields.json
0 → 100644
View file @
671a85fd
[
"0x1727d9cce8edf8b1d7228edf940be5ab221dc023f0e21785e634f5dcf9718007"
,
"0x26125da10a0ed06327508aba06d1e303ac616632dbed349f53422da953337857"
,
"0x0000000000000000000000000000000000000000000000000000000000100000"
,
"0x0000000000000000000000000000000000000000000000000000000000000005"
,
"0x0000000000000000000000000000000000000000000000000000000000100000"
,
"0x0000000000000000000000000000000000000000000000000000000000000011"
,
"0x0000000000000000000000000000000000000000000000000000000000000001"
,
"0x0000000000000000000000000000000000000000000000000000000000000001"
,
"0x0000000000000000000000000000000000000000000000000000000000000002"
,
"0x0000000000000000000000000000000000000000000000000000000000000003"
,
"0x0000000000000000000000000000000000000000000000000000000000000004"
,
"0x0000000000000000000000000000000000000000000000000000000000000005"
,
"0x0000000000000000000000000000000000000000000000000000000000000006"
,
"0x0000000000000000000000000000000000000000000000000000000000000007"
,
"0x0000000000000000000000000000000000000000000000000000000000000008"
,
"0x0000000000000000000000000000000000000000000000000000000000000009"
,
"0x000000000000000000000000000000000000000000000000000000000000000a"
,
"0x000000000000000000000000000000000000000000000000000000000000000b"
,
"0x000000000000000000000000000000000000000000000000000000000000000c"
,
"0x000000000000000000000000000000000000000000000000000000000000000d"
,
"0x000000000000000000000000000000000000000000000000000000000000000e"
,
"0x000000000000000000000000000000000000000000000000000000000000000f"
,
"0x0000000000000000000000000000000000000000000000000000000000000010"
,
"0x0000000000000000000000000000001ed8a02482426d263a4ecb0ef896ce89aa"
,
"0x00000000000000000000000000000000002dd82b96a445a3d48db46e78fd1fe5"
,
"0x0000000000000000000000000000001a810690ec0eb9e6cb49dd68aed57b9a2c"
,
"0x00000000000000000000000000000000002979d0f3b9cbc56e6a5eed0176979c"
,
"0x000000000000000000000000000000ece4843dd3e3cb41a29ffcc72d49376ac5"
,
"0x000000000000000000000000000000000013f586937d9e92ad01910eaf7267fe"
,
"0x000000000000000000000000000000832de083f2aea887e5b0b3e07195bf3369"
,
"0x0000000000000000000000000000000000020d7d6640cc6909b63450357223cd"
,
"0x0000000000000000000000000000001a1e30f8051c1e84c165acf80086e8dfca"
,
"0x00000000000000000000000000000000002c77a04e17e8644f9b7053b4bb08fa"
,
"0x000000000000000000000000000000accbe6a903bb7bfaf1909074da32f28e74"
,
"0x000000000000000000000000000000000010ef1c64b5aa796d3218d20cfdb0a4"
,
"0x000000000000000000000000000000cbc1cc027a33d219526471449d9049146a"
,
"0x00000000000000000000000000000000000735dd552cfe8a4cf17f17049626d0"
,
"0x000000000000000000000000000000f3576e636ff3ebaff43f0d1c8294b67a8c"
,
"0x00000000000000000000000000000000001bb99e027a879809200c2f3e9992da"
,
"0x00000000000000000000000000000004f1b4950d8b0919092c8b596a63e9396e"
,
"0x00000000000000000000000000000000001e031462c21a2937998d8ab726051a"
,
"0x000000000000000000000000000000618474ad492a93435a82a433a1ae1b5c04"
,
"0x00000000000000000000000000000000001beb3c3937fc9ff20499d59cc4665e"
,
"0x000000000000000000000000000000053a4799f326db1157b41ba75481ac65ce"
,
"0x00000000000000000000000000000000002a6cafad02233a289b5ee0dac52d24"
,
"0x00000000000000000000000000000029b0e33ec90e9b6e7c0c774b2a394f5c90"
,
"0x00000000000000000000000000000000000ff63c1bf54473c34651e117d63bbb"
,
"0x000000000000000000000000000000a0098ac613ba3629b0421d7894f0bc65d8"
,
"0x00000000000000000000000000000000001f58e2dbf833ed8c21a5ac822a390e"
,
"0x000000000000000000000000000000ad30140ddc344a8ac7a49d29b0b600d888"
,
"0x000000000000000000000000000000000000b0088164d522367583c90f73bcd7"
,
"0x0000000000000000000000000000004a05d497f585103d826b8a133761cc79ef"
,
"0x000000000000000000000000000000000011888c79e6b006e1a7d293898b9842"
,
"0x00000000000000000000000000000039b41a3dfe9e948d33fd4a759e9e8a7b30"
,
"0x00000000000000000000000000000000000427be5203eb43e160d9635925f64a"
,
"0x000000000000000000000000000000c0b125470d496d55ec3d8678b1efcc0b14"
,
"0x00000000000000000000000000000000000a346d061bfa2ad56a322a81a8be06"
,
"0x000000000000000000000000000000e36f8d9132dccbddbcbc4eca6bdb70583a"
,
"0x000000000000000000000000000000000006e53484e1a4f26129c00c5afdf4d4"
,
"0x000000000000000000000000000000d56ff07f6c8d054cf78d713a5f4399313b"
,
"0x000000000000000000000000000000000009fa75fa397a889177953777b32ad7"
,
"0x000000000000000000000000000000a9eba193f17a0cbcfd5cd786e8fc93e044"
,
"0x0000000000000000000000000000000000231e1180c41ca9fcad8207beb4aeb2"
,
"0x000000000000000000000000000000439f7eb6e39f8ef36735fde1563efc018c"
,
"0x0000000000000000000000000000000000131bb8b31246156eac5a9632b1c44c"
,
"0x000000000000000000000000000000ff14d910386a5d634a00deb8432bd230bd"
,
"0x0000000000000000000000000000000000032cf0e4f77b2c32b39804b2e836a1"
,
"0x00000000000000000000000000000059d45db08da921eb51ee629b37eaac3804"
,
"0x00000000000000000000000000000000001351c4d6ee97e5a13968cd8b575376"
,
"0x000000000000000000000000000000a1fef20a9e9baae03c66b387855786961c"
,
"0x00000000000000000000000000000000000087e919ca347c14bae58f2a007979"
,
"0x00000000000000000000000000000068c2dee24bf49f48ca91a9b31cf489100a"
,
"0x00000000000000000000000000000000002ebbec213fd5e638e763bf574db1dd"
,
"0x000000000000000000000000000000405686bb9661ab548fc6490de8273991c1"
,
"0x00000000000000000000000000000000000f55573e8cc6debc7e4ad36f55f0a3"
,
"0x00000000000000000000000000000016c8911d3abcc99956cfafd2469e57d352"
,
"0x000000000000000000000000000000000023987c17049de9e6e90534922b068d"
,
"0x000000000000000000000000000000310a00459798915e70bb5f6c0d93950c61"
,
"0x0000000000000000000000000000000000190404b6761da87bbb58c68adf9c07"
,
"0x0000000000000000000000000000007003780317cc6fecafa2529793ea4d856a"
,
"0x00000000000000000000000000000000000c49e1acc2e2a5ed2523a68e13bcc7"
,
"0x0000000000000000000000000000004564d9adecd904030f441c500bea3a54e8"
,
"0x0000000000000000000000000000000000118e887dc7827a303ae4efd884289f"
,
"0x00000000000000000000000000000055bcb8484f4bfca2d81c1a7a08f92dcc1e"
,
"0x0000000000000000000000000000000000192d66138ef5fb5cbd60d25910fa8b"
,
"0x0000000000000000000000000000006ce36390303806bf1667cac7fd3a0a911f"
,
"0x000000000000000000000000000000000025719fd7b4f27bd77387d4580c46ef"
,
"0x000000000000000000000000000000e15e5e8edb26147b877fd483a9be1ee557"
,
"0x00000000000000000000000000000000000d4744317ff703c20132134b9af145"
,
"0x00000000000000000000000000000097931bbb683b832edaedfb84ae020a3e6c"
,
"0x000000000000000000000000000000000024e913db937478f592ff958369c33b"
,
"0x000000000000000000000000000000aad0edb65a7896339ad92cb7bdda5bc0cb"
,
"0x00000000000000000000000000000000001f13f1759e514b1f053da73837b9bf"
,
"0x000000000000000000000000000000b5928facba6909a6cb9d3f99b451dbb0da"
,
"0x0000000000000000000000000000000000140e3a61cbd87e23e9f59c0c8a5f92"
,
"0x0000000000000000000000000000000c542c4e643211a206a43e3c82940ce012"
,
"0x0000000000000000000000000000000000073ab903217225282916b264ffbd6f"
,
"0x000000000000000000000000000000362a93230a711f6dff86b1a347c978b220"
,
"0x00000000000000000000000000000000001a14bd51e9dc0b4b973e46910d2b6f"
,
"0x000000000000000000000000000000e13a606550f2c61830bf9da814621581ee"
,
"0x00000000000000000000000000000000001297f1e111cc1ea7a2221affb5eecd"
,
"0x00000000000000000000000000000000aa60b3f6dc00be97871878c40d04a293"
,
"0x000000000000000000000000000000000007f762063f0123fba74c4cd66e7559"
,
"0x000000000000000000000000000000541c1db7e3c80a294ea50b24011999fe4e"
,
"0x00000000000000000000000000000000002e238e496f9533c8fe3a9342fcf1f6"
,
"0x000000000000000000000000000000f65f2ad1025c0ec713b29b2db4c57e83d6"
,
"0x000000000000000000000000000000000013ff20a10295d07fd0b16253a6775a"
,
"0x00000000000000000000000000000026d344ed7a87caa6087967171876052349"
,
"0x00000000000000000000000000000000002626bea2de7026dd6dd4e1df614a65"
,
"0x000000000000000000000000000000b264d3a3a6bb7a41d57f96e6877e5bb37d"
,
"0x0000000000000000000000000000000000275f1c996d71439acf76f03932c52c"
,
"0x000000000000000000000000000000c8b2af388b1f0b0832ca8f2a0154be54ce"
,
"0x00000000000000000000000000000000003020cfd271da8c447e2ccff6908c3a"
,
"0x00000000000000000000000000000008e8f770eef342595ebce4b42811af4966"
,
"0x000000000000000000000000000000000002ae8005a2c1cc97d4cdc896b54e70"
]
\ No newline at end of file
This diff is collapsed.
Click to expand it.
Prev
1
2
3
4
5
6
…
17
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment
Menu
Projects
Groups
Snippets
Help