@crisp-e3/contracts 0.5.12 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -41,6 +41,8 @@ contract CRISPProgram is IE3Program, Ownable {
41
41
  uint8 public constant TREE_DEPTH = 20;
42
42
  /// @notice Maximum number of bits allocated for vote counts in the plaintext output per option.
43
43
  uint256 constant MAX_VOTE_BITS = 50;
44
+ /// @notice The zero-knowledge verification key hash for the CRISP program.
45
+ bytes32 public constant ZK_VK_HASH = 0xfbb1352f018828a2e0989e3010af055b6217b60462066ae0ce06209e22ffa8c2;
44
46
 
45
47
  // State variables
46
48
  IEnclave public enclave;
@@ -171,22 +173,26 @@ contract CRISPProgram is IE3Program, Ownable {
171
173
 
172
174
  if (data.length == 0) revert EmptyInputData();
173
175
 
174
- (bytes memory noirProof, address slotAddress, bytes32 encryptedVoteCommitment, bytes memory encryptedVote) = abi.decode(
175
- data,
176
- (bytes, address, bytes32, bytes)
177
- );
176
+ (bytes memory noirProof, address slotAddress, bytes32 encryptedVoteCommitment, bytes32 zkKeyHash, bytes memory encryptedVote) = abi
177
+ .decode(data, (bytes, address, bytes32, bytes32, bytes));
178
+
179
+ if (zkKeyHash != ZK_VK_HASH) revert InvalidNoirProof();
178
180
 
179
181
  (uint40 voteIndex, bytes32 previousEncryptedVoteCommitment) = _processVote(e3Id, slotAddress, encryptedVoteCommitment);
180
182
 
181
183
  // Set the public inputs for the proof. Order must match Noir circuit.
182
- bytes32[] memory noirPublicInputs = new bytes32[](7);
184
+ bytes32[] memory noirPublicInputs = new bytes32[](39);
183
185
  noirPublicInputs[0] = previousEncryptedVoteCommitment;
184
- noirPublicInputs[1] = e3.committeePublicKey;
185
- noirPublicInputs[2] = bytes32(e3Data[e3Id].merkleRoot);
186
- noirPublicInputs[3] = bytes32(uint256(uint160(slotAddress)));
187
- noirPublicInputs[4] = bytes32(uint256(previousEncryptedVoteCommitment == bytes32(0) ? 1 : 0));
188
- noirPublicInputs[5] = bytes32(e3Data[e3Id].numOptions);
189
- noirPublicInputs[6] = encryptedVoteCommitment;
186
+ noirPublicInputs[1] = bytes32(e3Data[e3Id].merkleRoot);
187
+ noirPublicInputs[2] = bytes32(uint256(uint160(slotAddress)));
188
+ noirPublicInputs[3] = bytes32(uint256(previousEncryptedVoteCommitment == bytes32(0) ? 1 : 0));
189
+ noirPublicInputs[4] = bytes32(e3Data[e3Id].numOptions);
190
+ noirPublicInputs[5] = encryptedVoteCommitment;
191
+ noirPublicInputs[6] = e3.committeePublicKey;
192
+ // Insert ZK_VK_HASH as 32 separate bytes (each as bytes32), matching proof format
193
+ for (uint256 i = 0; i < 32; i++) {
194
+ noirPublicInputs[7 + i] = bytes32(uint256(uint8(zkKeyHash[i])));
195
+ }
190
196
 
191
197
  // Check if the ciphertext was encrypted correctly
192
198
  if (!honkVerifier.verify(noirProof, noirPublicInputs)) {
@@ -5,127 +5,127 @@
5
5
  // or FITNESS FOR A PARTICULAR PURPOSE.
6
6
  pragma solidity >=0.8.21;
7
7
 
8
- uint256 constant N = 524288;
9
- uint256 constant LOG_N = 19;
10
- uint256 constant NUMBER_OF_PUBLIC_INPUTS = 23;
11
- uint256 constant VK_HASH = 0x0b9c1b2dd5f380cd949f20a3a3f09c947df53c467285680777412ea4868a8d18;
8
+ uint256 constant N = 2097152;
9
+ uint256 constant LOG_N = 21;
10
+ uint256 constant NUMBER_OF_PUBLIC_INPUTS = 55;
11
+ uint256 constant VK_HASH = 0x0ea01fb8e0ba32adb2d0ab6391f3bccb82eaf1ea55adcf5b033ea180190f11bc;
12
12
  library HonkVerificationKey {
13
13
  function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) {
14
14
  Honk.VerificationKey memory vk = Honk.VerificationKey({
15
- circuitSize: uint256(524288),
16
- logCircuitSize: uint256(19),
17
- publicInputsSize: uint256(23),
15
+ circuitSize: uint256(2097152),
16
+ logCircuitSize: uint256(21),
17
+ publicInputsSize: uint256(55),
18
18
  ql: Honk.G1Point({
19
- x: uint256(0x240f688502a6acfbcaeaaa0846cd10fca763cc54deb740784eb4d79ed50a478e),
20
- y: uint256(0x1f085373708e94340f4f4741a11126be4f87589358cebb953ee6dddd1dca1172)
19
+ x: uint256(0x0b49cca46309009bcfb42f7e72925f6b8b5f102e6b6ab8825dae8a96fe3cba34),
20
+ y: uint256(0x1e4d119e1991788b257fd6c8b9d701c6b4fb80dc24e6bb34489e2aa679e4369b)
21
21
  }),
22
22
  qr: Honk.G1Point({
23
- x: uint256(0x03ca30044543a7c02e08505b4ed256125807d7bda68712c65bd674e7be18907e),
24
- y: uint256(0x223b9b4b0bfd2328aa898531ee94dde61fcaa9133773dc33184e167bb33fb343)
23
+ x: uint256(0x23fcf389d77e9fb6e9f257b2a577311a62703d7746e077b8c4efe6a40bcadc1a),
24
+ y: uint256(0x257e7923fabec927d96159f14fcede129348203152597db67c4ec0fe392d3038)
25
25
  }),
26
26
  qo: Honk.G1Point({
27
- x: uint256(0x01df0faac1b219c2932d044ff18952ccfdb0da9f3ca8d6d98d19d3b5dcaa75a8),
28
- y: uint256(0x2676231938eab7bf9e8b609d039dc7f0e9c28c8f94cf0ffcc8ce0ee2c40966e5)
27
+ x: uint256(0x125a3d99a31fa1f16585dfa287e82903828bb535c4b67895869f26d30b34a19b),
28
+ y: uint256(0x1a76e12e46934a8183e4d3983c40a12aa645054d272c5492aa48f933b216c957)
29
29
  }),
30
30
  q4: Honk.G1Point({
31
- x: uint256(0x05150a77def0a11380c5a171f15aac571dc0737a55aeddde2fdb147742cfccd3),
32
- y: uint256(0x1fab3e3345368910a19c18ce2c9649a27cc721d406fdec42e02204ac277161cb)
31
+ x: uint256(0x02717f10da28d42bd2096b5501b44c5e29a9d88ee76ad6b0e48b6d2b68dacf7d),
32
+ y: uint256(0x16e4f684dddc45d91078cef3af321dc5db6ba7ea9fc527a800a0e20e5ffbd9f3)
33
33
  }),
34
34
  qm: Honk.G1Point({
35
- x: uint256(0x264abf2ea58ab66dc9310c0d6360bd1fe8d7408259cedfbbeb119d6004583d53),
36
- y: uint256(0x089c345678f0ed3d16009853498f4189927c1c49eaab388e535b4ed5abc179ba)
35
+ x: uint256(0x0fdda6f7325628ba94b4ae2e823a1d5a1dfe18fbd1c875850ff4d7e6e2bb738a),
36
+ y: uint256(0x1fcedd6727ab1f287d0a2c2e68ef08319e00352719055e9fb40a4292282e167f)
37
37
  }),
38
38
  qc: Honk.G1Point({
39
- x: uint256(0x24bfc2a0618dd709363c0f726d12b55f556749f83ce5b7ccdfe214d2761cca2e),
40
- y: uint256(0x237c95ed74cf4ba0eedec3e8172cc943b4ea2b171806c3a528d1f13f83816180)
39
+ x: uint256(0x06422053a8c6503244e00acc1e275c3b86a2bd7139ae666d909453b766797de2),
40
+ y: uint256(0x000033f450d6a78d5841b7027366c1f40bf240dc5fbaf3a01e14aa5f047bb36c)
41
41
  }),
42
42
  qLookup: Honk.G1Point({
43
- x: uint256(0x136236e1bfc2af648ac078e134c1b4b9114b11937ebafcdd87f8ca7660715ebb),
44
- y: uint256(0x02293c705250462935a653b7b993e13e2e8bc6480c45c84976d526cbdbd071df)
43
+ x: uint256(0x285cd2008c8e9d282963405be4ac4f7733be06e27d5706a518a5902d712cae6c),
44
+ y: uint256(0x2efd1ebb7f3ed30e0c96ae4d42b15d9e6cd18d906f198a67fb3cbb64c62b3cb5)
45
45
  }),
46
46
  qArith: Honk.G1Point({
47
- x: uint256(0x1f15325c29f480d9fa82d2c7ed4994844919e3d613ba98aca98155abbe8af1d0),
48
- y: uint256(0x1eaf5badfb1f6604278dbe5b8cc935bc0cf19eaef7b77c7ca2f231a65ca94c83)
47
+ x: uint256(0x0afc885f2b56f40755a8f66743425b589f8ac283920e112dc4725a503ed69e14),
48
+ y: uint256(0x0fc111111f23cb77fb579532a4c393fafc412a223f76743c98567d08a2ce8450)
49
49
  }),
50
50
  qDeltaRange: Honk.G1Point({
51
- x: uint256(0x01319d2844fd5491e4140d6f0c43f4b708e1192c5bbdd2c0a53a7da62983efd8),
52
- y: uint256(0x164d72be753ed477363f4af7c5e0110d46c83ec7ebf4b753eb228e4f42b37ac8)
51
+ x: uint256(0x1968df000ac48429eb10b056a0a6dd3d1da8332b5de3724ab87547c3371ffc42),
52
+ y: uint256(0x18e9cb336ca060c58b7f6051d792fc40c0b488fd9672807e10cf10eab7cacb7a)
53
53
  }),
54
54
  qElliptic: Honk.G1Point({
55
- x: uint256(0x0672a9cbd7c1f9768e8d307bdd295145b3547b2783065b521a23487b927c0cfb),
56
- y: uint256(0x1252d1d2d8b7841d1e92ae3167104d924eabd7607bb5a655ad42cc3fedcd25f3)
55
+ x: uint256(0x177d1a59fab0f5281dfd02b19aa00eb945c77f91c5bd6112b8e857db9dc6b798),
56
+ y: uint256(0x11b2038670e6e114f87b1d472a31cea6390f5de495febdf47237a806969fae04)
57
57
  }),
58
58
  qMemory: Honk.G1Point({
59
- x: uint256(0x23d2fc90f4b3243cf25c79e052fddb5489397999ed49771b59da5ac27677647b),
60
- y: uint256(0x0523ed03b53e8f80c7588938b2e5487f6cc4a63918d3f1263cfab7a7ad61737d)
59
+ x: uint256(0x02b0cec1f1b948b9e689b5b172eb85c8cd7b901c142e07df3fde5b9d74ca7414),
60
+ y: uint256(0x2d84255bc631bcb170192dd095b8785369495061366530ba5c843e7a97240be9)
61
61
  }),
62
62
  qNnf: Honk.G1Point({
63
- x: uint256(0x0b3512e728f8ace54f9a6d83334f4f35e073c641b843a6ac52d4d99917b02349),
64
- y: uint256(0x1c89ed6a83ea8bdce1e560ab0204ee0939e1784dffc427cc11e321b61bed74c6)
63
+ x: uint256(0x053c8110bdde783ecb8ec60d71f1bd85a3bc8b43e1fe443c321a44d5692cdfc8),
64
+ y: uint256(0x177969870de5b24725a64587e0cf2258988960674a6ccc722133edb3fb6768aa)
65
65
  }),
66
66
  qPoseidon2External: Honk.G1Point({
67
- x: uint256(0x0b6006bc7b12f44177d3b59d197fe11d2fc41599f6f1fde44c7f268dd32d337e),
68
- y: uint256(0x0c45abdec36bd5b61f2edca097daccb7a81455a2d88496a6d1a72aef33b8b7b0)
67
+ x: uint256(0x087716c4e6b28e30afa0a4b0cf49a1ee4643709464ef89e708481e9fbcb3acf4),
68
+ y: uint256(0x081caf798962ce0724f2880fccebc8c2ee75ee6d0ea87d308fa91a97e4587afb)
69
69
  }),
70
70
  qPoseidon2Internal: Honk.G1Point({
71
- x: uint256(0x0be8cb27fcd19968dfa54fd667491a0e95b7a92518a9a82b506dfdd9d8bb0323),
72
- y: uint256(0x01c52d04c4d69fcaecdacb0b9dba750320704ae6375f8ef985a9fc2cb555ac48)
71
+ x: uint256(0x1f7ab7d4ad925b2c31233ea29ba4debac639158de845f734d3d4340f84d72937),
72
+ y: uint256(0x2d228dbe4b4eb9baf6d62068cb766b7fb3b44a1a3cbde58a5c40c69ff6e176d8)
73
73
  }),
74
74
  s1: Honk.G1Point({
75
- x: uint256(0x1a7257ea10514736801482f89408f5925618409f43bf217880f9ba8564708b3c),
76
- y: uint256(0x21bdeab9c20c3e3d0727429b2a6efdbd7473e1b239ef02d9581de8499444a82c)
75
+ x: uint256(0x2bd432c0b486d1b9a75870a420e02622eeec72f59615c9532a22c2d946f8dc03),
76
+ y: uint256(0x1037cf6462b36a560256ea2c03306634ed4d87e76ea81052083700fbb1a5f8d4)
77
77
  }),
78
78
  s2: Honk.G1Point({
79
- x: uint256(0x1cdd60998e69b08a3178183583434d5d1747fe2b59d53198cee0189184ef4137),
80
- y: uint256(0x0e1ec93697a66ada78411a4343a73a94162688bc96ceec69e06b6b292ad6ae15)
79
+ x: uint256(0x1359c3633ccf1c39969812967b170b2592f88dbec76079f0779c4ae17f79c432),
80
+ y: uint256(0x0e696f759fc6cd345418c89c3737d237bd2685d59b1843a884b2436f2a67fb97)
81
81
  }),
82
82
  s3: Honk.G1Point({
83
- x: uint256(0x2e3739fca228a028616288bded555bda201546582e0fbfb15ba1d79ecedb99ff),
84
- y: uint256(0x2d3fa85df312d814ce05e6424c0789aabd29780e2751d06191b5c19385c495e8)
83
+ x: uint256(0x2201b7d5413f027286a8ecebfb33c3fdac7974d241e428f2ab549bcb6b1ec379),
84
+ y: uint256(0x1327e17ebdcc90f8233b838e4836974e68812dde890018ce5e59ba0bed8d4eb1)
85
85
  }),
86
86
  s4: Honk.G1Point({
87
- x: uint256(0x2cc67ab5542d9dcb11e2f47c61991d3b8c1530be0fb65ae78bb579575472be37),
88
- y: uint256(0x1a9453a6379fcd5d9357de3c3c92f09a37a566d01452ccbc053a79cbf5d3062b)
87
+ x: uint256(0x2983325004b0f8ebfda26f1425a3fa7c780c9619a0a57b6026cebc3241233bb3),
88
+ y: uint256(0x21e0b11f18c587feb26a1b0984af1b928df573b57f6e1e7430892d02eebdaf56)
89
89
  }),
90
90
  t1: Honk.G1Point({
91
- x: uint256(0x1f16b037f0b4c96ea2a30a118a44e139881c0db8a4d6c9fde7db5c1c1738e61f),
92
- y: uint256(0x00c7781bda34afc32dedfb0d0f6b16c987c83375da000e180b44ad25685fc2ae)
91
+ x: uint256(0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c),
92
+ y: uint256(0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579)
93
93
  }),
94
94
  t2: Honk.G1Point({
95
- x: uint256(0x29345f914a28707887bee191c3a928191de584827a6d1a78ccce1d7629ca9dc0),
96
- y: uint256(0x1920cebd0b33ac9713424e3bc03d53d79dc72f6afc24c90e56593094a213444c)
95
+ x: uint256(0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887),
96
+ y: uint256(0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8)
97
97
  }),
98
98
  t3: Honk.G1Point({
99
- x: uint256(0x261c990958bc2ef77a45467d9639ab2c68cf787ff7bce55ce3074dfdaedc8f8f),
100
- y: uint256(0x23c1c05424a40360a61e46f4deab04988a6f5b71dda351e0da608cff1f332ee0)
99
+ x: uint256(0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f),
100
+ y: uint256(0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7)
101
101
  }),
102
102
  t4: Honk.G1Point({
103
- x: uint256(0x2b651d2fd644b2972d72ec439dc69d3339d0b052a296bfc48c6a08396aaca078),
104
- y: uint256(0x2d7e8c1ecb92e2490049b50efc811df63f1ca97e58d5e82852dbec0c29715d71)
103
+ x: uint256(0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6),
104
+ y: uint256(0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea)
105
105
  }),
106
106
  id1: Honk.G1Point({
107
- x: uint256(0x242bbe50c5583ccc277f71ef51aa243eda251285787baac14b27a1f4cc411edf),
108
- y: uint256(0x154f6eb3c9511184ca082c61b12292332f5a4d3a20f6612c8b337b988df6441e)
107
+ x: uint256(0x298881ca881438995e404581a83530c3d88dfa93485a6f29a882cdd2670e9593),
108
+ y: uint256(0x2cfd5e44e0a6979145879de24261c18defe4c22e7a7a23b20b002030e675c643)
109
109
  }),
110
110
  id2: Honk.G1Point({
111
- x: uint256(0x28516035c0e7f925849fc922c1cd3cc13d6da9c37e723131dbff7c7bb672d762),
112
- y: uint256(0x25a236e395d2e5b5b6bfa4c732982c53aeb626cd4f84624ced88724a2b271820)
111
+ x: uint256(0x02c16723d917f17d181a59379615e683306841760032f394cdb0b4f2903c0af7),
112
+ y: uint256(0x08d4e6f0f5f23b5431a424bd364a33e83009d3dce3214c92c22e033adb02f09c)
113
113
  }),
114
114
  id3: Honk.G1Point({
115
- x: uint256(0x296663882374f237a8fc9915de61b3a2f558a731cb10204dfa257273d90f6b93),
116
- y: uint256(0x2b20baa201e9ddcbbf5eba8a2d5cff0cb1b5e0ba96be4bbd3b0da781cca7b47d)
115
+ x: uint256(0x21dc5e14e79ab4bf1244065498cc521f001630dee5a631a0ace7cc03c12afc0c),
116
+ y: uint256(0x1fc77a1fc605ba6548e353b93411784ef9f79b966b676273acf37519b88a451f)
117
117
  }),
118
118
  id4: Honk.G1Point({
119
- x: uint256(0x1438d3dc0bd7d25324cfddbbdbe88df7782c33920c2276cafa2b5e316639721b),
120
- y: uint256(0x0bc0958c27e76138920ac5aa682f2b724456ebf2bd169a863244b9cc153639db)
119
+ x: uint256(0x246bd4706d80d364c95e550e7e12816e13cf5736985becd309eab4503a6a5dec),
120
+ y: uint256(0x1453ed9448d07a67232a466d416620e9cb8438cc6c54225cb12acbde33d0a2a9)
121
121
  }),
122
122
  lagrangeFirst: Honk.G1Point({
123
123
  x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001),
124
124
  y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002)
125
125
  }),
126
126
  lagrangeLast: Honk.G1Point({
127
- x: uint256(0x0ffb0f958447e12cda902c71cb70f85d2ebfcd4b93f50569c2f00eff7e60368e),
128
- y: uint256(0x1230b28595aee83bc86d1c7fb3d0fc726604d0e86455cf648b9ddaa89c173ce4)
127
+ x: uint256(0x12541b65d3fa0ed265d6fa8823d60b50e7e245797410c733a497fe858346b8fd),
128
+ y: uint256(0x237bf222dbadaeb345852f0bf7e93c87f7bbf30b85cacbd349d95c0f76c4e318)
129
129
  })
130
130
  });
131
131
  return vk;
@@ -296,14 +296,18 @@ uint256 constant NUMBER_OF_SUBRELATIONS = 28;
296
296
  uint256 constant BATCHED_RELATION_PARTIAL_LENGTH = 8;
297
297
  uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9;
298
298
  uint256 constant NUMBER_OF_ENTITIES = 41;
299
+ // The number of entities added for ZK (gemini_masking_poly)
300
+ uint256 constant NUM_MASKING_POLYNOMIALS = 1;
301
+ uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS;
299
302
  uint256 constant NUMBER_UNSHIFTED = 36;
303
+ uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS;
300
304
  uint256 constant NUMBER_TO_BE_SHIFTED = 5;
301
305
  uint256 constant PAIRING_POINTS_SIZE = 16;
302
306
 
303
307
  uint256 constant FIELD_ELEMENT_SIZE = 0x20;
304
308
  uint256 constant GROUP_ELEMENT_SIZE = 0x40;
305
309
 
306
- // Alphas are used as relation separators so there should be NUMBER_OF_SUBRELATIONS - 1
310
+ // Powers of alpha used to batch subrelations (alpha, alpha^2, ..., alpha^(NUM_SUBRELATIONS-1))
307
311
  uint256 constant NUMBER_OF_ALPHAS = NUMBER_OF_SUBRELATIONS - 1;
308
312
 
309
313
  // ENUM FOR WIRES
@@ -377,7 +381,7 @@ library Honk {
377
381
  G1Point qElliptic; // Auxillary
378
382
  G1Point qPoseidon2External;
379
383
  G1Point qPoseidon2Internal;
380
- // Copy cnstraints
384
+ // Copy constraints
381
385
  G1Point s1;
382
386
  G1Point s2;
383
387
  G1Point s3;
@@ -432,9 +436,12 @@ library Honk {
432
436
  G1Point kzgQuotient;
433
437
  }
434
438
 
439
+ /// forge-lint: disable-next-item(pascal-case-struct)
435
440
  struct ZKProof {
436
441
  // Pairing point object
437
442
  Fr[PAIRING_POINTS_SIZE] pairingPointObject;
443
+ // ZK: Gemini masking polynomial commitment (sent first, right after public inputs)
444
+ G1Point geminiMaskingPoly;
438
445
  // Commitments to wire polynomials
439
446
  G1Point w1;
440
447
  G1Point w2;
@@ -450,11 +457,8 @@ library Honk {
450
457
  // Sumcheck
451
458
  Fr libraSum;
452
459
  Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH][CONST_PROOF_SIZE_LOG_N] sumcheckUnivariates;
453
- Fr[NUMBER_OF_ENTITIES] sumcheckEvaluations;
454
460
  Fr libraEvaluation;
455
- // ZK
456
- G1Point geminiMaskingPoly;
457
- Fr geminiMaskingEval;
461
+ Fr[NUMBER_OF_ENTITIES_ZK] sumcheckEvaluations; // Includes gemini_masking_poly eval at index 0 (first position)
458
462
  // Shplemini
459
463
  G1Point[CONST_PROOF_SIZE_LOG_N - 1] geminiFoldComms;
460
464
  Fr[CONST_PROOF_SIZE_LOG_N] geminiAEvaluations;
@@ -465,10 +469,11 @@ library Honk {
465
469
  }
466
470
 
467
471
  // ZKTranscript library to generate fiat shamir challenges, the ZK transcript only differest
472
+ /// forge-lint: disable-next-item(pascal-case-struct)
468
473
  struct ZKTranscript {
469
474
  // Oink
470
475
  Honk.RelationParameters relationParameters;
471
- Fr[NUMBER_OF_ALPHAS] alphas;
476
+ Fr[NUMBER_OF_ALPHAS] alphas; // Powers of alpha: [alpha, alpha^2, ..., alpha^(NUM_SUBRELATIONS-1)]
472
477
  Fr[CONST_PROOF_SIZE_LOG_N] gateChallenges;
473
478
  // Sumcheck
474
479
  Fr libraChallenge;
@@ -517,8 +522,9 @@ library ZKTranscriptLib {
517
522
 
518
523
  function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) {
519
524
  uint256 challengeU256 = uint256(Fr.unwrap(challenge));
520
- uint256 lo = challengeU256 & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
521
- uint256 hi = challengeU256 >> 128;
525
+ // Split into two equal 127-bit chunks (254/2)
526
+ uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits
527
+ uint256 hi = challengeU256 >> 127;
522
528
  first = FrLib.fromBytes32(bytes32(lo));
523
529
  second = FrLib.fromBytes32(bytes32(hi));
524
530
  }
@@ -541,7 +547,8 @@ library ZKTranscriptLib {
541
547
  uint256 vkHash,
542
548
  uint256 publicInputsSize
543
549
  ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) {
544
- bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 6);
550
+ // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6))
551
+ bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8);
545
552
  round0[0] = bytes32(vkHash);
546
553
 
547
554
  for (uint256 i = 0; i < publicInputsSize - PAIRING_POINTS_SIZE; i++) {
@@ -551,14 +558,18 @@ library ZKTranscriptLib {
551
558
  round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]);
552
559
  }
553
560
 
561
+ // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs)
562
+ round0[1 + publicInputsSize] = bytes32(proof.geminiMaskingPoly.x);
563
+ round0[1 + publicInputsSize + 1] = bytes32(proof.geminiMaskingPoly.y);
564
+
554
565
  // Create the first challenge
555
566
  // Note: w4 is added to the challenge later on
556
- round0[1 + publicInputsSize] = bytes32(proof.w1.x);
557
- round0[1 + publicInputsSize + 1] = bytes32(proof.w1.y);
558
- round0[1 + publicInputsSize + 2] = bytes32(proof.w2.x);
559
- round0[1 + publicInputsSize + 3] = bytes32(proof.w2.y);
560
- round0[1 + publicInputsSize + 4] = bytes32(proof.w3.x);
561
- round0[1 + publicInputsSize + 5] = bytes32(proof.w3.y);
567
+ round0[1 + publicInputsSize + 2] = bytes32(proof.w1.x);
568
+ round0[1 + publicInputsSize + 3] = bytes32(proof.w1.y);
569
+ round0[1 + publicInputsSize + 4] = bytes32(proof.w2.x);
570
+ round0[1 + publicInputsSize + 5] = bytes32(proof.w2.y);
571
+ round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x);
572
+ round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y);
562
573
 
563
574
  previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0)));
564
575
  (eta, etaTwo) = splitChallenge(previousChallenge);
@@ -653,28 +664,21 @@ library ZKTranscriptLib {
653
664
  nextPreviousChallenge = prevChallenge;
654
665
  }
655
666
 
656
- // We add Libra claimed eval + 3 comm + 1 more eval
667
+ // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient)
657
668
  function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) internal pure returns (Fr rho, Fr nextPreviousChallenge) {
658
- uint256[NUMBER_OF_ENTITIES + 9] memory rhoChallengeElements;
669
+ uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements;
659
670
  rhoChallengeElements[0] = Fr.unwrap(prevChallenge);
660
671
  uint256 i;
661
- for (i = 1; i <= NUMBER_OF_ENTITIES; i++) {
672
+ for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) {
662
673
  rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]);
663
674
  }
664
675
  rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation);
665
-
666
676
  i += 1;
667
677
  rhoChallengeElements[i] = proof.libraCommitments[1].x;
668
678
  rhoChallengeElements[i + 1] = proof.libraCommitments[1].y;
669
679
  i += 2;
670
680
  rhoChallengeElements[i] = proof.libraCommitments[2].x;
671
681
  rhoChallengeElements[i + 1] = proof.libraCommitments[2].y;
672
- i += 2;
673
- rhoChallengeElements[i] = proof.geminiMaskingPoly.x;
674
- rhoChallengeElements[i + 1] = proof.geminiMaskingPoly.y;
675
-
676
- i += 2;
677
- rhoChallengeElements[i] = Fr.unwrap(proof.geminiMaskingEval);
678
682
 
679
683
  nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements)));
680
684
  (rho, ) = splitChallenge(nextPreviousChallenge);
@@ -742,6 +746,11 @@ library ZKTranscriptLib {
742
746
  p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]);
743
747
  boundary += FIELD_ELEMENT_SIZE;
744
748
  }
749
+
750
+ // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points)
751
+ p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
752
+ boundary += GROUP_ELEMENT_SIZE;
753
+
745
754
  // Commitments
746
755
  p.w1 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
747
756
  boundary += GROUP_ELEMENT_SIZE;
@@ -774,8 +783,8 @@ library ZKTranscriptLib {
774
783
  }
775
784
  }
776
785
 
777
- // Sumcheck evaluations
778
- for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) {
786
+ // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors)
787
+ for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) {
779
788
  p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]);
780
789
  boundary += FIELD_ELEMENT_SIZE;
781
790
  }
@@ -787,10 +796,6 @@ library ZKTranscriptLib {
787
796
  boundary += GROUP_ELEMENT_SIZE;
788
797
  p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
789
798
  boundary += GROUP_ELEMENT_SIZE;
790
- p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
791
- boundary += GROUP_ELEMENT_SIZE;
792
- p.geminiMaskingEval = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]);
793
- boundary += FIELD_ELEMENT_SIZE;
794
799
 
795
800
  // Gemini
796
801
  // Read gemini fold univariates
@@ -826,7 +831,7 @@ library RelationsLib {
826
831
  function accumulateRelationEvaluations(
827
832
  Fr[NUMBER_OF_ENTITIES] memory purportedEvaluations,
828
833
  Honk.RelationParameters memory rp,
829
- Fr[NUMBER_OF_ALPHAS] memory alphas,
834
+ Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges,
830
835
  Fr powPartialEval
831
836
  ) internal pure returns (Fr accumulator) {
832
837
  Fr[NUMBER_OF_SUBRELATIONS] memory evaluations;
@@ -842,8 +847,8 @@ library RelationsLib {
842
847
  accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval);
843
848
  accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval);
844
849
 
845
- // batch the subrelations with the alpha challenges to obtain the full honk relation
846
- accumulator = scaleAndBatchSubrelations(evaluations, alphas);
850
+ // batch the subrelations with the precomputed alpha powers to obtain the full honk relation
851
+ accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges);
847
852
  }
848
853
 
849
854
  /**
@@ -1520,6 +1525,8 @@ library RelationsLib {
1520
1525
  evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT));
1521
1526
  }
1522
1527
 
1528
+ // Batch subrelation evaluations using precomputed powers of alpha
1529
+ // First subrelation is implicitly scaled by 1, subsequent ones use powers from the subrelationChallenges array
1523
1530
  function scaleAndBatchSubrelations(
1524
1531
  Fr[NUMBER_OF_SUBRELATIONS] memory evaluations,
1525
1532
  Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges
@@ -1868,12 +1875,14 @@ abstract contract BaseZKHonkVerifier is IVerifier {
1868
1875
  uint256 immutable $LOG_N;
1869
1876
  uint256 immutable $VK_HASH;
1870
1877
  uint256 immutable $NUM_PUBLIC_INPUTS;
1878
+ uint256 immutable $MSMSize;
1871
1879
 
1872
1880
  constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) {
1873
1881
  $N = _N;
1874
1882
  $LOG_N = _logN;
1875
1883
  $VK_HASH = _vkHash;
1876
1884
  $NUM_PUBLIC_INPUTS = _numPublicInputs;
1885
+ $MSMSize = NUMBER_UNSHIFTED_ZK + _logN + LIBRA_COMMITMENTS + 2;
1877
1886
  }
1878
1887
 
1879
1888
  // Errors
@@ -1886,7 +1895,7 @@ abstract contract BaseZKHonkVerifier is IVerifier {
1886
1895
  error ConsistencyCheckFailed();
1887
1896
 
1888
1897
  // Constants for proof length calculation (matching UltraKeccakZKFlavor)
1889
- uint256 constant NUM_WITNESS_ENTITIES = 8;
1898
+ uint256 constant NUM_WITNESS_ENTITIES = 8 + NUM_MASKING_POLYNOMIALS;
1890
1899
  uint256 constant NUM_ELEMENTS_COMM = 2; // uint256 elements for curve points
1891
1900
  uint256 constant NUM_ELEMENTS_FR = 1; // uint256 elements for field elements
1892
1901
  uint256 constant NUM_LIBRA_EVALUATIONS = 4; // libra evaluations
@@ -1895,14 +1904,14 @@ abstract contract BaseZKHonkVerifier is IVerifier {
1895
1904
  function calculateProofSize(uint256 logN) internal pure returns (uint256) {
1896
1905
  // Witness and Libra commitments
1897
1906
  uint256 proofLength = NUM_WITNESS_ENTITIES * NUM_ELEMENTS_COMM; // witness commitments
1898
- proofLength += NUM_ELEMENTS_COMM * 4; // Libra concat, grand sum, quotient comms + Gemini masking
1907
+ proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking
1899
1908
 
1900
1909
  // Sumcheck
1901
1910
  proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates
1902
- proofLength += NUMBER_OF_ENTITIES * NUM_ELEMENTS_FR; // sumcheck evaluations
1911
+ proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations
1903
1912
 
1904
1913
  // Libra and Gemini
1905
- proofLength += NUM_ELEMENTS_FR * 3; // Libra sum, claimed eval, Gemini masking eval
1914
+ proofLength += NUM_ELEMENTS_FR * 2; // Libra sum, claimed eval
1906
1915
  proofLength += logN * NUM_ELEMENTS_FR; // Gemini a evaluations
1907
1916
  proofLength += NUM_LIBRA_EVALUATIONS * NUM_ELEMENTS_FR; // libra evaluations
1908
1917
 
@@ -2015,8 +2024,14 @@ abstract contract BaseZKHonkVerifier is IVerifier {
2015
2024
  }
2016
2025
 
2017
2026
  // Last round
2027
+ // For ZK flavors: sumcheckEvaluations has 42 elements
2028
+ // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations
2029
+ Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations;
2030
+ for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) {
2031
+ relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0
2032
+ }
2018
2033
  Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations(
2019
- proof.sumcheckEvaluations,
2034
+ relationsEvaluations,
2020
2035
  tp.relationParameters,
2021
2036
  tp.alphas,
2022
2037
  powPartialEvaluation
@@ -2089,8 +2104,8 @@ abstract contract BaseZKHonkVerifier is IVerifier {
2089
2104
  // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size
2090
2105
  Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N);
2091
2106
  // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings
2092
- Fr[] memory scalars = new Fr[](NUMBER_UNSHIFTED + $LOG_N + LIBRA_COMMITMENTS + 3);
2093
- Honk.G1Point[] memory commitments = new Honk.G1Point[](NUMBER_UNSHIFTED + $LOG_N + LIBRA_COMMITMENTS + 3);
2107
+ Fr[] memory scalars = new Fr[]($MSMSize);
2108
+ Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize);
2094
2109
 
2095
2110
  mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert();
2096
2111
  mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert();
@@ -2128,15 +2143,18 @@ abstract contract BaseZKHonkVerifier is IVerifier {
2128
2143
  * This approach minimizes the number of iterations over the commitments to multilinear polynomials
2129
2144
  * and eliminates the need to store the powers of \f$ \rho \f$.
2130
2145
  */
2131
- mem.batchedEvaluation = proof.geminiMaskingEval;
2132
- mem.batchingChallenge = tp.rho;
2146
+ // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...]
2147
+ // Start batching challenge at 1, not rho, to match non-ZK pattern
2148
+ mem.batchingChallenge = Fr.wrap(1);
2149
+ mem.batchedEvaluation = Fr.wrap(0);
2150
+
2133
2151
  mem.unshiftedScalarNeg = mem.unshiftedScalar.neg();
2134
2152
  mem.shiftedScalarNeg = mem.shiftedScalar.neg();
2135
2153
 
2136
- scalars[1] = mem.unshiftedScalarNeg;
2137
- for (uint256 i = 0; i < NUMBER_UNSHIFTED; ++i) {
2138
- scalars[i + 2] = mem.unshiftedScalarNeg * mem.batchingChallenge;
2139
- mem.batchedEvaluation = mem.batchedEvaluation + (proof.sumcheckEvaluations[i] * mem.batchingChallenge);
2154
+ // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0)
2155
+ for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) {
2156
+ scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge;
2157
+ mem.batchedEvaluation = mem.batchedEvaluation + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge);
2140
2158
  mem.batchingChallenge = mem.batchingChallenge * tp.rho;
2141
2159
  }
2142
2160
  // g commitments are accumulated at r
@@ -2147,7 +2165,7 @@ abstract contract BaseZKHonkVerifier is IVerifier {
2147
2165
  // Applied to w1, w2, w3, w4 and zPerm
2148
2166
  for (uint256 i = 0; i < NUMBER_TO_BE_SHIFTED; ++i) {
2149
2167
  uint256 scalarOff = i + SHIFTED_COMMITMENTS_START;
2150
- uint256 evaluationOff = i + NUMBER_UNSHIFTED;
2168
+ uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK;
2151
2169
 
2152
2170
  scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge);
2153
2171
  mem.batchedEvaluation = mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge);
@@ -2232,7 +2250,7 @@ abstract contract BaseZKHonkVerifier is IVerifier {
2232
2250
  mem.constantTermAccumulator = mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator);
2233
2251
 
2234
2252
  mem.batchingChallenge = tp.shplonkNu.sqr();
2235
- uint256 boundary = NUMBER_UNSHIFTED + 2;
2253
+ uint256 boundary = NUMBER_UNSHIFTED_ZK + 1;
2236
2254
 
2237
2255
  // Compute Shplonk constant term contributions from Aₗ(± r^{2ˡ}) for l = 1, ..., m-1;
2238
2256
  // Compute scalar multipliers for each fold commitment
@@ -2374,7 +2392,7 @@ abstract contract BaseZKHonkVerifier is IVerifier {
2374
2392
 
2375
2393
  // This implementation is the same as above with different constants
2376
2394
  function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) internal view returns (Honk.G1Point memory result) {
2377
- uint256 limit = NUMBER_UNSHIFTED + $LOG_N + LIBRA_COMMITMENTS + 3;
2395
+ uint256 limit = $MSMSize;
2378
2396
 
2379
2397
  // Validate all points are on the curve
2380
2398
  for (uint256 i = 0; i < limit; ++i) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crisp-e3/contracts",
3
- "version": "0.5.12",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "contracts",
@@ -31,7 +31,7 @@
31
31
  "@zk-kit/lazy-imt.sol": "2.0.0-beta.12",
32
32
  "poseidon-solidity": "^0.0.5",
33
33
  "solady": "^0.1.13",
34
- "@enclave-e3/contracts": "0.1.14"
34
+ "@enclave-e3/contracts": "0.1.15"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@nomicfoundation/hardhat-ethers": "4",
@@ -59,8 +59,8 @@
59
59
  "typechain": "^8.3.0",
60
60
  "typescript": "5.8.3",
61
61
  "viem": "2.30.6",
62
- "@crisp-e3/zk-inputs": "^0.5.12",
63
- "@crisp-e3/sdk": "^0.5.12"
62
+ "@crisp-e3/sdk": "^0.6.0",
63
+ "@crisp-e3/zk-inputs": "^0.6.0"
64
64
  },
65
65
  "scripts": {
66
66
  "compile": "hardhat compile",