@crisp-e3/contracts 0.5.11 → 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.
@@ -5,148 +5,148 @@
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 = 0x2349c3d182cb0307ecfa17d2cb91dcf9e7742a8daa67aa106831c761dd22030d;
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
- function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) {
14
- Honk.VerificationKey memory vk = Honk.VerificationKey({
15
- circuitSize: uint256(524288),
16
- logCircuitSize: uint256(19),
17
- publicInputsSize: uint256(23),
18
- ql: Honk.G1Point({
19
- x: uint256(0x253c5186ca1b682f7d302cd73b85ea3447a5a2c64fc82b70652f7fc44b203128),
20
- y: uint256(0x14f602413e183c57d3d3124290ab48b6c8b0cbace773766a573ead30bf5c2f4f)
21
- }),
22
- qr: Honk.G1Point({
23
- x: uint256(0x2d20633d112c7c7efd2821f2860e9d1287f8f68ddd4b0216bb179078497a571c),
24
- y: uint256(0x11928235f13ba529b1ee2f75fe26dda5ed5c79ec36028c09ad2bd8b957546344)
25
- }),
26
- qo: Honk.G1Point({
27
- x: uint256(0x2b570f8e2eccc265f39b9e73778cf6e282554452adb19d2a2406952447d4aa6e),
28
- y: uint256(0x14afd678c64e0dacd64724504e36bca6eccb5a477a5e2987b6543246b8135a90)
29
- }),
30
- q4: Honk.G1Point({
31
- x: uint256(0x2d7f53a245e9c855325eeb73c8fea788f5b457293cacfd7e3e332210a27a7d2f),
32
- y: uint256(0x182d5ef8840426caa829856d7e800ee98509383d9ee637d1facbc11f7a8fba0d)
33
- }),
34
- qm: Honk.G1Point({
35
- x: uint256(0x19d90118c106191d2f2f0350a56547c943c1ed5910d47dd067761099b54d11f3),
36
- y: uint256(0x0f8f03c2def6108448f72f5177b6a2eed01250670efb63ea6f5ab65abc2a6885)
37
- }),
38
- qc: Honk.G1Point({
39
- x: uint256(0x2ead500c79e717f5cdbba88ffef46477c4f30926bf59547a9c141c4158f7c843),
40
- y: uint256(0x027d6b571b395cf61aae9626f172ce4e41e598456f7b8e535c027e9129b2c98e)
41
- }),
42
- qLookup: Honk.G1Point({
43
- x: uint256(0x136236e1bfc2af648ac078e134c1b4b9114b11937ebafcdd87f8ca7660715ebb),
44
- y: uint256(0x02293c705250462935a653b7b993e13e2e8bc6480c45c84976d526cbdbd071df)
45
- }),
46
- qArith: Honk.G1Point({
47
- x: uint256(0x074e7ead679c76d2c2d6d3f158cb522e71d14a169b2b1d0792cdfdad726f17a4),
48
- y: uint256(0x21f9acd78d4150858f29c259c07e4552f6ec4bffac6c1e174cfb90e2e26bd68a)
49
- }),
50
- qDeltaRange: Honk.G1Point({
51
- x: uint256(0x0cebfba75751a4eff3d7855bf0f314e523e2c709a75992eb5674a546627248e3),
52
- y: uint256(0x16b715150f4de399940c535932174f5c634c9fd9137a94008bb38f89f1350b4c)
53
- }),
54
- qElliptic: Honk.G1Point({
55
- x: uint256(0x174dba7fa4e38a55a78e43dda6c3fb81a1293285cf33eeb333ee186bb4331563),
56
- y: uint256(0x0a2ff722359e35596e5abeec834c6cd73f4b06f5f2e837b31364f199f449ca77)
57
- }),
58
- qMemory: Honk.G1Point({
59
- x: uint256(0x29f116092db8eb3773b017110ca5d3a7b35fa1b064cde91c9e280e20399d7ec2),
60
- y: uint256(0x1f0e39e18ded75167a270d6e31b9a6ad9a9d1b0d67c365eeb172a3fea4d1371e)
61
- }),
62
- qNnf: Honk.G1Point({
63
- x: uint256(0x19da50fca27dd36a006892b7cb6b36d7cfdac9d65fec6ae7a1a2e092e0988de5),
64
- y: uint256(0x08f96f1d8e37be6dc83c9fbb3b912493841e4e81ab28a60c0fc85337cc4ed977)
65
- }),
66
- qPoseidon2External: Honk.G1Point({
67
- x: uint256(0x2d20446a333063e66c1a552fecdfe12d0fdddeef0034569ac350f3299b09955a),
68
- y: uint256(0x175e6e8776625134217ce9516a11cbbeab8c4c1c8b41cfb75f43de1f500b07d2)
69
- }),
70
- qPoseidon2Internal: Honk.G1Point({
71
- x: uint256(0x090a76cca3c754a2ba40abdc2572fff64645060cfa5c48239a1df4dbb8360828),
72
- y: uint256(0x0499d4c6aed1f78b33a6701459cd069e9bf7262754d2f15074a6c2401950761f)
73
- }),
74
- s1: Honk.G1Point({
75
- x: uint256(0x012609fcd0fa67214d78f2d5f5e3e2b459a3c4b21531646de7d51d9cd5383aa9),
76
- y: uint256(0x21394cebbe5f66f3aecf43d318b3cb9fc7640825baba0c5a5c190a20ceeb5edb)
77
- }),
78
- s2: Honk.G1Point({
79
- x: uint256(0x080c7f024d9c813c60c84fc3e9bcad553c51f276d55e8d7a03c044b41cedef36),
80
- y: uint256(0x2109db10b2da84ec2c4a1b62689d0952ababe94915293dfee09f1083010e5cfb)
81
- }),
82
- s3: Honk.G1Point({
83
- x: uint256(0x2f7d6b77cd5c3ee56c255be61d0e90fd7da5898a7ce4850aaf1060513cb7cd9d),
84
- y: uint256(0x2ce5fdacbf91fc3358f4eba637d60f109f70fa929a6a1bc1f9d86e7856b87dcd)
85
- }),
86
- s4: Honk.G1Point({
87
- x: uint256(0x1db1540beb4dc13e8519b82205e9b4cb48833040c8b322d626df57e078afecc4),
88
- y: uint256(0x28eb12f9f02ac092326277365f9eeaff536b8dbf771f9ace22d0e122f922196a)
89
- }),
90
- t1: Honk.G1Point({
91
- x: uint256(0x1f16b037f0b4c96ea2a30a118a44e139881c0db8a4d6c9fde7db5c1c1738e61f),
92
- y: uint256(0x00c7781bda34afc32dedfb0d0f6b16c987c83375da000e180b44ad25685fc2ae)
93
- }),
94
- t2: Honk.G1Point({
95
- x: uint256(0x29345f914a28707887bee191c3a928191de584827a6d1a78ccce1d7629ca9dc0),
96
- y: uint256(0x1920cebd0b33ac9713424e3bc03d53d79dc72f6afc24c90e56593094a213444c)
97
- }),
98
- t3: Honk.G1Point({
99
- x: uint256(0x261c990958bc2ef77a45467d9639ab2c68cf787ff7bce55ce3074dfdaedc8f8f),
100
- y: uint256(0x23c1c05424a40360a61e46f4deab04988a6f5b71dda351e0da608cff1f332ee0)
101
- }),
102
- t4: Honk.G1Point({
103
- x: uint256(0x2b651d2fd644b2972d72ec439dc69d3339d0b052a296bfc48c6a08396aaca078),
104
- y: uint256(0x2d7e8c1ecb92e2490049b50efc811df63f1ca97e58d5e82852dbec0c29715d71)
105
- }),
106
- id1: Honk.G1Point({
107
- x: uint256(0x0a10b2e79989b15e3a69bd491cdae007b0bf9c82d9be3d7867b33e2287b91dac),
108
- y: uint256(0x257794eaba7a0e7aed16e03d4d8c4cf7d878b029f4ea45c559bbc19b5ec4d1de)
109
- }),
110
- id2: Honk.G1Point({
111
- x: uint256(0x25791b725ea7c712316ac4ffe10fdfcf37bd2b8f9d730ba2e26fa709bc7c3ae0),
112
- y: uint256(0x15fba3e7928d36dc4dd6d6ff198b928c7246310974d4f74161cfd2781b9d3686)
113
- }),
114
- id3: Honk.G1Point({
115
- x: uint256(0x270be32427e6404801b9b014f983d80acf18b828c6cf049f430d98fbe34e85e0),
116
- y: uint256(0x2e7d27a99c4b574557ea6117c390c65072cdfa51a08621141ae26d7e143d0669)
117
- }),
118
- id4: Honk.G1Point({
119
- x: uint256(0x18e2902febdb3e45358fe65981f338a7ffd1b16edd917de670fabe5d15307e20),
120
- y: uint256(0x2f6f36f307a0f7012dc3918795312e71a1d4752966c2bc3151e5f5b3151fc236)
121
- }),
122
- lagrangeFirst: Honk.G1Point({
123
- x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001),
124
- y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002)
125
- }),
126
- lagrangeLast: Honk.G1Point({
127
- x: uint256(0x12b14f226e24a52e0bc85bc5478c806023107f257430aae49136064dd3315c60),
128
- y: uint256(0x0dfe490435cf839caf81df5c8a164afc5e3c8646f36bf27c19850b3f913edce4)
129
- })
130
- });
131
- return vk;
132
- }
13
+ function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) {
14
+ Honk.VerificationKey memory vk = Honk.VerificationKey({
15
+ circuitSize: uint256(2097152),
16
+ logCircuitSize: uint256(21),
17
+ publicInputsSize: uint256(55),
18
+ ql: Honk.G1Point({
19
+ x: uint256(0x0b49cca46309009bcfb42f7e72925f6b8b5f102e6b6ab8825dae8a96fe3cba34),
20
+ y: uint256(0x1e4d119e1991788b257fd6c8b9d701c6b4fb80dc24e6bb34489e2aa679e4369b)
21
+ }),
22
+ qr: Honk.G1Point({
23
+ x: uint256(0x23fcf389d77e9fb6e9f257b2a577311a62703d7746e077b8c4efe6a40bcadc1a),
24
+ y: uint256(0x257e7923fabec927d96159f14fcede129348203152597db67c4ec0fe392d3038)
25
+ }),
26
+ qo: Honk.G1Point({
27
+ x: uint256(0x125a3d99a31fa1f16585dfa287e82903828bb535c4b67895869f26d30b34a19b),
28
+ y: uint256(0x1a76e12e46934a8183e4d3983c40a12aa645054d272c5492aa48f933b216c957)
29
+ }),
30
+ q4: Honk.G1Point({
31
+ x: uint256(0x02717f10da28d42bd2096b5501b44c5e29a9d88ee76ad6b0e48b6d2b68dacf7d),
32
+ y: uint256(0x16e4f684dddc45d91078cef3af321dc5db6ba7ea9fc527a800a0e20e5ffbd9f3)
33
+ }),
34
+ qm: Honk.G1Point({
35
+ x: uint256(0x0fdda6f7325628ba94b4ae2e823a1d5a1dfe18fbd1c875850ff4d7e6e2bb738a),
36
+ y: uint256(0x1fcedd6727ab1f287d0a2c2e68ef08319e00352719055e9fb40a4292282e167f)
37
+ }),
38
+ qc: Honk.G1Point({
39
+ x: uint256(0x06422053a8c6503244e00acc1e275c3b86a2bd7139ae666d909453b766797de2),
40
+ y: uint256(0x000033f450d6a78d5841b7027366c1f40bf240dc5fbaf3a01e14aa5f047bb36c)
41
+ }),
42
+ qLookup: Honk.G1Point({
43
+ x: uint256(0x285cd2008c8e9d282963405be4ac4f7733be06e27d5706a518a5902d712cae6c),
44
+ y: uint256(0x2efd1ebb7f3ed30e0c96ae4d42b15d9e6cd18d906f198a67fb3cbb64c62b3cb5)
45
+ }),
46
+ qArith: Honk.G1Point({
47
+ x: uint256(0x0afc885f2b56f40755a8f66743425b589f8ac283920e112dc4725a503ed69e14),
48
+ y: uint256(0x0fc111111f23cb77fb579532a4c393fafc412a223f76743c98567d08a2ce8450)
49
+ }),
50
+ qDeltaRange: Honk.G1Point({
51
+ x: uint256(0x1968df000ac48429eb10b056a0a6dd3d1da8332b5de3724ab87547c3371ffc42),
52
+ y: uint256(0x18e9cb336ca060c58b7f6051d792fc40c0b488fd9672807e10cf10eab7cacb7a)
53
+ }),
54
+ qElliptic: Honk.G1Point({
55
+ x: uint256(0x177d1a59fab0f5281dfd02b19aa00eb945c77f91c5bd6112b8e857db9dc6b798),
56
+ y: uint256(0x11b2038670e6e114f87b1d472a31cea6390f5de495febdf47237a806969fae04)
57
+ }),
58
+ qMemory: Honk.G1Point({
59
+ x: uint256(0x02b0cec1f1b948b9e689b5b172eb85c8cd7b901c142e07df3fde5b9d74ca7414),
60
+ y: uint256(0x2d84255bc631bcb170192dd095b8785369495061366530ba5c843e7a97240be9)
61
+ }),
62
+ qNnf: Honk.G1Point({
63
+ x: uint256(0x053c8110bdde783ecb8ec60d71f1bd85a3bc8b43e1fe443c321a44d5692cdfc8),
64
+ y: uint256(0x177969870de5b24725a64587e0cf2258988960674a6ccc722133edb3fb6768aa)
65
+ }),
66
+ qPoseidon2External: Honk.G1Point({
67
+ x: uint256(0x087716c4e6b28e30afa0a4b0cf49a1ee4643709464ef89e708481e9fbcb3acf4),
68
+ y: uint256(0x081caf798962ce0724f2880fccebc8c2ee75ee6d0ea87d308fa91a97e4587afb)
69
+ }),
70
+ qPoseidon2Internal: Honk.G1Point({
71
+ x: uint256(0x1f7ab7d4ad925b2c31233ea29ba4debac639158de845f734d3d4340f84d72937),
72
+ y: uint256(0x2d228dbe4b4eb9baf6d62068cb766b7fb3b44a1a3cbde58a5c40c69ff6e176d8)
73
+ }),
74
+ s1: Honk.G1Point({
75
+ x: uint256(0x2bd432c0b486d1b9a75870a420e02622eeec72f59615c9532a22c2d946f8dc03),
76
+ y: uint256(0x1037cf6462b36a560256ea2c03306634ed4d87e76ea81052083700fbb1a5f8d4)
77
+ }),
78
+ s2: Honk.G1Point({
79
+ x: uint256(0x1359c3633ccf1c39969812967b170b2592f88dbec76079f0779c4ae17f79c432),
80
+ y: uint256(0x0e696f759fc6cd345418c89c3737d237bd2685d59b1843a884b2436f2a67fb97)
81
+ }),
82
+ s3: Honk.G1Point({
83
+ x: uint256(0x2201b7d5413f027286a8ecebfb33c3fdac7974d241e428f2ab549bcb6b1ec379),
84
+ y: uint256(0x1327e17ebdcc90f8233b838e4836974e68812dde890018ce5e59ba0bed8d4eb1)
85
+ }),
86
+ s4: Honk.G1Point({
87
+ x: uint256(0x2983325004b0f8ebfda26f1425a3fa7c780c9619a0a57b6026cebc3241233bb3),
88
+ y: uint256(0x21e0b11f18c587feb26a1b0984af1b928df573b57f6e1e7430892d02eebdaf56)
89
+ }),
90
+ t1: Honk.G1Point({
91
+ x: uint256(0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c),
92
+ y: uint256(0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579)
93
+ }),
94
+ t2: Honk.G1Point({
95
+ x: uint256(0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887),
96
+ y: uint256(0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8)
97
+ }),
98
+ t3: Honk.G1Point({
99
+ x: uint256(0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f),
100
+ y: uint256(0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7)
101
+ }),
102
+ t4: Honk.G1Point({
103
+ x: uint256(0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6),
104
+ y: uint256(0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea)
105
+ }),
106
+ id1: Honk.G1Point({
107
+ x: uint256(0x298881ca881438995e404581a83530c3d88dfa93485a6f29a882cdd2670e9593),
108
+ y: uint256(0x2cfd5e44e0a6979145879de24261c18defe4c22e7a7a23b20b002030e675c643)
109
+ }),
110
+ id2: Honk.G1Point({
111
+ x: uint256(0x02c16723d917f17d181a59379615e683306841760032f394cdb0b4f2903c0af7),
112
+ y: uint256(0x08d4e6f0f5f23b5431a424bd364a33e83009d3dce3214c92c22e033adb02f09c)
113
+ }),
114
+ id3: Honk.G1Point({
115
+ x: uint256(0x21dc5e14e79ab4bf1244065498cc521f001630dee5a631a0ace7cc03c12afc0c),
116
+ y: uint256(0x1fc77a1fc605ba6548e353b93411784ef9f79b966b676273acf37519b88a451f)
117
+ }),
118
+ id4: Honk.G1Point({
119
+ x: uint256(0x246bd4706d80d364c95e550e7e12816e13cf5736985becd309eab4503a6a5dec),
120
+ y: uint256(0x1453ed9448d07a67232a466d416620e9cb8438cc6c54225cb12acbde33d0a2a9)
121
+ }),
122
+ lagrangeFirst: Honk.G1Point({
123
+ x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001),
124
+ y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002)
125
+ }),
126
+ lagrangeLast: Honk.G1Point({
127
+ x: uint256(0x12541b65d3fa0ed265d6fa8823d60b50e7e245797410c733a497fe858346b8fd),
128
+ y: uint256(0x237bf222dbadaeb345852f0bf7e93c87f7bbf30b85cacbd349d95c0f76c4e318)
129
+ })
130
+ });
131
+ return vk;
132
+ }
133
133
  }
134
134
 
135
135
  pragma solidity ^0.8.27;
136
136
 
137
137
  interface IVerifier {
138
- function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool);
138
+ function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool);
139
139
  }
140
140
 
141
141
  type Fr is uint256;
142
142
 
143
- using {add as +} for Fr global;
144
- using {sub as -} for Fr global;
145
- using {mul as *} for Fr global;
143
+ using { add as + } for Fr global;
144
+ using { sub as - } for Fr global;
145
+ using { mul as * } for Fr global;
146
146
 
147
- using {exp as ^} for Fr global;
148
- using {notEqual as !=} for Fr global;
149
- using {equal as ==} for Fr global;
147
+ using { exp as ^ } for Fr global;
148
+ using { notEqual as != } for Fr global;
149
+ using { equal as == } for Fr global;
150
150
 
151
151
  uint256 constant SUBGROUP_SIZE = 256;
152
152
  uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order
@@ -159,135 +159,135 @@ Fr constant ZERO = Fr.wrap(0);
159
159
  // Instantiation
160
160
 
161
161
  library FrLib {
162
- function from(uint256 value) internal pure returns (Fr) {
163
- unchecked {
164
- return Fr.wrap(value % MODULUS);
165
- }
166
- }
167
-
168
- function fromBytes32(bytes32 value) internal pure returns (Fr) {
169
- unchecked {
170
- return Fr.wrap(uint256(value) % MODULUS);
171
- }
172
- }
173
-
174
- function toBytes32(Fr value) internal pure returns (bytes32) {
175
- unchecked {
176
- return bytes32(Fr.unwrap(value));
177
- }
178
- }
179
-
180
- function invert(Fr value) internal view returns (Fr) {
181
- uint256 v = Fr.unwrap(value);
182
- uint256 result;
183
-
184
- // Call the modexp precompile to invert in the field
185
- assembly {
186
- let free := mload(0x40)
187
- mstore(free, 0x20)
188
- mstore(add(free, 0x20), 0x20)
189
- mstore(add(free, 0x40), 0x20)
190
- mstore(add(free, 0x60), v)
191
- mstore(add(free, 0x80), sub(MODULUS, 2))
192
- mstore(add(free, 0xa0), MODULUS)
193
- let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20)
194
- if iszero(success) {
195
- revert(0, 0)
196
- }
197
- result := mload(0x00)
198
- mstore(0x40, add(free, 0x80))
199
- }
200
-
201
- return Fr.wrap(result);
202
- }
203
-
204
- function pow(Fr base, uint256 v) internal view returns (Fr) {
205
- uint256 b = Fr.unwrap(base);
206
- uint256 result;
207
-
208
- // Call the modexp precompile to invert in the field
209
- assembly {
210
- let free := mload(0x40)
211
- mstore(free, 0x20)
212
- mstore(add(free, 0x20), 0x20)
213
- mstore(add(free, 0x40), 0x20)
214
- mstore(add(free, 0x60), b)
215
- mstore(add(free, 0x80), v)
216
- mstore(add(free, 0xa0), MODULUS)
217
- let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20)
218
- if iszero(success) {
219
- revert(0, 0)
220
- }
221
- result := mload(0x00)
222
- mstore(0x40, add(free, 0x80))
223
- }
224
-
225
- return Fr.wrap(result);
226
- }
227
-
228
- function div(Fr numerator, Fr denominator) internal view returns (Fr) {
229
- unchecked {
230
- return numerator * invert(denominator);
231
- }
232
- }
233
-
234
- function sqr(Fr value) internal pure returns (Fr) {
235
- unchecked {
236
- return value * value;
237
- }
238
- }
239
-
240
- function unwrap(Fr value) internal pure returns (uint256) {
241
- unchecked {
242
- return Fr.unwrap(value);
243
- }
244
- }
245
-
246
- function neg(Fr value) internal pure returns (Fr) {
247
- unchecked {
248
- return Fr.wrap(MODULUS - Fr.unwrap(value));
249
- }
162
+ function from(uint256 value) internal pure returns (Fr) {
163
+ unchecked {
164
+ return Fr.wrap(value % MODULUS);
165
+ }
166
+ }
167
+
168
+ function fromBytes32(bytes32 value) internal pure returns (Fr) {
169
+ unchecked {
170
+ return Fr.wrap(uint256(value) % MODULUS);
171
+ }
172
+ }
173
+
174
+ function toBytes32(Fr value) internal pure returns (bytes32) {
175
+ unchecked {
176
+ return bytes32(Fr.unwrap(value));
177
+ }
178
+ }
179
+
180
+ function invert(Fr value) internal view returns (Fr) {
181
+ uint256 v = Fr.unwrap(value);
182
+ uint256 result;
183
+
184
+ // Call the modexp precompile to invert in the field
185
+ assembly {
186
+ let free := mload(0x40)
187
+ mstore(free, 0x20)
188
+ mstore(add(free, 0x20), 0x20)
189
+ mstore(add(free, 0x40), 0x20)
190
+ mstore(add(free, 0x60), v)
191
+ mstore(add(free, 0x80), sub(MODULUS, 2))
192
+ mstore(add(free, 0xa0), MODULUS)
193
+ let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20)
194
+ if iszero(success) {
195
+ revert(0, 0)
196
+ }
197
+ result := mload(0x00)
198
+ mstore(0x40, add(free, 0x80))
199
+ }
200
+
201
+ return Fr.wrap(result);
202
+ }
203
+
204
+ function pow(Fr base, uint256 v) internal view returns (Fr) {
205
+ uint256 b = Fr.unwrap(base);
206
+ uint256 result;
207
+
208
+ // Call the modexp precompile to invert in the field
209
+ assembly {
210
+ let free := mload(0x40)
211
+ mstore(free, 0x20)
212
+ mstore(add(free, 0x20), 0x20)
213
+ mstore(add(free, 0x40), 0x20)
214
+ mstore(add(free, 0x60), b)
215
+ mstore(add(free, 0x80), v)
216
+ mstore(add(free, 0xa0), MODULUS)
217
+ let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20)
218
+ if iszero(success) {
219
+ revert(0, 0)
220
+ }
221
+ result := mload(0x00)
222
+ mstore(0x40, add(free, 0x80))
223
+ }
224
+
225
+ return Fr.wrap(result);
226
+ }
227
+
228
+ function div(Fr numerator, Fr denominator) internal view returns (Fr) {
229
+ unchecked {
230
+ return numerator * invert(denominator);
250
231
  }
232
+ }
233
+
234
+ function sqr(Fr value) internal pure returns (Fr) {
235
+ unchecked {
236
+ return value * value;
237
+ }
238
+ }
239
+
240
+ function unwrap(Fr value) internal pure returns (uint256) {
241
+ unchecked {
242
+ return Fr.unwrap(value);
243
+ }
244
+ }
245
+
246
+ function neg(Fr value) internal pure returns (Fr) {
247
+ unchecked {
248
+ return Fr.wrap(MODULUS - Fr.unwrap(value));
249
+ }
250
+ }
251
251
  }
252
252
 
253
253
  // Free functions
254
254
  function add(Fr a, Fr b) pure returns (Fr) {
255
- unchecked {
256
- return Fr.wrap(addmod(Fr.unwrap(a), Fr.unwrap(b), MODULUS));
257
- }
255
+ unchecked {
256
+ return Fr.wrap(addmod(Fr.unwrap(a), Fr.unwrap(b), MODULUS));
257
+ }
258
258
  }
259
259
 
260
260
  function mul(Fr a, Fr b) pure returns (Fr) {
261
- unchecked {
262
- return Fr.wrap(mulmod(Fr.unwrap(a), Fr.unwrap(b), MODULUS));
263
- }
261
+ unchecked {
262
+ return Fr.wrap(mulmod(Fr.unwrap(a), Fr.unwrap(b), MODULUS));
263
+ }
264
264
  }
265
265
 
266
266
  function sub(Fr a, Fr b) pure returns (Fr) {
267
- unchecked {
268
- return Fr.wrap(addmod(Fr.unwrap(a), MODULUS - Fr.unwrap(b), MODULUS));
269
- }
267
+ unchecked {
268
+ return Fr.wrap(addmod(Fr.unwrap(a), MODULUS - Fr.unwrap(b), MODULUS));
269
+ }
270
270
  }
271
271
 
272
272
  function exp(Fr base, Fr exponent) pure returns (Fr) {
273
- if (Fr.unwrap(exponent) == 0) return Fr.wrap(1);
274
- // Implement exponent with a loop as we will overflow otherwise
275
- for (uint256 i = 1; i < Fr.unwrap(exponent); i += i) {
276
- base = base * base;
277
- }
278
- return base;
273
+ if (Fr.unwrap(exponent) == 0) return Fr.wrap(1);
274
+ // Implement exponent with a loop as we will overflow otherwise
275
+ for (uint256 i = 1; i < Fr.unwrap(exponent); i += i) {
276
+ base = base * base;
277
+ }
278
+ return base;
279
279
  }
280
280
 
281
281
  function notEqual(Fr a, Fr b) pure returns (bool) {
282
- unchecked {
283
- return Fr.unwrap(a) != Fr.unwrap(b);
284
- }
282
+ unchecked {
283
+ return Fr.unwrap(a) != Fr.unwrap(b);
284
+ }
285
285
  }
286
286
 
287
287
  function equal(Fr a, Fr b) pure returns (bool) {
288
- unchecked {
289
- return Fr.unwrap(a) == Fr.unwrap(b);
290
- }
288
+ unchecked {
289
+ return Fr.unwrap(a) == Fr.unwrap(b);
290
+ }
291
291
  }
292
292
 
293
293
  uint256 constant CONST_PROOF_SIZE_LOG_N = 28;
@@ -296,1344 +296,1344 @@ 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
310
314
  enum WIRE {
311
- Q_M,
312
- Q_C,
313
- Q_L,
314
- Q_R,
315
- Q_O,
316
- Q_4,
317
- Q_LOOKUP,
318
- Q_ARITH,
319
- Q_RANGE,
320
- Q_ELLIPTIC,
321
- Q_MEMORY,
322
- Q_NNF,
323
- Q_POSEIDON2_EXTERNAL,
324
- Q_POSEIDON2_INTERNAL,
325
- SIGMA_1,
326
- SIGMA_2,
327
- SIGMA_3,
328
- SIGMA_4,
329
- ID_1,
330
- ID_2,
331
- ID_3,
332
- ID_4,
333
- TABLE_1,
334
- TABLE_2,
335
- TABLE_3,
336
- TABLE_4,
337
- LAGRANGE_FIRST,
338
- LAGRANGE_LAST,
339
- W_L,
340
- W_R,
341
- W_O,
342
- W_4,
343
- Z_PERM,
344
- LOOKUP_INVERSES,
345
- LOOKUP_READ_COUNTS,
346
- LOOKUP_READ_TAGS,
347
- W_L_SHIFT,
348
- W_R_SHIFT,
349
- W_O_SHIFT,
350
- W_4_SHIFT,
351
- Z_PERM_SHIFT
315
+ Q_M,
316
+ Q_C,
317
+ Q_L,
318
+ Q_R,
319
+ Q_O,
320
+ Q_4,
321
+ Q_LOOKUP,
322
+ Q_ARITH,
323
+ Q_RANGE,
324
+ Q_ELLIPTIC,
325
+ Q_MEMORY,
326
+ Q_NNF,
327
+ Q_POSEIDON2_EXTERNAL,
328
+ Q_POSEIDON2_INTERNAL,
329
+ SIGMA_1,
330
+ SIGMA_2,
331
+ SIGMA_3,
332
+ SIGMA_4,
333
+ ID_1,
334
+ ID_2,
335
+ ID_3,
336
+ ID_4,
337
+ TABLE_1,
338
+ TABLE_2,
339
+ TABLE_3,
340
+ TABLE_4,
341
+ LAGRANGE_FIRST,
342
+ LAGRANGE_LAST,
343
+ W_L,
344
+ W_R,
345
+ W_O,
346
+ W_4,
347
+ Z_PERM,
348
+ LOOKUP_INVERSES,
349
+ LOOKUP_READ_COUNTS,
350
+ LOOKUP_READ_TAGS,
351
+ W_L_SHIFT,
352
+ W_R_SHIFT,
353
+ W_O_SHIFT,
354
+ W_4_SHIFT,
355
+ Z_PERM_SHIFT
352
356
  }
353
357
 
354
358
  library Honk {
355
- struct G1Point {
356
- uint256 x;
357
- uint256 y;
358
- }
359
-
360
- struct VerificationKey {
361
- // Misc Params
362
- uint256 circuitSize;
363
- uint256 logCircuitSize;
364
- uint256 publicInputsSize;
365
- // Selectors
366
- G1Point qm;
367
- G1Point qc;
368
- G1Point ql;
369
- G1Point qr;
370
- G1Point qo;
371
- G1Point q4;
372
- G1Point qLookup; // Lookup
373
- G1Point qArith; // Arithmetic widget
374
- G1Point qDeltaRange; // Delta Range sort
375
- G1Point qMemory; // Memory
376
- G1Point qNnf; // Non-native Field
377
- G1Point qElliptic; // Auxillary
378
- G1Point qPoseidon2External;
379
- G1Point qPoseidon2Internal;
380
- // Copy cnstraints
381
- G1Point s1;
382
- G1Point s2;
383
- G1Point s3;
384
- G1Point s4;
385
- // Copy identity
386
- G1Point id1;
387
- G1Point id2;
388
- G1Point id3;
389
- G1Point id4;
390
- // Precomputed lookup table
391
- G1Point t1;
392
- G1Point t2;
393
- G1Point t3;
394
- G1Point t4;
395
- // Fixed first and last
396
- G1Point lagrangeFirst;
397
- G1Point lagrangeLast;
398
- }
399
-
400
- struct RelationParameters {
401
- // challenges
402
- Fr eta;
403
- Fr etaTwo;
404
- Fr etaThree;
405
- Fr beta;
406
- Fr gamma;
407
- // derived
408
- Fr publicInputsDelta;
409
- }
410
-
411
- struct Proof {
412
- // Pairing point object
413
- Fr[PAIRING_POINTS_SIZE] pairingPointObject;
414
- // Free wires
415
- G1Point w1;
416
- G1Point w2;
417
- G1Point w3;
418
- G1Point w4;
419
- // Lookup helpers - Permutations
420
- G1Point zPerm;
421
- // Lookup helpers - logup
422
- G1Point lookupReadCounts;
423
- G1Point lookupReadTags;
424
- G1Point lookupInverses;
425
- // Sumcheck
426
- Fr[BATCHED_RELATION_PARTIAL_LENGTH][CONST_PROOF_SIZE_LOG_N] sumcheckUnivariates;
427
- Fr[NUMBER_OF_ENTITIES] sumcheckEvaluations;
428
- // Shplemini
429
- G1Point[CONST_PROOF_SIZE_LOG_N - 1] geminiFoldComms;
430
- Fr[CONST_PROOF_SIZE_LOG_N] geminiAEvaluations;
431
- G1Point shplonkQ;
432
- G1Point kzgQuotient;
433
- }
434
-
435
- struct ZKProof {
436
- // Pairing point object
437
- Fr[PAIRING_POINTS_SIZE] pairingPointObject;
438
- // Commitments to wire polynomials
439
- G1Point w1;
440
- G1Point w2;
441
- G1Point w3;
442
- G1Point w4;
443
- // Commitments to logup witness polynomials
444
- G1Point lookupReadCounts;
445
- G1Point lookupReadTags;
446
- G1Point lookupInverses;
447
- // Commitment to grand permutation polynomial
448
- G1Point zPerm;
449
- G1Point[3] libraCommitments;
450
- // Sumcheck
451
- Fr libraSum;
452
- Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH][CONST_PROOF_SIZE_LOG_N] sumcheckUnivariates;
453
- Fr[NUMBER_OF_ENTITIES] sumcheckEvaluations;
454
- Fr libraEvaluation;
455
- // ZK
456
- G1Point geminiMaskingPoly;
457
- Fr geminiMaskingEval;
458
- // Shplemini
459
- G1Point[CONST_PROOF_SIZE_LOG_N - 1] geminiFoldComms;
460
- Fr[CONST_PROOF_SIZE_LOG_N] geminiAEvaluations;
461
- Fr[4] libraPolyEvals;
462
- G1Point shplonkQ;
463
- G1Point kzgQuotient;
464
- }
359
+ struct G1Point {
360
+ uint256 x;
361
+ uint256 y;
362
+ }
363
+
364
+ struct VerificationKey {
365
+ // Misc Params
366
+ uint256 circuitSize;
367
+ uint256 logCircuitSize;
368
+ uint256 publicInputsSize;
369
+ // Selectors
370
+ G1Point qm;
371
+ G1Point qc;
372
+ G1Point ql;
373
+ G1Point qr;
374
+ G1Point qo;
375
+ G1Point q4;
376
+ G1Point qLookup; // Lookup
377
+ G1Point qArith; // Arithmetic widget
378
+ G1Point qDeltaRange; // Delta Range sort
379
+ G1Point qMemory; // Memory
380
+ G1Point qNnf; // Non-native Field
381
+ G1Point qElliptic; // Auxillary
382
+ G1Point qPoseidon2External;
383
+ G1Point qPoseidon2Internal;
384
+ // Copy constraints
385
+ G1Point s1;
386
+ G1Point s2;
387
+ G1Point s3;
388
+ G1Point s4;
389
+ // Copy identity
390
+ G1Point id1;
391
+ G1Point id2;
392
+ G1Point id3;
393
+ G1Point id4;
394
+ // Precomputed lookup table
395
+ G1Point t1;
396
+ G1Point t2;
397
+ G1Point t3;
398
+ G1Point t4;
399
+ // Fixed first and last
400
+ G1Point lagrangeFirst;
401
+ G1Point lagrangeLast;
402
+ }
403
+
404
+ struct RelationParameters {
405
+ // challenges
406
+ Fr eta;
407
+ Fr etaTwo;
408
+ Fr etaThree;
409
+ Fr beta;
410
+ Fr gamma;
411
+ // derived
412
+ Fr publicInputsDelta;
413
+ }
414
+
415
+ struct Proof {
416
+ // Pairing point object
417
+ Fr[PAIRING_POINTS_SIZE] pairingPointObject;
418
+ // Free wires
419
+ G1Point w1;
420
+ G1Point w2;
421
+ G1Point w3;
422
+ G1Point w4;
423
+ // Lookup helpers - Permutations
424
+ G1Point zPerm;
425
+ // Lookup helpers - logup
426
+ G1Point lookupReadCounts;
427
+ G1Point lookupReadTags;
428
+ G1Point lookupInverses;
429
+ // Sumcheck
430
+ Fr[BATCHED_RELATION_PARTIAL_LENGTH][CONST_PROOF_SIZE_LOG_N] sumcheckUnivariates;
431
+ Fr[NUMBER_OF_ENTITIES] sumcheckEvaluations;
432
+ // Shplemini
433
+ G1Point[CONST_PROOF_SIZE_LOG_N - 1] geminiFoldComms;
434
+ Fr[CONST_PROOF_SIZE_LOG_N] geminiAEvaluations;
435
+ G1Point shplonkQ;
436
+ G1Point kzgQuotient;
437
+ }
438
+
439
+ /// forge-lint: disable-next-item(pascal-case-struct)
440
+ struct ZKProof {
441
+ // Pairing point object
442
+ Fr[PAIRING_POINTS_SIZE] pairingPointObject;
443
+ // ZK: Gemini masking polynomial commitment (sent first, right after public inputs)
444
+ G1Point geminiMaskingPoly;
445
+ // Commitments to wire polynomials
446
+ G1Point w1;
447
+ G1Point w2;
448
+ G1Point w3;
449
+ G1Point w4;
450
+ // Commitments to logup witness polynomials
451
+ G1Point lookupReadCounts;
452
+ G1Point lookupReadTags;
453
+ G1Point lookupInverses;
454
+ // Commitment to grand permutation polynomial
455
+ G1Point zPerm;
456
+ G1Point[3] libraCommitments;
457
+ // Sumcheck
458
+ Fr libraSum;
459
+ Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH][CONST_PROOF_SIZE_LOG_N] sumcheckUnivariates;
460
+ Fr libraEvaluation;
461
+ Fr[NUMBER_OF_ENTITIES_ZK] sumcheckEvaluations; // Includes gemini_masking_poly eval at index 0 (first position)
462
+ // Shplemini
463
+ G1Point[CONST_PROOF_SIZE_LOG_N - 1] geminiFoldComms;
464
+ Fr[CONST_PROOF_SIZE_LOG_N] geminiAEvaluations;
465
+ Fr[4] libraPolyEvals;
466
+ G1Point shplonkQ;
467
+ G1Point kzgQuotient;
468
+ }
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
- // Oink
470
- Honk.RelationParameters relationParameters;
471
- Fr[NUMBER_OF_ALPHAS] alphas;
472
- Fr[CONST_PROOF_SIZE_LOG_N] gateChallenges;
473
- // Sumcheck
474
- Fr libraChallenge;
475
- Fr[CONST_PROOF_SIZE_LOG_N] sumCheckUChallenges;
476
- // Shplemini
477
- Fr rho;
478
- Fr geminiR;
479
- Fr shplonkNu;
480
- Fr shplonkZ;
481
- // Derived
482
- Fr publicInputsDelta;
474
+ // Oink
475
+ Honk.RelationParameters relationParameters;
476
+ Fr[NUMBER_OF_ALPHAS] alphas; // Powers of alpha: [alpha, alpha^2, ..., alpha^(NUM_SUBRELATIONS-1)]
477
+ Fr[CONST_PROOF_SIZE_LOG_N] gateChallenges;
478
+ // Sumcheck
479
+ Fr libraChallenge;
480
+ Fr[CONST_PROOF_SIZE_LOG_N] sumCheckUChallenges;
481
+ // Shplemini
482
+ Fr rho;
483
+ Fr geminiR;
484
+ Fr shplonkNu;
485
+ Fr shplonkZ;
486
+ // Derived
487
+ Fr publicInputsDelta;
483
488
  }
484
489
 
485
490
  library ZKTranscriptLib {
486
- function generateTranscript(
487
- Honk.ZKProof memory proof,
488
- bytes32[] calldata publicInputs,
489
- uint256 vkHash,
490
- uint256 publicInputsSize,
491
- uint256 logN
492
- ) external pure returns (ZKTranscript memory t) {
493
- Fr previousChallenge;
494
- (t.relationParameters, previousChallenge) =
495
- generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge);
496
-
497
- (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof);
498
-
499
- (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN);
500
- (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof);
501
- (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN);
502
-
503
- (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge);
504
-
505
- (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN);
506
-
507
- (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN);
508
-
509
- (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge);
510
- return t;
511
- }
512
-
513
- function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) {
514
- uint256 challengeU256 = uint256(Fr.unwrap(challenge));
515
- uint256 lo = challengeU256 & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
516
- uint256 hi = challengeU256 >> 128;
517
- first = FrLib.fromBytes32(bytes32(lo));
518
- second = FrLib.fromBytes32(bytes32(hi));
519
- }
520
-
521
- function generateRelationParametersChallenges(
522
- Honk.ZKProof memory proof,
523
- bytes32[] calldata publicInputs,
524
- uint256 vkHash,
525
- uint256 publicInputsSize,
526
- Fr previousChallenge
527
- ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) {
528
- (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) =
529
- generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize);
530
-
531
- (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof);
532
- }
533
-
534
- function generateEtaChallenge(
535
- Honk.ZKProof memory proof,
536
- bytes32[] calldata publicInputs,
537
- uint256 vkHash,
538
- uint256 publicInputsSize
539
- ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) {
540
- bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 6);
541
- round0[0] = bytes32(vkHash);
542
-
543
- for (uint256 i = 0; i < publicInputsSize - PAIRING_POINTS_SIZE; i++) {
544
- round0[1 + i] = bytes32(publicInputs[i]);
545
- }
546
- for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) {
547
- round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]);
548
- }
549
-
550
- // Create the first challenge
551
- // Note: w4 is added to the challenge later on
552
- round0[1 + publicInputsSize] = bytes32(proof.w1.x);
553
- round0[1 + publicInputsSize + 1] = bytes32(proof.w1.y);
554
- round0[1 + publicInputsSize + 2] = bytes32(proof.w2.x);
555
- round0[1 + publicInputsSize + 3] = bytes32(proof.w2.y);
556
- round0[1 + publicInputsSize + 4] = bytes32(proof.w3.x);
557
- round0[1 + publicInputsSize + 5] = bytes32(proof.w3.y);
558
-
559
- previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0)));
560
- (eta, etaTwo) = splitChallenge(previousChallenge);
561
- previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))));
562
-
563
- (etaThree,) = splitChallenge(previousChallenge);
564
- }
565
-
566
- function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof)
567
- internal
568
- pure
569
- returns (Fr beta, Fr gamma, Fr nextPreviousChallenge)
491
+ function generateTranscript(
492
+ Honk.ZKProof memory proof,
493
+ bytes32[] calldata publicInputs,
494
+ uint256 vkHash,
495
+ uint256 publicInputsSize,
496
+ uint256 logN
497
+ ) external pure returns (ZKTranscript memory t) {
498
+ Fr previousChallenge;
499
+ (t.relationParameters, previousChallenge) = generateRelationParametersChallenges(
500
+ proof,
501
+ publicInputs,
502
+ vkHash,
503
+ publicInputsSize,
504
+ previousChallenge
505
+ );
506
+
507
+ (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof);
508
+
509
+ (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN);
510
+ (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof);
511
+ (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN);
512
+
513
+ (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge);
514
+
515
+ (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN);
516
+
517
+ (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN);
518
+
519
+ (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge);
520
+ return t;
521
+ }
522
+
523
+ function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) {
524
+ uint256 challengeU256 = uint256(Fr.unwrap(challenge));
525
+ // Split into two equal 127-bit chunks (254/2)
526
+ uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits
527
+ uint256 hi = challengeU256 >> 127;
528
+ first = FrLib.fromBytes32(bytes32(lo));
529
+ second = FrLib.fromBytes32(bytes32(hi));
530
+ }
531
+
532
+ function generateRelationParametersChallenges(
533
+ Honk.ZKProof memory proof,
534
+ bytes32[] calldata publicInputs,
535
+ uint256 vkHash,
536
+ uint256 publicInputsSize,
537
+ Fr previousChallenge
538
+ ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) {
539
+ (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize);
540
+
541
+ (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof);
542
+ }
543
+
544
+ function generateEtaChallenge(
545
+ Honk.ZKProof memory proof,
546
+ bytes32[] calldata publicInputs,
547
+ uint256 vkHash,
548
+ uint256 publicInputsSize
549
+ ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) {
550
+ // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6))
551
+ bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8);
552
+ round0[0] = bytes32(vkHash);
553
+
554
+ for (uint256 i = 0; i < publicInputsSize - PAIRING_POINTS_SIZE; i++) {
555
+ round0[1 + i] = bytes32(publicInputs[i]);
556
+ }
557
+ for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) {
558
+ round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]);
559
+ }
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
+
565
+ // Create the first challenge
566
+ // Note: w4 is added to the challenge later on
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);
573
+
574
+ previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0)));
575
+ (eta, etaTwo) = splitChallenge(previousChallenge);
576
+ previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))));
577
+
578
+ (etaThree, ) = splitChallenge(previousChallenge);
579
+ }
580
+
581
+ function generateBetaAndGammaChallenges(
582
+ Fr previousChallenge,
583
+ Honk.ZKProof memory proof
584
+ ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) {
585
+ bytes32[7] memory round1;
586
+ round1[0] = FrLib.toBytes32(previousChallenge);
587
+ round1[1] = bytes32(proof.lookupReadCounts.x);
588
+ round1[2] = bytes32(proof.lookupReadCounts.y);
589
+ round1[3] = bytes32(proof.lookupReadTags.x);
590
+ round1[4] = bytes32(proof.lookupReadTags.y);
591
+ round1[5] = bytes32(proof.w4.x);
592
+ round1[6] = bytes32(proof.w4.y);
593
+
594
+ nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1)));
595
+ (beta, gamma) = splitChallenge(nextPreviousChallenge);
596
+ }
597
+
598
+ // Alpha challenges non-linearise the gate contributions
599
+ function generateAlphaChallenges(
600
+ Fr previousChallenge,
601
+ Honk.ZKProof memory proof
602
+ ) internal pure returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) {
603
+ // Generate the original sumcheck alpha 0 by hashing zPerm and zLookup
604
+ uint256[5] memory alpha0;
605
+ alpha0[0] = Fr.unwrap(previousChallenge);
606
+ alpha0[1] = proof.lookupInverses.x;
607
+ alpha0[2] = proof.lookupInverses.y;
608
+ alpha0[3] = proof.zPerm.x;
609
+ alpha0[4] = proof.zPerm.y;
610
+
611
+ nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0)));
612
+ Fr alpha;
613
+ (alpha, ) = splitChallenge(nextPreviousChallenge);
614
+
615
+ // Compute powers of alpha for batching subrelations
616
+ alphas[0] = alpha;
617
+ for (uint256 i = 1; i < NUMBER_OF_ALPHAS; i++) {
618
+ alphas[i] = alphas[i - 1] * alpha;
619
+ }
620
+ }
621
+
622
+ function generateGateChallenges(
623
+ Fr previousChallenge,
624
+ uint256 logN
625
+ ) internal pure returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) {
626
+ previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))));
627
+ (gateChallenges[0], ) = splitChallenge(previousChallenge);
628
+ for (uint256 i = 1; i < logN; i++) {
629
+ gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1];
630
+ }
631
+ nextPreviousChallenge = previousChallenge;
632
+ }
633
+
634
+ function generateLibraChallenge(
635
+ Fr previousChallenge,
636
+ Honk.ZKProof memory proof
637
+ ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) {
638
+ // 2 comm, 1 sum, 1 challenge
639
+ uint256[4] memory challengeData;
640
+ challengeData[0] = Fr.unwrap(previousChallenge);
641
+ challengeData[1] = proof.libraCommitments[0].x;
642
+ challengeData[2] = proof.libraCommitments[0].y;
643
+ challengeData[3] = Fr.unwrap(proof.libraSum);
644
+ nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData)));
645
+ (libraChallenge, ) = splitChallenge(nextPreviousChallenge);
646
+ }
647
+
648
+ function generateSumcheckChallenges(
649
+ Honk.ZKProof memory proof,
650
+ Fr prevChallenge,
651
+ uint256 logN
652
+ ) internal pure returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) {
653
+ for (uint256 i = 0; i < logN; i++) {
654
+ Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal;
655
+ univariateChal[0] = prevChallenge;
656
+
657
+ for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) {
658
+ univariateChal[j + 1] = proof.sumcheckUnivariates[i][j];
659
+ }
660
+ prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal)));
661
+
662
+ (sumcheckChallenges[i], ) = splitChallenge(prevChallenge);
663
+ }
664
+ nextPreviousChallenge = prevChallenge;
665
+ }
666
+
667
+ // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient)
668
+ function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) internal pure returns (Fr rho, Fr nextPreviousChallenge) {
669
+ uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements;
670
+ rhoChallengeElements[0] = Fr.unwrap(prevChallenge);
671
+ uint256 i;
672
+ for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) {
673
+ rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]);
674
+ }
675
+ rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation);
676
+ i += 1;
677
+ rhoChallengeElements[i] = proof.libraCommitments[1].x;
678
+ rhoChallengeElements[i + 1] = proof.libraCommitments[1].y;
679
+ i += 2;
680
+ rhoChallengeElements[i] = proof.libraCommitments[2].x;
681
+ rhoChallengeElements[i + 1] = proof.libraCommitments[2].y;
682
+
683
+ nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements)));
684
+ (rho, ) = splitChallenge(nextPreviousChallenge);
685
+ }
686
+
687
+ function generateGeminiRChallenge(
688
+ Honk.ZKProof memory proof,
689
+ Fr prevChallenge,
690
+ uint256 logN
691
+ ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) {
692
+ uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1);
693
+ gR[0] = Fr.unwrap(prevChallenge);
694
+
695
+ for (uint256 i = 0; i < logN - 1; i++) {
696
+ gR[1 + i * 2] = proof.geminiFoldComms[i].x;
697
+ gR[2 + i * 2] = proof.geminiFoldComms[i].y;
698
+ }
699
+
700
+ nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR)));
701
+
702
+ (geminiR, ) = splitChallenge(nextPreviousChallenge);
703
+ }
704
+
705
+ function generateShplonkNuChallenge(
706
+ Honk.ZKProof memory proof,
707
+ Fr prevChallenge,
708
+ uint256 logN
709
+ ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) {
710
+ uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4);
711
+ shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge);
712
+
713
+ for (uint256 i = 1; i <= logN; i++) {
714
+ shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]);
715
+ }
716
+
717
+ uint256 libraIdx = 0;
718
+ for (uint256 i = logN + 1; i <= logN + 4; i++) {
719
+ shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]);
720
+ libraIdx++;
721
+ }
722
+
723
+ nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements)));
724
+ (shplonkNu, ) = splitChallenge(nextPreviousChallenge);
725
+ }
726
+
727
+ function generateShplonkZChallenge(
728
+ Honk.ZKProof memory proof,
729
+ Fr prevChallenge
730
+ ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) {
731
+ uint256[3] memory shplonkZChallengeElements;
732
+ shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge);
733
+
734
+ shplonkZChallengeElements[1] = proof.shplonkQ.x;
735
+ shplonkZChallengeElements[2] = proof.shplonkQ.y;
736
+
737
+ nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements)));
738
+ (shplonkZ, ) = splitChallenge(nextPreviousChallenge);
739
+ }
740
+
741
+ function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) {
742
+ uint256 boundary = 0x0;
743
+
744
+ // Pairing point object
745
+ for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) {
746
+ p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]);
747
+ boundary += FIELD_ELEMENT_SIZE;
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
+
754
+ // Commitments
755
+ p.w1 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
756
+ boundary += GROUP_ELEMENT_SIZE;
757
+ p.w2 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
758
+ boundary += GROUP_ELEMENT_SIZE;
759
+ p.w3 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
760
+ boundary += GROUP_ELEMENT_SIZE;
761
+
762
+ // Lookup / Permutation Helper Commitments
763
+ p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
764
+ boundary += GROUP_ELEMENT_SIZE;
765
+ p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
766
+ boundary += GROUP_ELEMENT_SIZE;
767
+ p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
768
+ boundary += GROUP_ELEMENT_SIZE;
769
+ p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
770
+ boundary += GROUP_ELEMENT_SIZE;
771
+ p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
772
+ boundary += GROUP_ELEMENT_SIZE;
773
+ p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
774
+ boundary += GROUP_ELEMENT_SIZE;
775
+
776
+ p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]);
777
+ boundary += FIELD_ELEMENT_SIZE;
778
+ // Sumcheck univariates
779
+ for (uint256 i = 0; i < logN; i++) {
780
+ for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) {
781
+ p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]);
782
+ boundary += FIELD_ELEMENT_SIZE;
783
+ }
784
+ }
785
+
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++) {
788
+ p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]);
789
+ boundary += FIELD_ELEMENT_SIZE;
790
+ }
791
+
792
+ p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]);
793
+ boundary += FIELD_ELEMENT_SIZE;
794
+
795
+ p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
796
+ boundary += GROUP_ELEMENT_SIZE;
797
+ p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
798
+ boundary += GROUP_ELEMENT_SIZE;
799
+
800
+ // Gemini
801
+ // Read gemini fold univariates
802
+ for (uint256 i = 0; i < logN - 1; i++) {
803
+ p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
804
+ boundary += GROUP_ELEMENT_SIZE;
805
+ }
806
+
807
+ // Read gemini a evaluations
808
+ for (uint256 i = 0; i < logN; i++) {
809
+ p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]);
810
+ boundary += FIELD_ELEMENT_SIZE;
811
+ }
812
+
813
+ for (uint256 i = 0; i < 4; i++) {
814
+ p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]);
815
+ boundary += FIELD_ELEMENT_SIZE;
816
+ }
817
+
818
+ // Shplonk
819
+ p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
820
+ boundary += GROUP_ELEMENT_SIZE;
821
+ // KZG
822
+ p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
823
+ }
824
+ }
825
+
826
+ // Field arithmetic libraries
827
+
828
+ library RelationsLib {
829
+ Fr internal constant GRUMPKIN_CURVE_B_PARAMETER_NEGATED = Fr.wrap(17); // -(-17)
830
+
831
+ function accumulateRelationEvaluations(
832
+ Fr[NUMBER_OF_ENTITIES] memory purportedEvaluations,
833
+ Honk.RelationParameters memory rp,
834
+ Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges,
835
+ Fr powPartialEval
836
+ ) internal pure returns (Fr accumulator) {
837
+ Fr[NUMBER_OF_SUBRELATIONS] memory evaluations;
838
+
839
+ // Accumulate all relations in Ultra Honk - each with varying number of subrelations
840
+ accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval);
841
+ accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval);
842
+ accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval);
843
+ accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval);
844
+ accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval);
845
+ accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval);
846
+ accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval);
847
+ accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval);
848
+ accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval);
849
+
850
+ // batch the subrelations with the precomputed alpha powers to obtain the full honk relation
851
+ accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges);
852
+ }
853
+
854
+ /**
855
+ * Aesthetic helper function that is used to index by enum into proof.sumcheckEvaluations, it avoids
856
+ * the relation checking code being cluttered with uint256 type casting, which is often a different colour in code
857
+ * editors, and thus is noisy.
858
+ */
859
+ function wire(Fr[NUMBER_OF_ENTITIES] memory p, WIRE _wire) internal pure returns (Fr) {
860
+ return p[uint256(_wire)];
861
+ }
862
+
863
+ uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000;
864
+ /**
865
+ * Ultra Arithmetic Relation
866
+ *
867
+ */
868
+
869
+ function accumulateArithmeticRelation(
870
+ Fr[NUMBER_OF_ENTITIES] memory p,
871
+ Fr[NUMBER_OF_SUBRELATIONS] memory evals,
872
+ Fr domainSep
873
+ ) internal pure {
874
+ // Relation 0
875
+ Fr q_arith = wire(p, WIRE.Q_ARITH);
570
876
  {
571
- bytes32[7] memory round1;
572
- round1[0] = FrLib.toBytes32(previousChallenge);
573
- round1[1] = bytes32(proof.lookupReadCounts.x);
574
- round1[2] = bytes32(proof.lookupReadCounts.y);
575
- round1[3] = bytes32(proof.lookupReadTags.x);
576
- round1[4] = bytes32(proof.lookupReadTags.y);
577
- round1[5] = bytes32(proof.w4.x);
578
- round1[6] = bytes32(proof.w4.y);
579
-
580
- nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1)));
581
- (beta, gamma) = splitChallenge(nextPreviousChallenge);
582
- }
583
-
584
- // Alpha challenges non-linearise the gate contributions
585
- function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof)
586
- internal
587
- pure
588
- returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge)
877
+ Fr neg_half = Fr.wrap(NEG_HALF_MODULO_P);
878
+
879
+ Fr accum = (q_arith - Fr.wrap(3)) * (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * neg_half;
880
+ accum =
881
+ accum +
882
+ (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) +
883
+ (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) +
884
+ (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) +
885
+ (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) +
886
+ wire(p, WIRE.Q_C);
887
+ accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT);
888
+ accum = accum * q_arith;
889
+ accum = accum * domainSep;
890
+ evals[0] = accum;
891
+ }
892
+
893
+ // Relation 1
589
894
  {
590
- // Generate the original sumcheck alpha 0 by hashing zPerm and zLookup
591
- uint256[5] memory alpha0;
592
- alpha0[0] = Fr.unwrap(previousChallenge);
593
- alpha0[1] = proof.lookupInverses.x;
594
- alpha0[2] = proof.lookupInverses.y;
595
- alpha0[3] = proof.zPerm.x;
596
- alpha0[4] = proof.zPerm.y;
597
-
598
- nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0)));
599
- Fr alpha;
600
- (alpha,) = splitChallenge(nextPreviousChallenge);
601
-
602
- // Compute powers of alpha for batching subrelations
603
- alphas[0] = alpha;
604
- for (uint256 i = 1; i < NUMBER_OF_ALPHAS; i++) {
605
- alphas[i] = alphas[i - 1] * alpha;
606
- }
607
- }
608
-
609
- function generateGateChallenges(Fr previousChallenge, uint256 logN)
610
- internal
611
- pure
612
- returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge)
895
+ Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M);
896
+ accum = accum * (q_arith - Fr.wrap(2));
897
+ accum = accum * (q_arith - ONE);
898
+ accum = accum * q_arith;
899
+ accum = accum * domainSep;
900
+ evals[1] = accum;
901
+ }
902
+ }
903
+
904
+ function accumulatePermutationRelation(
905
+ Fr[NUMBER_OF_ENTITIES] memory p,
906
+ Honk.RelationParameters memory rp,
907
+ Fr[NUMBER_OF_SUBRELATIONS] memory evals,
908
+ Fr domainSep
909
+ ) internal pure {
910
+ Fr grand_product_numerator;
911
+ Fr grand_product_denominator;
912
+
613
913
  {
614
- previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))));
615
- (gateChallenges[0],) = splitChallenge(previousChallenge);
616
- for (uint256 i = 1; i < logN; i++) {
617
- gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1];
618
- }
619
- nextPreviousChallenge = previousChallenge;
620
- }
621
-
622
- function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof)
623
- internal
624
- pure
625
- returns (Fr libraChallenge, Fr nextPreviousChallenge)
914
+ Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma;
915
+ num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma);
916
+ num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma);
917
+ num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma);
918
+
919
+ grand_product_numerator = num;
920
+ }
626
921
  {
627
- // 2 comm, 1 sum, 1 challenge
628
- uint256[4] memory challengeData;
629
- challengeData[0] = Fr.unwrap(previousChallenge);
630
- challengeData[1] = proof.libraCommitments[0].x;
631
- challengeData[2] = proof.libraCommitments[0].y;
632
- challengeData[3] = Fr.unwrap(proof.libraSum);
633
- nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData)));
634
- (libraChallenge,) = splitChallenge(nextPreviousChallenge);
635
- }
636
-
637
- function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN)
638
- internal
639
- pure
640
- returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge)
922
+ Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma;
923
+ den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma);
924
+ den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma);
925
+ den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma);
926
+
927
+ grand_product_denominator = den;
928
+ }
929
+
930
+ // Contribution 2
641
931
  {
642
- for (uint256 i = 0; i < logN; i++) {
643
- Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal;
644
- univariateChal[0] = prevChallenge;
932
+ Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator;
645
933
 
646
- for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) {
647
- univariateChal[j + 1] = proof.sumcheckUnivariates[i][j];
648
- }
649
- prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal)));
934
+ acc = acc - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * grand_product_denominator);
935
+ acc = acc * domainSep;
936
+ evals[2] = acc;
937
+ }
650
938
 
651
- (sumcheckChallenges[i],) = splitChallenge(prevChallenge);
652
- }
653
- nextPreviousChallenge = prevChallenge;
939
+ // Contribution 3
940
+ {
941
+ Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep;
942
+ evals[3] = acc;
654
943
  }
944
+ }
945
+
946
+ function accumulateLogDerivativeLookupRelation(
947
+ Fr[NUMBER_OF_ENTITIES] memory p,
948
+ Honk.RelationParameters memory rp,
949
+ Fr[NUMBER_OF_SUBRELATIONS] memory evals,
950
+ Fr domainSep
951
+ ) internal pure {
952
+ Fr write_term;
953
+ Fr read_term;
655
954
 
656
- // We add Libra claimed eval + 3 comm + 1 more eval
657
- function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge)
658
- internal
659
- pure
660
- returns (Fr rho, Fr nextPreviousChallenge)
955
+ // Calculate the write term (the table accumulation)
661
956
  {
662
- uint256[NUMBER_OF_ENTITIES + 9] memory rhoChallengeElements;
663
- rhoChallengeElements[0] = Fr.unwrap(prevChallenge);
664
- uint256 i;
665
- for (i = 1; i <= NUMBER_OF_ENTITIES; i++) {
666
- rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]);
667
- }
668
- rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation);
669
-
670
- i += 1;
671
- rhoChallengeElements[i] = proof.libraCommitments[1].x;
672
- rhoChallengeElements[i + 1] = proof.libraCommitments[1].y;
673
- i += 2;
674
- rhoChallengeElements[i] = proof.libraCommitments[2].x;
675
- rhoChallengeElements[i + 1] = proof.libraCommitments[2].y;
676
- i += 2;
677
- rhoChallengeElements[i] = proof.geminiMaskingPoly.x;
678
- rhoChallengeElements[i + 1] = proof.geminiMaskingPoly.y;
679
-
680
- i += 2;
681
- rhoChallengeElements[i] = Fr.unwrap(proof.geminiMaskingEval);
682
-
683
- nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements)));
684
- (rho,) = splitChallenge(nextPreviousChallenge);
685
- }
686
-
687
- function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN)
688
- internal
689
- pure
690
- returns (Fr geminiR, Fr nextPreviousChallenge)
957
+ write_term =
958
+ wire(p, WIRE.TABLE_1) +
959
+ rp.gamma +
960
+ (wire(p, WIRE.TABLE_2) * rp.eta) +
961
+ (wire(p, WIRE.TABLE_3) * rp.etaTwo) +
962
+ (wire(p, WIRE.TABLE_4) * rp.etaThree);
963
+ }
964
+
965
+ // Calculate the write term
691
966
  {
692
- uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1);
693
- gR[0] = Fr.unwrap(prevChallenge);
967
+ Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT));
968
+ Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT);
969
+ Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT);
970
+
971
+ read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) + (wire(p, WIRE.Q_O) * rp.etaThree);
972
+ }
694
973
 
695
- for (uint256 i = 0; i < logN - 1; i++) {
696
- gR[1 + i * 2] = proof.geminiFoldComms[i].x;
697
- gR[2 + i * 2] = proof.geminiFoldComms[i].y;
698
- }
974
+ Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term;
975
+ Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term;
699
976
 
700
- nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR)));
977
+ Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) +
978
+ wire(p, WIRE.Q_LOOKUP) -
979
+ (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP));
701
980
 
702
- (geminiR,) = splitChallenge(nextPreviousChallenge);
981
+ // Inverse calculated correctly relation
982
+ Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor;
983
+ accumulatorNone = accumulatorNone * domainSep;
984
+
985
+ // Inverse
986
+ Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse;
987
+
988
+ Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS);
989
+
990
+ Fr read_tag_boolean_relation = read_tag * read_tag - read_tag;
991
+
992
+ evals[4] = accumulatorNone;
993
+ evals[5] = accumulatorOne;
994
+ evals[6] = read_tag_boolean_relation * domainSep;
995
+ }
996
+
997
+ function accumulateDeltaRangeRelation(
998
+ Fr[NUMBER_OF_ENTITIES] memory p,
999
+ Fr[NUMBER_OF_SUBRELATIONS] memory evals,
1000
+ Fr domainSep
1001
+ ) internal pure {
1002
+ Fr minus_one = ZERO - ONE;
1003
+ Fr minus_two = ZERO - Fr.wrap(2);
1004
+ Fr minus_three = ZERO - Fr.wrap(3);
1005
+
1006
+ // Compute wire differences
1007
+ Fr delta_1 = wire(p, WIRE.W_R) - wire(p, WIRE.W_L);
1008
+ Fr delta_2 = wire(p, WIRE.W_O) - wire(p, WIRE.W_R);
1009
+ Fr delta_3 = wire(p, WIRE.W_4) - wire(p, WIRE.W_O);
1010
+ Fr delta_4 = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_4);
1011
+
1012
+ // Contribution 6
1013
+ {
1014
+ Fr acc = delta_1;
1015
+ acc = acc * (delta_1 + minus_one);
1016
+ acc = acc * (delta_1 + minus_two);
1017
+ acc = acc * (delta_1 + minus_three);
1018
+ acc = acc * wire(p, WIRE.Q_RANGE);
1019
+ acc = acc * domainSep;
1020
+ evals[7] = acc;
703
1021
  }
704
1022
 
705
- function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN)
706
- internal
707
- pure
708
- returns (Fr shplonkNu, Fr nextPreviousChallenge)
1023
+ // Contribution 7
709
1024
  {
710
- uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4);
711
- shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge);
1025
+ Fr acc = delta_2;
1026
+ acc = acc * (delta_2 + minus_one);
1027
+ acc = acc * (delta_2 + minus_two);
1028
+ acc = acc * (delta_2 + minus_three);
1029
+ acc = acc * wire(p, WIRE.Q_RANGE);
1030
+ acc = acc * domainSep;
1031
+ evals[8] = acc;
1032
+ }
712
1033
 
713
- for (uint256 i = 1; i <= logN; i++) {
714
- shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]);
715
- }
1034
+ // Contribution 8
1035
+ {
1036
+ Fr acc = delta_3;
1037
+ acc = acc * (delta_3 + minus_one);
1038
+ acc = acc * (delta_3 + minus_two);
1039
+ acc = acc * (delta_3 + minus_three);
1040
+ acc = acc * wire(p, WIRE.Q_RANGE);
1041
+ acc = acc * domainSep;
1042
+ evals[9] = acc;
1043
+ }
716
1044
 
717
- uint256 libraIdx = 0;
718
- for (uint256 i = logN + 1; i <= logN + 4; i++) {
719
- shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]);
720
- libraIdx++;
721
- }
1045
+ // Contribution 9
1046
+ {
1047
+ Fr acc = delta_4;
1048
+ acc = acc * (delta_4 + minus_one);
1049
+ acc = acc * (delta_4 + minus_two);
1050
+ acc = acc * (delta_4 + minus_three);
1051
+ acc = acc * wire(p, WIRE.Q_RANGE);
1052
+ acc = acc * domainSep;
1053
+ evals[10] = acc;
1054
+ }
1055
+ }
1056
+
1057
+ struct EllipticParams {
1058
+ // Points
1059
+ Fr x_1;
1060
+ Fr y_1;
1061
+ Fr x_2;
1062
+ Fr y_2;
1063
+ Fr y_3;
1064
+ Fr x_3;
1065
+ // push accumulators into memory
1066
+ Fr x_double_identity;
1067
+ }
1068
+
1069
+ function accumulateEllipticRelation(
1070
+ Fr[NUMBER_OF_ENTITIES] memory p,
1071
+ Fr[NUMBER_OF_SUBRELATIONS] memory evals,
1072
+ Fr domainSep
1073
+ ) internal pure {
1074
+ EllipticParams memory ep;
1075
+ ep.x_1 = wire(p, WIRE.W_R);
1076
+ ep.y_1 = wire(p, WIRE.W_O);
1077
+
1078
+ ep.x_2 = wire(p, WIRE.W_L_SHIFT);
1079
+ ep.y_2 = wire(p, WIRE.W_4_SHIFT);
1080
+ ep.y_3 = wire(p, WIRE.W_O_SHIFT);
1081
+ ep.x_3 = wire(p, WIRE.W_R_SHIFT);
1082
+
1083
+ Fr q_sign = wire(p, WIRE.Q_L);
1084
+ Fr q_is_double = wire(p, WIRE.Q_M);
1085
+
1086
+ // Contribution 10 point addition, x-coordinate check
1087
+ // q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0
1088
+ Fr x_diff = (ep.x_2 - ep.x_1);
1089
+ Fr y1_sqr = (ep.y_1 * ep.y_1);
1090
+ {
1091
+ // Move to top
1092
+ Fr partialEval = domainSep;
722
1093
 
723
- nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements)));
724
- (shplonkNu,) = splitChallenge(nextPreviousChallenge);
1094
+ Fr y2_sqr = (ep.y_2 * ep.y_2);
1095
+ Fr y1y2 = ep.y_1 * ep.y_2 * q_sign;
1096
+ Fr x_add_identity = (ep.x_3 + ep.x_2 + ep.x_1);
1097
+ x_add_identity = x_add_identity * x_diff * x_diff;
1098
+ x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2;
1099
+
1100
+ evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double);
725
1101
  }
726
1102
 
727
- function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge)
728
- internal
729
- pure
730
- returns (Fr shplonkZ, Fr nextPreviousChallenge)
1103
+ // Contribution 11 point addition, x-coordinate check
1104
+ // q_elliptic * (q_sign * y1 + y3)(x2 - x1) + (x3 - x1)(y2 - q_sign * y1) = 0
731
1105
  {
732
- uint256[3] memory shplonkZChallengeElements;
733
- shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge);
734
-
735
- shplonkZChallengeElements[1] = proof.shplonkQ.x;
736
- shplonkZChallengeElements[2] = proof.shplonkQ.y;
737
-
738
- nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements)));
739
- (shplonkZ,) = splitChallenge(nextPreviousChallenge);
740
- }
741
-
742
- function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) {
743
- uint256 boundary = 0x0;
744
-
745
- // Pairing point object
746
- for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) {
747
- p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]);
748
- boundary += FIELD_ELEMENT_SIZE;
749
- }
750
- // Commitments
751
- p.w1 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
752
- boundary += GROUP_ELEMENT_SIZE;
753
- p.w2 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
754
- boundary += GROUP_ELEMENT_SIZE;
755
- p.w3 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
756
- boundary += GROUP_ELEMENT_SIZE;
757
-
758
- // Lookup / Permutation Helper Commitments
759
- p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
760
- boundary += GROUP_ELEMENT_SIZE;
761
- p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
762
- boundary += GROUP_ELEMENT_SIZE;
763
- p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
764
- boundary += GROUP_ELEMENT_SIZE;
765
- p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
766
- boundary += GROUP_ELEMENT_SIZE;
767
- p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
768
- boundary += GROUP_ELEMENT_SIZE;
769
- p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
770
- boundary += GROUP_ELEMENT_SIZE;
771
-
772
- p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]);
773
- boundary += FIELD_ELEMENT_SIZE;
774
- // Sumcheck univariates
775
- for (uint256 i = 0; i < logN; i++) {
776
- for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) {
777
- p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]);
778
- boundary += FIELD_ELEMENT_SIZE;
779
- }
780
- }
781
-
782
- // Sumcheck evaluations
783
- for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) {
784
- p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]);
785
- boundary += FIELD_ELEMENT_SIZE;
786
- }
787
-
788
- p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]);
789
- boundary += FIELD_ELEMENT_SIZE;
1106
+ Fr y1_plus_y3 = ep.y_1 + ep.y_3;
1107
+ Fr y_diff = ep.y_2 * q_sign - ep.y_1;
1108
+ Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff;
1109
+ evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double);
1110
+ }
790
1111
 
791
- p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
792
- boundary += GROUP_ELEMENT_SIZE;
793
- p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
794
- boundary += GROUP_ELEMENT_SIZE;
795
- p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
796
- boundary += GROUP_ELEMENT_SIZE;
797
- p.geminiMaskingEval = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]);
798
- boundary += FIELD_ELEMENT_SIZE;
1112
+ // Contribution 10 point doubling, x-coordinate check
1113
+ // (x3 + x1 + x1) (4y1*y1) - 9 * x1 * x1 * x1 * x1 = 0
1114
+ // N.B. we're using the equivalence x1*x1*x1 === y1*y1 - curve_b to reduce degree by 1
1115
+ {
1116
+ Fr x_pow_4 = (y1_sqr + GRUMPKIN_CURVE_B_PARAMETER_NEGATED) * ep.x_1;
1117
+ Fr y1_sqr_mul_4 = y1_sqr + y1_sqr;
1118
+ y1_sqr_mul_4 = y1_sqr_mul_4 + y1_sqr_mul_4;
1119
+ Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9);
799
1120
 
800
- // Gemini
801
- // Read gemini fold univariates
802
- for (uint256 i = 0; i < logN - 1; i++) {
803
- p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
804
- boundary += GROUP_ELEMENT_SIZE;
805
- }
806
-
807
- // Read gemini a evaluations
808
- for (uint256 i = 0; i < logN; i++) {
809
- p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]);
810
- boundary += FIELD_ELEMENT_SIZE;
811
- }
812
-
813
- for (uint256 i = 0; i < 4; i++) {
814
- p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]);
815
- boundary += FIELD_ELEMENT_SIZE;
816
- }
817
-
818
- // Shplonk
819
- p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
820
- boundary += GROUP_ELEMENT_SIZE;
821
- // KZG
822
- p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);
1121
+ // NOTE: pushed into memory (stack >:'( )
1122
+ ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9;
1123
+
1124
+ Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double;
1125
+ evals[11] = evals[11] + acc;
823
1126
  }
824
- }
825
1127
 
826
- // Field arithmetic libraries
1128
+ // Contribution 11 point doubling, y-coordinate check
1129
+ // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0
1130
+ {
1131
+ Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1;
1132
+ Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3);
1133
+ evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double;
1134
+ }
1135
+ }
1136
+
1137
+ // Parameters used within the Memory Relation
1138
+ // A struct is used to work around stack too deep. This relation has alot of variables
1139
+ struct MemParams {
1140
+ Fr memory_record_check;
1141
+ Fr partial_record_check;
1142
+ Fr next_gate_access_type;
1143
+ Fr record_delta;
1144
+ Fr index_delta;
1145
+ Fr adjacent_values_match_if_adjacent_indices_match;
1146
+ Fr adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation;
1147
+ Fr access_check;
1148
+ Fr next_gate_access_type_is_boolean;
1149
+ Fr ROM_consistency_check_identity;
1150
+ Fr RAM_consistency_check_identity;
1151
+ Fr timestamp_delta;
1152
+ Fr RAM_timestamp_check_identity;
1153
+ Fr memory_identity;
1154
+ Fr index_is_monotonically_increasing;
1155
+ }
1156
+
1157
+ function accumulateMemoryRelation(
1158
+ Fr[NUMBER_OF_ENTITIES] memory p,
1159
+ Honk.RelationParameters memory rp,
1160
+ Fr[NUMBER_OF_SUBRELATIONS] memory evals,
1161
+ Fr domainSep
1162
+ ) internal pure {
1163
+ MemParams memory ap;
827
1164
 
828
- library RelationsLib {
829
- Fr internal constant GRUMPKIN_CURVE_B_PARAMETER_NEGATED = Fr.wrap(17); // -(-17)
830
-
831
- function accumulateRelationEvaluations(
832
- Fr[NUMBER_OF_ENTITIES] memory purportedEvaluations,
833
- Honk.RelationParameters memory rp,
834
- Fr[NUMBER_OF_ALPHAS] memory alphas,
835
- Fr powPartialEval
836
- ) internal pure returns (Fr accumulator) {
837
- Fr[NUMBER_OF_SUBRELATIONS] memory evaluations;
838
-
839
- // Accumulate all relations in Ultra Honk - each with varying number of subrelations
840
- accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval);
841
- accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval);
842
- accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval);
843
- accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval);
844
- accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval);
845
- accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval);
846
- accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval);
847
- accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval);
848
- accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval);
849
-
850
- // batch the subrelations with the alpha challenges to obtain the full honk relation
851
- accumulator = scaleAndBatchSubrelations(evaluations, alphas);
852
- }
1165
+ /**
1166
+ * MEMORY
1167
+ *
1168
+ * A RAM memory record contains a tuple of the following fields:
1169
+ * * i: `index` of memory cell being accessed
1170
+ * * t: `timestamp` of memory cell being accessed (used for RAM, set to 0 for ROM)
1171
+ * * v: `value` of memory cell being accessed
1172
+ * * a: `access` type of record. read: 0 = read, 1 = write
1173
+ * * r: `record` of memory cell. record = access + index * eta + timestamp * eta_two + value * eta_three
1174
+ *
1175
+ * A ROM memory record contains a tuple of the following fields:
1176
+ * * i: `index` of memory cell being accessed
1177
+ * * v: `value1` of memory cell being accessed (ROM tables can store up to 2 values per index)
1178
+ * * v2:`value2` of memory cell being accessed (ROM tables can store up to 2 values per index)
1179
+ * * r: `record` of memory cell. record = index * eta + value2 * eta_two + value1 * eta_three
1180
+ *
1181
+ * When performing a read/write access, the values of i, t, v, v2, a, r are stored in the following wires +
1182
+ * selectors, depending on whether the gate is a RAM read/write or a ROM read
1183
+ *
1184
+ * | gate type | i | v2/t | v | a | r |
1185
+ * | --------- | -- | ----- | -- | -- | -- |
1186
+ * | ROM | w1 | w2 | w3 | -- | w4 |
1187
+ * | RAM | w1 | w2 | w3 | qc | w4 |
1188
+ *
1189
+ * (for accesses where `index` is a circuit constant, it is assumed the circuit will apply a copy constraint on
1190
+ * `w2` to fix its value)
1191
+ *
1192
+ *
1193
+ */
853
1194
 
854
1195
  /**
855
- * Aesthetic helper function that is used to index by enum into proof.sumcheckEvaluations, it avoids
856
- * the relation checking code being cluttered with uint256 type casting, which is often a different colour in code
857
- * editors, and thus is noisy.
1196
+ * Memory Record Check
1197
+ * Partial degree: 1
1198
+ * Total degree: 4
1199
+ *
1200
+ * A ROM/ROM access gate can be evaluated with the identity:
1201
+ *
1202
+ * qc + w1 \eta + w2 \eta_two + w3 \eta_three - w4 = 0
1203
+ *
1204
+ * For ROM gates, qc = 0
858
1205
  */
859
- function wire(Fr[NUMBER_OF_ENTITIES] memory p, WIRE _wire) internal pure returns (Fr) {
860
- return p[uint256(_wire)];
861
- }
1206
+ ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree;
1207
+ ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo);
1208
+ ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta);
1209
+ ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C);
1210
+ ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4
1211
+ ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4);
862
1212
 
863
- uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000;
864
1213
  /**
865
- * Ultra Arithmetic Relation
1214
+ * Contribution 13 & 14
1215
+ * ROM Consistency Check
1216
+ * Partial degree: 1
1217
+ * Total degree: 4
1218
+ *
1219
+ * For every ROM read, a set equivalence check is applied between the record witnesses, and a second set of
1220
+ * records that are sorted.
1221
+ *
1222
+ * We apply the following checks for the sorted records:
1223
+ *
1224
+ * 1. w1, w2, w3 correctly map to 'index', 'v1, 'v2' for a given record value at w4
1225
+ * 2. index values for adjacent records are monotonically increasing
1226
+ * 3. if, at gate i, index_i == index_{i + 1}, then value1_i == value1_{i + 1} and value2_i == value2_{i + 1}
866
1227
  *
867
1228
  */
1229
+ ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L);
1230
+ ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4);
868
1231
 
869
- function accumulateArithmeticRelation(
870
- Fr[NUMBER_OF_ENTITIES] memory p,
871
- Fr[NUMBER_OF_SUBRELATIONS] memory evals,
872
- Fr domainSep
873
- ) internal pure {
874
- // Relation 0
875
- Fr q_arith = wire(p, WIRE.Q_ARITH);
876
- {
877
- Fr neg_half = Fr.wrap(NEG_HALF_MODULO_P);
878
-
879
- Fr accum = (q_arith - Fr.wrap(3)) * (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * neg_half;
880
- accum = accum + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R))
881
- + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + wire(p, WIRE.Q_C);
882
- accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT);
883
- accum = accum * q_arith;
884
- accum = accum * domainSep;
885
- evals[0] = accum;
886
- }
887
-
888
- // Relation 1
889
- {
890
- Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M);
891
- accum = accum * (q_arith - Fr.wrap(2));
892
- accum = accum * (q_arith - ONE);
893
- accum = accum * q_arith;
894
- accum = accum * domainSep;
895
- evals[1] = accum;
896
- }
897
- }
898
-
899
- function accumulatePermutationRelation(
900
- Fr[NUMBER_OF_ENTITIES] memory p,
901
- Honk.RelationParameters memory rp,
902
- Fr[NUMBER_OF_SUBRELATIONS] memory evals,
903
- Fr domainSep
904
- ) internal pure {
905
- Fr grand_product_numerator;
906
- Fr grand_product_denominator;
907
-
908
- {
909
- Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma;
910
- num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma);
911
- num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma);
912
- num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma);
913
-
914
- grand_product_numerator = num;
915
- }
916
- {
917
- Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma;
918
- den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma);
919
- den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma);
920
- den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma);
921
-
922
- grand_product_denominator = den;
923
- }
924
-
925
- // Contribution 2
926
- {
927
- Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator;
928
-
929
- acc = acc
930
- - (
931
- (wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta))
932
- * grand_product_denominator
933
- );
934
- acc = acc * domainSep;
935
- evals[2] = acc;
936
- }
937
-
938
- // Contribution 3
939
- {
940
- Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep;
941
- evals[3] = acc;
942
- }
943
- }
944
-
945
- function accumulateLogDerivativeLookupRelation(
946
- Fr[NUMBER_OF_ENTITIES] memory p,
947
- Honk.RelationParameters memory rp,
948
- Fr[NUMBER_OF_SUBRELATIONS] memory evals,
949
- Fr domainSep
950
- ) internal pure {
951
- Fr write_term;
952
- Fr read_term;
953
-
954
- // Calculate the write term (the table accumulation)
955
- {
956
- write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta)
957
- + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree);
958
- }
959
-
960
- // Calculate the write term
961
- {
962
- Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT));
963
- Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT);
964
- Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT);
965
-
966
- read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo)
967
- + (wire(p, WIRE.Q_O) * rp.etaThree);
968
- }
969
-
970
- Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term;
971
- Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term;
972
-
973
- Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP)
974
- - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP));
975
-
976
- // Inverse calculated correctly relation
977
- Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor;
978
- accumulatorNone = accumulatorNone * domainSep;
979
-
980
- // Inverse
981
- Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse;
982
-
983
- Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS);
984
-
985
- Fr read_tag_boolean_relation = read_tag * read_tag - read_tag;
986
-
987
- evals[4] = accumulatorNone;
988
- evals[5] = accumulatorOne;
989
- evals[6] = read_tag_boolean_relation * domainSep;
990
- }
991
-
992
- function accumulateDeltaRangeRelation(
993
- Fr[NUMBER_OF_ENTITIES] memory p,
994
- Fr[NUMBER_OF_SUBRELATIONS] memory evals,
995
- Fr domainSep
996
- ) internal pure {
997
- Fr minus_one = ZERO - ONE;
998
- Fr minus_two = ZERO - Fr.wrap(2);
999
- Fr minus_three = ZERO - Fr.wrap(3);
1000
-
1001
- // Compute wire differences
1002
- Fr delta_1 = wire(p, WIRE.W_R) - wire(p, WIRE.W_L);
1003
- Fr delta_2 = wire(p, WIRE.W_O) - wire(p, WIRE.W_R);
1004
- Fr delta_3 = wire(p, WIRE.W_4) - wire(p, WIRE.W_O);
1005
- Fr delta_4 = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_4);
1006
-
1007
- // Contribution 6
1008
- {
1009
- Fr acc = delta_1;
1010
- acc = acc * (delta_1 + minus_one);
1011
- acc = acc * (delta_1 + minus_two);
1012
- acc = acc * (delta_1 + minus_three);
1013
- acc = acc * wire(p, WIRE.Q_RANGE);
1014
- acc = acc * domainSep;
1015
- evals[7] = acc;
1016
- }
1017
-
1018
- // Contribution 7
1019
- {
1020
- Fr acc = delta_2;
1021
- acc = acc * (delta_2 + minus_one);
1022
- acc = acc * (delta_2 + minus_two);
1023
- acc = acc * (delta_2 + minus_three);
1024
- acc = acc * wire(p, WIRE.Q_RANGE);
1025
- acc = acc * domainSep;
1026
- evals[8] = acc;
1027
- }
1028
-
1029
- // Contribution 8
1030
- {
1031
- Fr acc = delta_3;
1032
- acc = acc * (delta_3 + minus_one);
1033
- acc = acc * (delta_3 + minus_two);
1034
- acc = acc * (delta_3 + minus_three);
1035
- acc = acc * wire(p, WIRE.Q_RANGE);
1036
- acc = acc * domainSep;
1037
- evals[9] = acc;
1038
- }
1039
-
1040
- // Contribution 9
1041
- {
1042
- Fr acc = delta_4;
1043
- acc = acc * (delta_4 + minus_one);
1044
- acc = acc * (delta_4 + minus_two);
1045
- acc = acc * (delta_4 + minus_three);
1046
- acc = acc * wire(p, WIRE.Q_RANGE);
1047
- acc = acc * domainSep;
1048
- evals[10] = acc;
1049
- }
1050
- }
1051
-
1052
- struct EllipticParams {
1053
- // Points
1054
- Fr x_1;
1055
- Fr y_1;
1056
- Fr x_2;
1057
- Fr y_2;
1058
- Fr y_3;
1059
- Fr x_3;
1060
- // push accumulators into memory
1061
- Fr x_double_identity;
1062
- }
1063
-
1064
- function accumulateEllipticRelation(
1065
- Fr[NUMBER_OF_ENTITIES] memory p,
1066
- Fr[NUMBER_OF_SUBRELATIONS] memory evals,
1067
- Fr domainSep
1068
- ) internal pure {
1069
- EllipticParams memory ep;
1070
- ep.x_1 = wire(p, WIRE.W_R);
1071
- ep.y_1 = wire(p, WIRE.W_O);
1072
-
1073
- ep.x_2 = wire(p, WIRE.W_L_SHIFT);
1074
- ep.y_2 = wire(p, WIRE.W_4_SHIFT);
1075
- ep.y_3 = wire(p, WIRE.W_O_SHIFT);
1076
- ep.x_3 = wire(p, WIRE.W_R_SHIFT);
1077
-
1078
- Fr q_sign = wire(p, WIRE.Q_L);
1079
- Fr q_is_double = wire(p, WIRE.Q_M);
1080
-
1081
- // Contribution 10 point addition, x-coordinate check
1082
- // q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0
1083
- Fr x_diff = (ep.x_2 - ep.x_1);
1084
- Fr y1_sqr = (ep.y_1 * ep.y_1);
1085
- {
1086
- // Move to top
1087
- Fr partialEval = domainSep;
1088
-
1089
- Fr y2_sqr = (ep.y_2 * ep.y_2);
1090
- Fr y1y2 = ep.y_1 * ep.y_2 * q_sign;
1091
- Fr x_add_identity = (ep.x_3 + ep.x_2 + ep.x_1);
1092
- x_add_identity = x_add_identity * x_diff * x_diff;
1093
- x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2;
1094
-
1095
- evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double);
1096
- }
1097
-
1098
- // Contribution 11 point addition, x-coordinate check
1099
- // q_elliptic * (q_sign * y1 + y3)(x2 - x1) + (x3 - x1)(y2 - q_sign * y1) = 0
1100
- {
1101
- Fr y1_plus_y3 = ep.y_1 + ep.y_3;
1102
- Fr y_diff = ep.y_2 * q_sign - ep.y_1;
1103
- Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff;
1104
- evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double);
1105
- }
1106
-
1107
- // Contribution 10 point doubling, x-coordinate check
1108
- // (x3 + x1 + x1) (4y1*y1) - 9 * x1 * x1 * x1 * x1 = 0
1109
- // N.B. we're using the equivalence x1*x1*x1 === y1*y1 - curve_b to reduce degree by 1
1110
- {
1111
- Fr x_pow_4 = (y1_sqr + GRUMPKIN_CURVE_B_PARAMETER_NEGATED) * ep.x_1;
1112
- Fr y1_sqr_mul_4 = y1_sqr + y1_sqr;
1113
- y1_sqr_mul_4 = y1_sqr_mul_4 + y1_sqr_mul_4;
1114
- Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9);
1115
-
1116
- // NOTE: pushed into memory (stack >:'( )
1117
- ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9;
1118
-
1119
- Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double;
1120
- evals[11] = evals[11] + acc;
1121
- }
1122
-
1123
- // Contribution 11 point doubling, y-coordinate check
1124
- // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0
1125
- {
1126
- Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1;
1127
- Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3);
1128
- evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double;
1129
- }
1130
- }
1131
-
1132
- // Parameters used within the Memory Relation
1133
- // A struct is used to work around stack too deep. This relation has alot of variables
1134
- struct MemParams {
1135
- Fr memory_record_check;
1136
- Fr partial_record_check;
1137
- Fr next_gate_access_type;
1138
- Fr record_delta;
1139
- Fr index_delta;
1140
- Fr adjacent_values_match_if_adjacent_indices_match;
1141
- Fr adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation;
1142
- Fr access_check;
1143
- Fr next_gate_access_type_is_boolean;
1144
- Fr ROM_consistency_check_identity;
1145
- Fr RAM_consistency_check_identity;
1146
- Fr timestamp_delta;
1147
- Fr RAM_timestamp_check_identity;
1148
- Fr memory_identity;
1149
- Fr index_is_monotonically_increasing;
1150
- }
1151
-
1152
- function accumulateMemoryRelation(
1153
- Fr[NUMBER_OF_ENTITIES] memory p,
1154
- Honk.RelationParameters memory rp,
1155
- Fr[NUMBER_OF_SUBRELATIONS] memory evals,
1156
- Fr domainSep
1157
- ) internal pure {
1158
- MemParams memory ap;
1159
-
1160
- /**
1161
- * MEMORY
1162
- *
1163
- * A RAM memory record contains a tuple of the following fields:
1164
- * * i: `index` of memory cell being accessed
1165
- * * t: `timestamp` of memory cell being accessed (used for RAM, set to 0 for ROM)
1166
- * * v: `value` of memory cell being accessed
1167
- * * a: `access` type of record. read: 0 = read, 1 = write
1168
- * * r: `record` of memory cell. record = access + index * eta + timestamp * eta_two + value * eta_three
1169
- *
1170
- * A ROM memory record contains a tuple of the following fields:
1171
- * * i: `index` of memory cell being accessed
1172
- * * v: `value1` of memory cell being accessed (ROM tables can store up to 2 values per index)
1173
- * * v2:`value2` of memory cell being accessed (ROM tables can store up to 2 values per index)
1174
- * * r: `record` of memory cell. record = index * eta + value2 * eta_two + value1 * eta_three
1175
- *
1176
- * When performing a read/write access, the values of i, t, v, v2, a, r are stored in the following wires +
1177
- * selectors, depending on whether the gate is a RAM read/write or a ROM read
1178
- *
1179
- * | gate type | i | v2/t | v | a | r |
1180
- * | --------- | -- | ----- | -- | -- | -- |
1181
- * | ROM | w1 | w2 | w3 | -- | w4 |
1182
- * | RAM | w1 | w2 | w3 | qc | w4 |
1183
- *
1184
- * (for accesses where `index` is a circuit constant, it is assumed the circuit will apply a copy constraint on
1185
- * `w2` to fix its value)
1186
- *
1187
- *
1188
- */
1189
-
1190
- /**
1191
- * Memory Record Check
1192
- * Partial degree: 1
1193
- * Total degree: 4
1194
- *
1195
- * A ROM/ROM access gate can be evaluated with the identity:
1196
- *
1197
- * qc + w1 \eta + w2 \eta_two + w3 \eta_three - w4 = 0
1198
- *
1199
- * For ROM gates, qc = 0
1200
- */
1201
- ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree;
1202
- ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo);
1203
- ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta);
1204
- ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C);
1205
- ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4
1206
- ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4);
1207
-
1208
- /**
1209
- * Contribution 13 & 14
1210
- * ROM Consistency Check
1211
- * Partial degree: 1
1212
- * Total degree: 4
1213
- *
1214
- * For every ROM read, a set equivalence check is applied between the record witnesses, and a second set of
1215
- * records that are sorted.
1216
- *
1217
- * We apply the following checks for the sorted records:
1218
- *
1219
- * 1. w1, w2, w3 correctly map to 'index', 'v1, 'v2' for a given record value at w4
1220
- * 2. index values for adjacent records are monotonically increasing
1221
- * 3. if, at gate i, index_i == index_{i + 1}, then value1_i == value1_{i + 1} and value2_i == value2_{i + 1}
1222
- *
1223
- */
1224
- ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L);
1225
- ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4);
1226
-
1227
- ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2
1228
-
1229
- ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2
1230
-
1231
- evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R))
1232
- * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5
1233
- evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R))
1234
- * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5
1235
-
1236
- ap.ROM_consistency_check_identity = ap.memory_record_check * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)); // deg 3 or 7
1237
-
1238
- /**
1239
- * Contributions 15,16,17
1240
- * RAM Consistency Check
1241
- *
1242
- * The 'access' type of the record is extracted with the expression `w_4 - ap.partial_record_check`
1243
- * (i.e. for an honest Prover `w1 * eta + w2 * eta^2 + w3 * eta^3 - w4 = access`.
1244
- * This is validated by requiring `access` to be boolean
1245
- *
1246
- * For two adjacent entries in the sorted list if _both_
1247
- * A) index values match
1248
- * B) adjacent access value is 0 (i.e. next gate is a READ)
1249
- * then
1250
- * C) both values must match.
1251
- * The gate boolean check is
1252
- * (A && B) => C === !(A && B) || C === !A || !B || C
1253
- *
1254
- * N.B. it is the responsibility of the circuit writer to ensure that every RAM cell is initialized
1255
- * with a WRITE operation.
1256
- */
1257
- Fr access_type = (wire(p, WIRE.W_4) - ap.partial_record_check); // will be 0 or 1 for honest Prover; deg 1 or 4
1258
- ap.access_check = access_type * (access_type - Fr.wrap(1)); // check value is 0 or 1; deg 2 or 8
1259
-
1260
- // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta
1261
- // deg 1 or 4
1262
- ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree;
1263
- ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo);
1264
- ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta);
1265
- ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type;
1266
-
1267
- Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O);
1268
- ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation =
1269
- (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6
1270
-
1271
- // We can't apply the RAM consistency check identity on the final entry in the sorted list (the wires in the
1272
- // next gate would make the identity fail). We need to validate that its 'access type' bool is correct. Can't
1273
- // do with an arithmetic gate because of the `eta` factors. We need to check that the *next* gate's access
1274
- // type is correct, to cover this edge case
1275
- // deg 2 or 4
1276
- ap.next_gate_access_type_is_boolean =
1277
- ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type;
1278
-
1279
- // Putting it all together...
1280
- evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation
1281
- * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8
1282
- evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4
1283
- evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6
1284
-
1285
- ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9
1286
-
1287
- /**
1288
- * RAM Timestamp Consistency Check
1289
- *
1290
- * | w1 | w2 | w3 | w4 |
1291
- * | index | timestamp | timestamp_check | -- |
1292
- *
1293
- * Let delta_index = index_{i + 1} - index_{i}
1294
- *
1295
- * Iff delta_index == 0, timestamp_check = timestamp_{i + 1} - timestamp_i
1296
- * Else timestamp_check = 0
1297
- */
1298
- ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R);
1299
- ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3
1300
-
1301
- /**
1302
- * Complete Contribution 12
1303
- * The complete RAM/ROM memory identity
1304
- * Partial degree:
1305
- */
1306
- ap.memory_identity = ap.ROM_consistency_check_identity; // deg 3 or 6
1307
- ap.memory_identity =
1308
- ap.memory_identity + ap.RAM_timestamp_check_identity * (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4
1309
- ap.memory_identity = ap.memory_identity + ap.memory_record_check * (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6
1310
- ap.memory_identity = ap.memory_identity + ap.RAM_consistency_check_identity; // deg 3 or 9
1311
-
1312
- // (deg 3 or 9) + (deg 4) + (deg 3)
1313
- ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10
1314
- evals[13] = ap.memory_identity;
1315
- }
1316
-
1317
- // Constants for the Non-native Field relation
1318
- Fr constant LIMB_SIZE = Fr.wrap(uint256(1) << 68);
1319
- Fr constant SUBLIMB_SHIFT = Fr.wrap(uint256(1) << 14);
1320
-
1321
- // Parameters used within the Non-Native Field Relation
1322
- // A struct is used to work around stack too deep. This relation has alot of variables
1323
- struct NnfParams {
1324
- Fr limb_subproduct;
1325
- Fr non_native_field_gate_1;
1326
- Fr non_native_field_gate_2;
1327
- Fr non_native_field_gate_3;
1328
- Fr limb_accumulator_1;
1329
- Fr limb_accumulator_2;
1330
- Fr nnf_identity;
1331
- }
1332
-
1333
- function accumulateNnfRelation(
1334
- Fr[NUMBER_OF_ENTITIES] memory p,
1335
- Fr[NUMBER_OF_SUBRELATIONS] memory evals,
1336
- Fr domainSep
1337
- ) internal pure {
1338
- NnfParams memory ap;
1339
-
1340
- /**
1341
- * Contribution 12
1342
- * Non native field arithmetic gate 2
1343
- * deg 4
1344
- *
1345
- * _ _
1346
- * / _ _ _ 14 \
1347
- * 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 |
1348
- * \_ _/
1349
- *
1350
- *
1351
- */
1352
- ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R);
1353
- ap.non_native_field_gate_2 =
1354
- (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT));
1355
- ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE;
1356
- ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT);
1357
- ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct;
1358
- ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4);
1359
-
1360
- ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE;
1361
- ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT));
1362
- ap.non_native_field_gate_1 = ap.limb_subproduct;
1363
- ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4));
1364
- ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O);
1365
-
1366
- ap.non_native_field_gate_3 = ap.limb_subproduct;
1367
- ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4);
1368
- ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT));
1369
- ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M);
1370
-
1371
- Fr non_native_field_identity =
1372
- ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3;
1373
- non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R);
1374
-
1375
- // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm
1376
- // deg 2
1377
- ap.limb_accumulator_1 = wire(p, WIRE.W_R_SHIFT) * SUBLIMB_SHIFT;
1378
- ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L_SHIFT);
1379
- ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT;
1380
- ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_O);
1381
- ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT;
1382
- ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_R);
1383
- ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT;
1384
- ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L);
1385
- ap.limb_accumulator_1 = ap.limb_accumulator_1 - wire(p, WIRE.W_4);
1386
- ap.limb_accumulator_1 = ap.limb_accumulator_1 * wire(p, WIRE.Q_4);
1387
-
1388
- // ((((w3' * 2^14 + w2') * 2^14 + w1') * 2^14 + w4) * 2^14 + w3 - w4') * qm
1389
- // deg 2
1390
- ap.limb_accumulator_2 = wire(p, WIRE.W_O_SHIFT) * SUBLIMB_SHIFT;
1391
- ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_R_SHIFT);
1392
- ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT;
1393
- ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_L_SHIFT);
1394
- ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT;
1395
- ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_4);
1396
- ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT;
1397
- ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_O);
1398
- ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT);
1399
- ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M);
1400
-
1401
- Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2;
1402
- limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3
1403
-
1404
- ap.nnf_identity = non_native_field_identity + limb_accumulator_identity;
1405
- ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep);
1406
- evals[19] = ap.nnf_identity;
1407
- }
1408
-
1409
- struct PoseidonExternalParams {
1410
- Fr s1;
1411
- Fr s2;
1412
- Fr s3;
1413
- Fr s4;
1414
- Fr u1;
1415
- Fr u2;
1416
- Fr u3;
1417
- Fr u4;
1418
- Fr t0;
1419
- Fr t1;
1420
- Fr t2;
1421
- Fr t3;
1422
- Fr v1;
1423
- Fr v2;
1424
- Fr v3;
1425
- Fr v4;
1426
- Fr q_pos_by_scaling;
1427
- }
1428
-
1429
- function accumulatePoseidonExternalRelation(
1430
- Fr[NUMBER_OF_ENTITIES] memory p,
1431
- Fr[NUMBER_OF_SUBRELATIONS] memory evals,
1432
- Fr domainSep
1433
- ) internal pure {
1434
- PoseidonExternalParams memory ep;
1435
-
1436
- ep.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L);
1437
- ep.s2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_R);
1438
- ep.s3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_O);
1439
- ep.s4 = wire(p, WIRE.W_4) + wire(p, WIRE.Q_4);
1440
-
1441
- ep.u1 = ep.s1 * ep.s1 * ep.s1 * ep.s1 * ep.s1;
1442
- ep.u2 = ep.s2 * ep.s2 * ep.s2 * ep.s2 * ep.s2;
1443
- ep.u3 = ep.s3 * ep.s3 * ep.s3 * ep.s3 * ep.s3;
1444
- ep.u4 = ep.s4 * ep.s4 * ep.s4 * ep.s4 * ep.s4;
1445
- // matrix mul v = M_E * u with 14 additions
1446
- ep.t0 = ep.u1 + ep.u2; // u_1 + u_2
1447
- ep.t1 = ep.u3 + ep.u4; // u_3 + u_4
1448
- ep.t2 = ep.u2 + ep.u2 + ep.t1; // 2u_2
1449
- // ep.t2 += ep.t1; // 2u_2 + u_3 + u_4
1450
- ep.t3 = ep.u4 + ep.u4 + ep.t0; // 2u_4
1451
- // ep.t3 += ep.t0; // u_1 + u_2 + 2u_4
1452
- ep.v4 = ep.t1 + ep.t1;
1453
- ep.v4 = ep.v4 + ep.v4 + ep.t3;
1454
- // ep.v4 += ep.t3; // u_1 + u_2 + 4u_3 + 6u_4
1455
- ep.v2 = ep.t0 + ep.t0;
1456
- ep.v2 = ep.v2 + ep.v2 + ep.t2;
1457
- // ep.v2 += ep.t2; // 4u_1 + 6u_2 + u_3 + u_4
1458
- ep.v1 = ep.t3 + ep.v2; // 5u_1 + 7u_2 + u_3 + 3u_4
1459
- ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4
1460
-
1461
- ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep;
1462
- evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT));
1463
-
1464
- evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT));
1465
-
1466
- evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT));
1467
-
1468
- evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT));
1469
- }
1470
-
1471
- struct PoseidonInternalParams {
1472
- Fr u1;
1473
- Fr u2;
1474
- Fr u3;
1475
- Fr u4;
1476
- Fr u_sum;
1477
- Fr v1;
1478
- Fr v2;
1479
- Fr v3;
1480
- Fr v4;
1481
- Fr s1;
1482
- Fr q_pos_by_scaling;
1483
- }
1484
-
1485
- function accumulatePoseidonInternalRelation(
1486
- Fr[NUMBER_OF_ENTITIES] memory p,
1487
- Fr[NUMBER_OF_SUBRELATIONS] memory evals,
1488
- Fr domainSep
1489
- ) internal pure {
1490
- PoseidonInternalParams memory ip;
1491
-
1492
- Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [
1493
- FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7),
1494
- FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b),
1495
- FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15),
1496
- FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b)
1497
- ];
1498
-
1499
- // add round constants
1500
- ip.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L);
1501
-
1502
- // apply s-box round
1503
- ip.u1 = ip.s1 * ip.s1 * ip.s1 * ip.s1 * ip.s1;
1504
- ip.u2 = wire(p, WIRE.W_R);
1505
- ip.u3 = wire(p, WIRE.W_O);
1506
- ip.u4 = wire(p, WIRE.W_4);
1507
-
1508
- // matrix mul with v = M_I * u 4 muls and 7 additions
1509
- ip.u_sum = ip.u1 + ip.u2 + ip.u3 + ip.u4;
1510
-
1511
- ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep;
1512
-
1513
- ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum;
1514
- evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT));
1515
-
1516
- ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum;
1517
- evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT));
1518
-
1519
- ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum;
1520
- evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT));
1521
-
1522
- ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum;
1523
- evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT));
1524
- }
1525
-
1526
- function scaleAndBatchSubrelations(
1527
- Fr[NUMBER_OF_SUBRELATIONS] memory evaluations,
1528
- Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges
1529
- ) internal pure returns (Fr accumulator) {
1530
- accumulator = evaluations[0];
1531
-
1532
- for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) {
1533
- accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1];
1534
- }
1535
- }
1232
+ ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2
1233
+
1234
+ ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2
1235
+
1236
+ evals[14] =
1237
+ ap.adjacent_values_match_if_adjacent_indices_match *
1238
+ (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) *
1239
+ (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5
1240
+ evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5
1241
+
1242
+ ap.ROM_consistency_check_identity = ap.memory_record_check * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)); // deg 3 or 7
1243
+
1244
+ /**
1245
+ * Contributions 15,16,17
1246
+ * RAM Consistency Check
1247
+ *
1248
+ * The 'access' type of the record is extracted with the expression `w_4 - ap.partial_record_check`
1249
+ * (i.e. for an honest Prover `w1 * eta + w2 * eta^2 + w3 * eta^3 - w4 = access`.
1250
+ * This is validated by requiring `access` to be boolean
1251
+ *
1252
+ * For two adjacent entries in the sorted list if _both_
1253
+ * A) index values match
1254
+ * B) adjacent access value is 0 (i.e. next gate is a READ)
1255
+ * then
1256
+ * C) both values must match.
1257
+ * The gate boolean check is
1258
+ * (A && B) => C === !(A && B) || C === !A || !B || C
1259
+ *
1260
+ * N.B. it is the responsibility of the circuit writer to ensure that every RAM cell is initialized
1261
+ * with a WRITE operation.
1262
+ */
1263
+ Fr access_type = (wire(p, WIRE.W_4) - ap.partial_record_check); // will be 0 or 1 for honest Prover; deg 1 or 4
1264
+ ap.access_check = access_type * (access_type - Fr.wrap(1)); // check value is 0 or 1; deg 2 or 8
1265
+
1266
+ // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta
1267
+ // deg 1 or 4
1268
+ ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree;
1269
+ ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo);
1270
+ ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta);
1271
+ ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type;
1272
+
1273
+ Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O);
1274
+ ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation =
1275
+ (ap.index_delta * MINUS_ONE + ONE) *
1276
+ value_delta *
1277
+ (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6
1278
+
1279
+ // We can't apply the RAM consistency check identity on the final entry in the sorted list (the wires in the
1280
+ // next gate would make the identity fail). We need to validate that its 'access type' bool is correct. Can't
1281
+ // do with an arithmetic gate because of the `eta` factors. We need to check that the *next* gate's access
1282
+ // type is correct, to cover this edge case
1283
+ // deg 2 or 4
1284
+ ap.next_gate_access_type_is_boolean = ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type;
1285
+
1286
+ // Putting it all together...
1287
+ evals[16] =
1288
+ ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation *
1289
+ (wire(p, WIRE.Q_O)) *
1290
+ (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8
1291
+ evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4
1292
+ evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6
1293
+
1294
+ ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9
1295
+
1296
+ /**
1297
+ * RAM Timestamp Consistency Check
1298
+ *
1299
+ * | w1 | w2 | w3 | w4 |
1300
+ * | index | timestamp | timestamp_check | -- |
1301
+ *
1302
+ * Let delta_index = index_{i + 1} - index_{i}
1303
+ *
1304
+ * Iff delta_index == 0, timestamp_check = timestamp_{i + 1} - timestamp_i
1305
+ * Else timestamp_check = 0
1306
+ */
1307
+ ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R);
1308
+ ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3
1309
+
1310
+ /**
1311
+ * Complete Contribution 12
1312
+ * The complete RAM/ROM memory identity
1313
+ * Partial degree:
1314
+ */
1315
+ ap.memory_identity = ap.ROM_consistency_check_identity; // deg 3 or 6
1316
+ ap.memory_identity = ap.memory_identity + ap.RAM_timestamp_check_identity * (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4
1317
+ ap.memory_identity = ap.memory_identity + ap.memory_record_check * (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6
1318
+ ap.memory_identity = ap.memory_identity + ap.RAM_consistency_check_identity; // deg 3 or 9
1319
+
1320
+ // (deg 3 or 9) + (deg 4) + (deg 3)
1321
+ ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10
1322
+ evals[13] = ap.memory_identity;
1323
+ }
1324
+
1325
+ // Constants for the Non-native Field relation
1326
+ Fr constant LIMB_SIZE = Fr.wrap(uint256(1) << 68);
1327
+ Fr constant SUBLIMB_SHIFT = Fr.wrap(uint256(1) << 14);
1328
+
1329
+ // Parameters used within the Non-Native Field Relation
1330
+ // A struct is used to work around stack too deep. This relation has alot of variables
1331
+ struct NnfParams {
1332
+ Fr limb_subproduct;
1333
+ Fr non_native_field_gate_1;
1334
+ Fr non_native_field_gate_2;
1335
+ Fr non_native_field_gate_3;
1336
+ Fr limb_accumulator_1;
1337
+ Fr limb_accumulator_2;
1338
+ Fr nnf_identity;
1339
+ }
1340
+
1341
+ function accumulateNnfRelation(Fr[NUMBER_OF_ENTITIES] memory p, Fr[NUMBER_OF_SUBRELATIONS] memory evals, Fr domainSep) internal pure {
1342
+ NnfParams memory ap;
1343
+
1344
+ /**
1345
+ * Contribution 12
1346
+ * Non native field arithmetic gate 2
1347
+ * deg 4
1348
+ *
1349
+ * _ _
1350
+ * / _ _ _ 14 \
1351
+ * 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 |
1352
+ * \_ _/
1353
+ *
1354
+ *
1355
+ */
1356
+ ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R);
1357
+ ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT));
1358
+ ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE;
1359
+ ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT);
1360
+ ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct;
1361
+ ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4);
1362
+
1363
+ ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE;
1364
+ ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT));
1365
+ ap.non_native_field_gate_1 = ap.limb_subproduct;
1366
+ ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4));
1367
+ ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O);
1368
+
1369
+ ap.non_native_field_gate_3 = ap.limb_subproduct;
1370
+ ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4);
1371
+ ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT));
1372
+ ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M);
1373
+
1374
+ Fr non_native_field_identity = ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3;
1375
+ non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R);
1376
+
1377
+ // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm
1378
+ // deg 2
1379
+ ap.limb_accumulator_1 = wire(p, WIRE.W_R_SHIFT) * SUBLIMB_SHIFT;
1380
+ ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L_SHIFT);
1381
+ ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT;
1382
+ ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_O);
1383
+ ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT;
1384
+ ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_R);
1385
+ ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT;
1386
+ ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L);
1387
+ ap.limb_accumulator_1 = ap.limb_accumulator_1 - wire(p, WIRE.W_4);
1388
+ ap.limb_accumulator_1 = ap.limb_accumulator_1 * wire(p, WIRE.Q_4);
1389
+
1390
+ // ((((w3' * 2^14 + w2') * 2^14 + w1') * 2^14 + w4) * 2^14 + w3 - w4') * qm
1391
+ // deg 2
1392
+ ap.limb_accumulator_2 = wire(p, WIRE.W_O_SHIFT) * SUBLIMB_SHIFT;
1393
+ ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_R_SHIFT);
1394
+ ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT;
1395
+ ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_L_SHIFT);
1396
+ ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT;
1397
+ ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_4);
1398
+ ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT;
1399
+ ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_O);
1400
+ ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT);
1401
+ ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M);
1402
+
1403
+ Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2;
1404
+ limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3
1405
+
1406
+ ap.nnf_identity = non_native_field_identity + limb_accumulator_identity;
1407
+ ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep);
1408
+ evals[19] = ap.nnf_identity;
1409
+ }
1410
+
1411
+ struct PoseidonExternalParams {
1412
+ Fr s1;
1413
+ Fr s2;
1414
+ Fr s3;
1415
+ Fr s4;
1416
+ Fr u1;
1417
+ Fr u2;
1418
+ Fr u3;
1419
+ Fr u4;
1420
+ Fr t0;
1421
+ Fr t1;
1422
+ Fr t2;
1423
+ Fr t3;
1424
+ Fr v1;
1425
+ Fr v2;
1426
+ Fr v3;
1427
+ Fr v4;
1428
+ Fr q_pos_by_scaling;
1429
+ }
1430
+
1431
+ function accumulatePoseidonExternalRelation(
1432
+ Fr[NUMBER_OF_ENTITIES] memory p,
1433
+ Fr[NUMBER_OF_SUBRELATIONS] memory evals,
1434
+ Fr domainSep
1435
+ ) internal pure {
1436
+ PoseidonExternalParams memory ep;
1437
+
1438
+ ep.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L);
1439
+ ep.s2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_R);
1440
+ ep.s3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_O);
1441
+ ep.s4 = wire(p, WIRE.W_4) + wire(p, WIRE.Q_4);
1442
+
1443
+ ep.u1 = ep.s1 * ep.s1 * ep.s1 * ep.s1 * ep.s1;
1444
+ ep.u2 = ep.s2 * ep.s2 * ep.s2 * ep.s2 * ep.s2;
1445
+ ep.u3 = ep.s3 * ep.s3 * ep.s3 * ep.s3 * ep.s3;
1446
+ ep.u4 = ep.s4 * ep.s4 * ep.s4 * ep.s4 * ep.s4;
1447
+ // matrix mul v = M_E * u with 14 additions
1448
+ ep.t0 = ep.u1 + ep.u2; // u_1 + u_2
1449
+ ep.t1 = ep.u3 + ep.u4; // u_3 + u_4
1450
+ ep.t2 = ep.u2 + ep.u2 + ep.t1; // 2u_2
1451
+ // ep.t2 += ep.t1; // 2u_2 + u_3 + u_4
1452
+ ep.t3 = ep.u4 + ep.u4 + ep.t0; // 2u_4
1453
+ // ep.t3 += ep.t0; // u_1 + u_2 + 2u_4
1454
+ ep.v4 = ep.t1 + ep.t1;
1455
+ ep.v4 = ep.v4 + ep.v4 + ep.t3;
1456
+ // ep.v4 += ep.t3; // u_1 + u_2 + 4u_3 + 6u_4
1457
+ ep.v2 = ep.t0 + ep.t0;
1458
+ ep.v2 = ep.v2 + ep.v2 + ep.t2;
1459
+ // ep.v2 += ep.t2; // 4u_1 + 6u_2 + u_3 + u_4
1460
+ ep.v1 = ep.t3 + ep.v2; // 5u_1 + 7u_2 + u_3 + 3u_4
1461
+ ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4
1462
+
1463
+ ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep;
1464
+ evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT));
1465
+
1466
+ evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT));
1467
+
1468
+ evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT));
1469
+
1470
+ evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT));
1471
+ }
1472
+
1473
+ struct PoseidonInternalParams {
1474
+ Fr u1;
1475
+ Fr u2;
1476
+ Fr u3;
1477
+ Fr u4;
1478
+ Fr u_sum;
1479
+ Fr v1;
1480
+ Fr v2;
1481
+ Fr v3;
1482
+ Fr v4;
1483
+ Fr s1;
1484
+ Fr q_pos_by_scaling;
1485
+ }
1486
+
1487
+ function accumulatePoseidonInternalRelation(
1488
+ Fr[NUMBER_OF_ENTITIES] memory p,
1489
+ Fr[NUMBER_OF_SUBRELATIONS] memory evals,
1490
+ Fr domainSep
1491
+ ) internal pure {
1492
+ PoseidonInternalParams memory ip;
1493
+
1494
+ Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [
1495
+ FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7),
1496
+ FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b),
1497
+ FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15),
1498
+ FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b)
1499
+ ];
1500
+
1501
+ // add round constants
1502
+ ip.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L);
1503
+
1504
+ // apply s-box round
1505
+ ip.u1 = ip.s1 * ip.s1 * ip.s1 * ip.s1 * ip.s1;
1506
+ ip.u2 = wire(p, WIRE.W_R);
1507
+ ip.u3 = wire(p, WIRE.W_O);
1508
+ ip.u4 = wire(p, WIRE.W_4);
1509
+
1510
+ // matrix mul with v = M_I * u 4 muls and 7 additions
1511
+ ip.u_sum = ip.u1 + ip.u2 + ip.u3 + ip.u4;
1512
+
1513
+ ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep;
1514
+
1515
+ ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum;
1516
+ evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT));
1517
+
1518
+ ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum;
1519
+ evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT));
1520
+
1521
+ ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum;
1522
+ evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT));
1523
+
1524
+ ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum;
1525
+ evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT));
1526
+ }
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
1530
+ function scaleAndBatchSubrelations(
1531
+ Fr[NUMBER_OF_SUBRELATIONS] memory evaluations,
1532
+ Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges
1533
+ ) internal pure returns (Fr accumulator) {
1534
+ accumulator = evaluations[0];
1535
+
1536
+ for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) {
1537
+ accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1];
1538
+ }
1539
+ }
1536
1540
  }
1537
1541
 
1538
1542
  // Field arithmetic libraries - prevent littering the code with modmul / addmul
1539
1543
 
1540
1544
  library CommitmentSchemeLib {
1541
- using FrLib for Fr;
1542
-
1543
- // Avoid stack too deep
1544
- struct ShpleminiIntermediates {
1545
- Fr unshiftedScalar;
1546
- Fr shiftedScalar;
1547
- Fr unshiftedScalarNeg;
1548
- Fr shiftedScalarNeg;
1549
- // Scalar to be multiplied by [1]₁
1550
- Fr constantTermAccumulator;
1551
- // Accumulator for powers of rho
1552
- Fr batchingChallenge;
1553
- // Linear combination of multilinear (sumcheck) evaluations and powers of rho
1554
- Fr batchedEvaluation;
1555
- Fr[4] denominators;
1556
- Fr[4] batchingScalars;
1557
- // 1/(z - r^{2^i}) for i = 0, ..., logSize, dynamically updated
1558
- Fr posInvertedDenominator;
1559
- // 1/(z + r^{2^i}) for i = 0, ..., logSize, dynamically updated
1560
- Fr negInvertedDenominator;
1561
- // ν^{2i} * 1/(z - r^{2^i})
1562
- Fr scalingFactorPos;
1563
- // ν^{2i+1} * 1/(z + r^{2^i})
1564
- Fr scalingFactorNeg;
1565
- // Fold_i(r^{2^i}) reconstructed by Verifier
1566
- Fr[] foldPosEvaluations;
1567
- }
1568
-
1569
- function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) {
1570
- Fr[] memory squares = new Fr[](logN);
1571
- squares[0] = r;
1572
- for (uint256 i = 1; i < logN; ++i) {
1573
- squares[i] = squares[i - 1].sqr();
1574
- }
1575
- return squares;
1576
- }
1577
- // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., m-1
1578
-
1579
- function computeFoldPosEvaluations(
1580
- Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckUChallenges,
1581
- Fr batchedEvalAccumulator,
1582
- Fr[CONST_PROOF_SIZE_LOG_N] memory geminiEvaluations,
1583
- Fr[] memory geminiEvalChallengePowers,
1584
- uint256 logSize
1585
- ) internal view returns (Fr[] memory) {
1586
- Fr[] memory foldPosEvaluations = new Fr[](logSize);
1587
- for (uint256 i = logSize; i > 0; --i) {
1588
- Fr challengePower = geminiEvalChallengePowers[i - 1];
1589
- Fr u = sumcheckUChallenges[i - 1];
1590
-
1591
- Fr batchedEvalRoundAcc = (
1592
- (challengePower * batchedEvalAccumulator * Fr.wrap(2))
1593
- - geminiEvaluations[i - 1] * (challengePower * (ONE - u) - u)
1594
- );
1595
- // Divide by the denominator
1596
- batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert();
1597
-
1598
- batchedEvalAccumulator = batchedEvalRoundAcc;
1599
- foldPosEvaluations[i - 1] = batchedEvalRoundAcc;
1600
- }
1601
- return foldPosEvaluations;
1602
- }
1545
+ using FrLib for Fr;
1546
+
1547
+ // Avoid stack too deep
1548
+ struct ShpleminiIntermediates {
1549
+ Fr unshiftedScalar;
1550
+ Fr shiftedScalar;
1551
+ Fr unshiftedScalarNeg;
1552
+ Fr shiftedScalarNeg;
1553
+ // Scalar to be multiplied by [1]₁
1554
+ Fr constantTermAccumulator;
1555
+ // Accumulator for powers of rho
1556
+ Fr batchingChallenge;
1557
+ // Linear combination of multilinear (sumcheck) evaluations and powers of rho
1558
+ Fr batchedEvaluation;
1559
+ Fr[4] denominators;
1560
+ Fr[4] batchingScalars;
1561
+ // 1/(z - r^{2^i}) for i = 0, ..., logSize, dynamically updated
1562
+ Fr posInvertedDenominator;
1563
+ // 1/(z + r^{2^i}) for i = 0, ..., logSize, dynamically updated
1564
+ Fr negInvertedDenominator;
1565
+ // ν^{2i} * 1/(z - r^{2^i})
1566
+ Fr scalingFactorPos;
1567
+ // ν^{2i+1} * 1/(z + r^{2^i})
1568
+ Fr scalingFactorNeg;
1569
+ // Fold_i(r^{2^i}) reconstructed by Verifier
1570
+ Fr[] foldPosEvaluations;
1571
+ }
1572
+
1573
+ function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) {
1574
+ Fr[] memory squares = new Fr[](logN);
1575
+ squares[0] = r;
1576
+ for (uint256 i = 1; i < logN; ++i) {
1577
+ squares[i] = squares[i - 1].sqr();
1578
+ }
1579
+ return squares;
1580
+ }
1581
+ // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., m-1
1582
+
1583
+ function computeFoldPosEvaluations(
1584
+ Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckUChallenges,
1585
+ Fr batchedEvalAccumulator,
1586
+ Fr[CONST_PROOF_SIZE_LOG_N] memory geminiEvaluations,
1587
+ Fr[] memory geminiEvalChallengePowers,
1588
+ uint256 logSize
1589
+ ) internal view returns (Fr[] memory) {
1590
+ Fr[] memory foldPosEvaluations = new Fr[](logSize);
1591
+ for (uint256 i = logSize; i > 0; --i) {
1592
+ Fr challengePower = geminiEvalChallengePowers[i - 1];
1593
+ Fr u = sumcheckUChallenges[i - 1];
1594
+
1595
+ Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) -
1596
+ geminiEvaluations[i - 1] *
1597
+ (challengePower * (ONE - u) - u));
1598
+ // Divide by the denominator
1599
+ batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert();
1600
+
1601
+ batchedEvalAccumulator = batchedEvalRoundAcc;
1602
+ foldPosEvaluations[i - 1] = batchedEvalRoundAcc;
1603
+ }
1604
+ return foldPosEvaluations;
1605
+ }
1603
1606
  }
1604
1607
 
1605
1608
  uint256 constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; // EC group order. F_q
1606
1609
 
1607
1610
  function bytes32ToString(bytes32 value) pure returns (string memory result) {
1608
- bytes memory alphabet = "0123456789abcdef";
1609
-
1610
- bytes memory str = new bytes(66);
1611
- str[0] = "0";
1612
- str[1] = "x";
1613
- for (uint256 i = 0; i < 32; i++) {
1614
- str[2 + i * 2] = alphabet[uint8(value[i] >> 4)];
1615
- str[3 + i * 2] = alphabet[uint8(value[i] & 0x0f)];
1616
- }
1617
- result = string(str);
1611
+ bytes memory alphabet = "0123456789abcdef";
1612
+
1613
+ bytes memory str = new bytes(66);
1614
+ str[0] = "0";
1615
+ str[1] = "x";
1616
+ for (uint256 i = 0; i < 32; i++) {
1617
+ str[2 + i * 2] = alphabet[uint8(value[i] >> 4)];
1618
+ str[3 + i * 2] = alphabet[uint8(value[i] & 0x0f)];
1619
+ }
1620
+ result = string(str);
1618
1621
  }
1619
1622
 
1620
1623
  // Fr utility
1621
1624
 
1622
1625
  function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) {
1623
- scalar = FrLib.fromBytes32(bytes32(proofSection));
1626
+ scalar = FrLib.fromBytes32(bytes32(proofSection));
1624
1627
  }
1625
1628
 
1626
1629
  // EC Point utilities
1627
1630
  function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) {
1628
- point = Honk.G1Point({
1629
- x: uint256(bytes32(proofSection[0x00:0x20])) % Q,
1630
- y: uint256(bytes32(proofSection[0x20:0x40])) % Q
1631
- });
1631
+ point = Honk.G1Point({ x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q });
1632
1632
  }
1633
1633
 
1634
1634
  function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) {
1635
- point.y = (Q - point.y) % Q;
1636
- return point;
1635
+ point.y = (Q - point.y) % Q;
1636
+ return point;
1637
1637
  }
1638
1638
 
1639
1639
  /**
@@ -1648,33 +1648,32 @@ function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point mem
1648
1648
  * @return lhs
1649
1649
  * @return rhs
1650
1650
  */
1651
- function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints)
1652
- pure
1653
- returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs)
1654
- {
1655
- uint256 lhsX = Fr.unwrap(pairingPoints[0]);
1656
- lhsX |= Fr.unwrap(pairingPoints[1]) << 68;
1657
- lhsX |= Fr.unwrap(pairingPoints[2]) << 136;
1658
- lhsX |= Fr.unwrap(pairingPoints[3]) << 204;
1659
- lhs.x = lhsX;
1660
-
1661
- uint256 lhsY = Fr.unwrap(pairingPoints[4]);
1662
- lhsY |= Fr.unwrap(pairingPoints[5]) << 68;
1663
- lhsY |= Fr.unwrap(pairingPoints[6]) << 136;
1664
- lhsY |= Fr.unwrap(pairingPoints[7]) << 204;
1665
- lhs.y = lhsY;
1666
-
1667
- uint256 rhsX = Fr.unwrap(pairingPoints[8]);
1668
- rhsX |= Fr.unwrap(pairingPoints[9]) << 68;
1669
- rhsX |= Fr.unwrap(pairingPoints[10]) << 136;
1670
- rhsX |= Fr.unwrap(pairingPoints[11]) << 204;
1671
- rhs.x = rhsX;
1672
-
1673
- uint256 rhsY = Fr.unwrap(pairingPoints[12]);
1674
- rhsY |= Fr.unwrap(pairingPoints[13]) << 68;
1675
- rhsY |= Fr.unwrap(pairingPoints[14]) << 136;
1676
- rhsY |= Fr.unwrap(pairingPoints[15]) << 204;
1677
- rhs.y = rhsY;
1651
+ function convertPairingPointsToG1(
1652
+ Fr[PAIRING_POINTS_SIZE] memory pairingPoints
1653
+ ) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) {
1654
+ uint256 lhsX = Fr.unwrap(pairingPoints[0]);
1655
+ lhsX |= Fr.unwrap(pairingPoints[1]) << 68;
1656
+ lhsX |= Fr.unwrap(pairingPoints[2]) << 136;
1657
+ lhsX |= Fr.unwrap(pairingPoints[3]) << 204;
1658
+ lhs.x = lhsX;
1659
+
1660
+ uint256 lhsY = Fr.unwrap(pairingPoints[4]);
1661
+ lhsY |= Fr.unwrap(pairingPoints[5]) << 68;
1662
+ lhsY |= Fr.unwrap(pairingPoints[6]) << 136;
1663
+ lhsY |= Fr.unwrap(pairingPoints[7]) << 204;
1664
+ lhs.y = lhsY;
1665
+
1666
+ uint256 rhsX = Fr.unwrap(pairingPoints[8]);
1667
+ rhsX |= Fr.unwrap(pairingPoints[9]) << 68;
1668
+ rhsX |= Fr.unwrap(pairingPoints[10]) << 136;
1669
+ rhsX |= Fr.unwrap(pairingPoints[11]) << 204;
1670
+ rhs.x = rhsX;
1671
+
1672
+ uint256 rhsY = Fr.unwrap(pairingPoints[12]);
1673
+ rhsY |= Fr.unwrap(pairingPoints[13]) << 68;
1674
+ rhsY |= Fr.unwrap(pairingPoints[14]) << 136;
1675
+ rhsY |= Fr.unwrap(pairingPoints[15]) << 204;
1676
+ rhs.y = rhsY;
1678
1677
  }
1679
1678
 
1680
1679
  /**
@@ -1686,32 +1685,32 @@ function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints)
1686
1685
  * @return recursionSeparator The recursion separator - generated from hashing the above.
1687
1686
  */
1688
1687
  function generateRecursionSeparator(
1689
- Fr[PAIRING_POINTS_SIZE] memory proofPairingPoints,
1690
- Honk.G1Point memory accLhs,
1691
- Honk.G1Point memory accRhs
1688
+ Fr[PAIRING_POINTS_SIZE] memory proofPairingPoints,
1689
+ Honk.G1Point memory accLhs,
1690
+ Honk.G1Point memory accRhs
1692
1691
  ) pure returns (Fr recursionSeparator) {
1693
- // hash the proof aggregated X
1694
- // hash the proof aggregated Y
1695
- // hash the accum X
1696
- // hash the accum Y
1692
+ // hash the proof aggregated X
1693
+ // hash the proof aggregated Y
1694
+ // hash the accum X
1695
+ // hash the accum Y
1697
1696
 
1698
- (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints);
1697
+ (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints);
1699
1698
 
1700
- uint256[8] memory recursionSeparatorElements;
1699
+ uint256[8] memory recursionSeparatorElements;
1701
1700
 
1702
- // Proof points
1703
- recursionSeparatorElements[0] = proofLhs.x;
1704
- recursionSeparatorElements[1] = proofLhs.y;
1705
- recursionSeparatorElements[2] = proofRhs.x;
1706
- recursionSeparatorElements[3] = proofRhs.y;
1701
+ // Proof points
1702
+ recursionSeparatorElements[0] = proofLhs.x;
1703
+ recursionSeparatorElements[1] = proofLhs.y;
1704
+ recursionSeparatorElements[2] = proofRhs.x;
1705
+ recursionSeparatorElements[3] = proofRhs.y;
1707
1706
 
1708
- // Accumulator points
1709
- recursionSeparatorElements[4] = accLhs.x;
1710
- recursionSeparatorElements[5] = accLhs.y;
1711
- recursionSeparatorElements[6] = accRhs.x;
1712
- recursionSeparatorElements[7] = accRhs.y;
1707
+ // Accumulator points
1708
+ recursionSeparatorElements[4] = accLhs.x;
1709
+ recursionSeparatorElements[5] = accLhs.y;
1710
+ recursionSeparatorElements[6] = accRhs.x;
1711
+ recursionSeparatorElements[7] = accRhs.y;
1713
1712
 
1714
- recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements)));
1713
+ recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements)));
1715
1714
  }
1716
1715
 
1717
1716
  /**
@@ -1723,16 +1722,17 @@ function generateRecursionSeparator(
1723
1722
  * @param recursionSeperator The separator to use for the multiplication.
1724
1723
  * @return `(recursionSeperator * basePoint) + other`.
1725
1724
  */
1726
- function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator)
1727
- view
1728
- returns (Honk.G1Point memory)
1729
- {
1730
- Honk.G1Point memory result;
1725
+ function mulWithSeperator(
1726
+ Honk.G1Point memory basePoint,
1727
+ Honk.G1Point memory other,
1728
+ Fr recursionSeperator
1729
+ ) view returns (Honk.G1Point memory) {
1730
+ Honk.G1Point memory result;
1731
1731
 
1732
- result = ecMul(recursionSeperator, basePoint);
1733
- result = ecAdd(result, other);
1732
+ result = ecMul(recursionSeperator, basePoint);
1733
+ result = ecAdd(result, other);
1734
1734
 
1735
- return result;
1735
+ return result;
1736
1736
  }
1737
1737
 
1738
1738
  /**
@@ -1744,41 +1744,41 @@ function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory oth
1744
1744
  * @return result The result of the multiplication.
1745
1745
  */
1746
1746
  function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) {
1747
- Honk.G1Point memory result;
1748
-
1749
- assembly {
1750
- let free := mload(0x40)
1751
- // Write the point into memory (two 32 byte words)
1752
- // Memory layout:
1753
- // Address | value
1754
- // free | point.x
1755
- // free + 0x20| point.y
1756
- mstore(free, mload(point))
1757
- mstore(add(free, 0x20), mload(add(point, 0x20)))
1758
- // Write the scalar into memory (one 32 byte word)
1759
- // Memory layout:
1760
- // Address | value
1761
- // free + 0x40| value
1762
- mstore(add(free, 0x40), value)
1763
-
1764
- // Call the ecMul precompile, it takes in the following
1765
- // [point.x, point.y, scalar], and returns the result back into the free memory location.
1766
- let success := staticcall(gas(), 0x07, free, 0x60, free, 0x40)
1767
- if iszero(success) {
1768
- revert(0, 0)
1769
- }
1770
- // Copy the result of the multiplication back into the result memory location.
1771
- // Memory layout:
1772
- // Address | value
1773
- // result | result.x
1774
- // result + 0x20| result.y
1775
- mstore(result, mload(free))
1776
- mstore(add(result, 0x20), mload(add(free, 0x20)))
1777
-
1778
- mstore(0x40, add(free, 0x60))
1779
- }
1780
-
1781
- return result;
1747
+ Honk.G1Point memory result;
1748
+
1749
+ assembly {
1750
+ let free := mload(0x40)
1751
+ // Write the point into memory (two 32 byte words)
1752
+ // Memory layout:
1753
+ // Address | value
1754
+ // free | point.x
1755
+ // free + 0x20| point.y
1756
+ mstore(free, mload(point))
1757
+ mstore(add(free, 0x20), mload(add(point, 0x20)))
1758
+ // Write the scalar into memory (one 32 byte word)
1759
+ // Memory layout:
1760
+ // Address | value
1761
+ // free + 0x40| value
1762
+ mstore(add(free, 0x40), value)
1763
+
1764
+ // Call the ecMul precompile, it takes in the following
1765
+ // [point.x, point.y, scalar], and returns the result back into the free memory location.
1766
+ let success := staticcall(gas(), 0x07, free, 0x60, free, 0x40)
1767
+ if iszero(success) {
1768
+ revert(0, 0)
1769
+ }
1770
+ // Copy the result of the multiplication back into the result memory location.
1771
+ // Memory layout:
1772
+ // Address | value
1773
+ // result | result.x
1774
+ // result + 0x20| result.y
1775
+ mstore(result, mload(free))
1776
+ mstore(add(result, 0x20), mload(add(free, 0x20)))
1777
+
1778
+ mstore(0x40, add(free, 0x60))
1779
+ }
1780
+
1781
+ return result;
1782
1782
  }
1783
1783
 
1784
1784
  /**
@@ -1790,649 +1790,648 @@ function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point m
1790
1790
  * @return result The result of the addition.
1791
1791
  */
1792
1792
  function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) {
1793
- Honk.G1Point memory result;
1794
-
1795
- assembly {
1796
- let free := mload(0x40)
1797
- // Write lhs into memory (two 32 byte words)
1798
- // Memory layout:
1799
- // Address | value
1800
- // free | lhs.x
1801
- // free + 0x20| lhs.y
1802
- mstore(free, mload(lhs))
1803
- mstore(add(free, 0x20), mload(add(lhs, 0x20)))
1804
-
1805
- // Write rhs into memory (two 32 byte words)
1806
- // Memory layout:
1807
- // Address | value
1808
- // free + 0x40| rhs.x
1809
- // free + 0x60| rhs.y
1810
- mstore(add(free, 0x40), mload(rhs))
1811
- mstore(add(free, 0x60), mload(add(rhs, 0x20)))
1812
-
1813
- // Call the ecAdd precompile, it takes in the following
1814
- // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location.
1815
- let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40)
1816
- if iszero(success) { revert(0, 0) }
1817
-
1818
- // Copy the result of the addition back into the result memory location.
1819
- // Memory layout:
1820
- // Address | value
1821
- // result | result.x
1822
- // result + 0x20| result.y
1823
- mstore(result, mload(free))
1824
- mstore(add(result, 0x20), mload(add(free, 0x20)))
1825
-
1826
- mstore(0x40, add(free, 0x80))
1827
- }
1828
-
1829
- return result;
1793
+ Honk.G1Point memory result;
1794
+
1795
+ assembly {
1796
+ let free := mload(0x40)
1797
+ // Write lhs into memory (two 32 byte words)
1798
+ // Memory layout:
1799
+ // Address | value
1800
+ // free | lhs.x
1801
+ // free + 0x20| lhs.y
1802
+ mstore(free, mload(lhs))
1803
+ mstore(add(free, 0x20), mload(add(lhs, 0x20)))
1804
+
1805
+ // Write rhs into memory (two 32 byte words)
1806
+ // Memory layout:
1807
+ // Address | value
1808
+ // free + 0x40| rhs.x
1809
+ // free + 0x60| rhs.y
1810
+ mstore(add(free, 0x40), mload(rhs))
1811
+ mstore(add(free, 0x60), mload(add(rhs, 0x20)))
1812
+
1813
+ // Call the ecAdd precompile, it takes in the following
1814
+ // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location.
1815
+ let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40)
1816
+ if iszero(success) {
1817
+ revert(0, 0)
1818
+ }
1819
+
1820
+ // Copy the result of the addition back into the result memory location.
1821
+ // Memory layout:
1822
+ // Address | value
1823
+ // result | result.x
1824
+ // result + 0x20| result.y
1825
+ mstore(result, mload(free))
1826
+ mstore(add(result, 0x20), mload(add(free, 0x20)))
1827
+
1828
+ mstore(0x40, add(free, 0x80))
1829
+ }
1830
+
1831
+ return result;
1830
1832
  }
1831
1833
 
1832
1834
  function validateOnCurve(Honk.G1Point memory point) pure {
1833
- uint256 x = point.x;
1834
- uint256 y = point.y;
1835
+ uint256 x = point.x;
1836
+ uint256 y = point.y;
1835
1837
 
1836
- bool success = false;
1837
- assembly {
1838
- let xx := mulmod(x, x, Q)
1839
- success := eq(mulmod(y, y, Q), addmod(mulmod(x, xx, Q), 3, Q))
1840
- }
1838
+ bool success = false;
1839
+ assembly {
1840
+ let xx := mulmod(x, x, Q)
1841
+ success := eq(mulmod(y, y, Q), addmod(mulmod(x, xx, Q), 3, Q))
1842
+ }
1841
1843
 
1842
- require(success, "point is not on the curve");
1844
+ require(success, "point is not on the curve");
1843
1845
  }
1844
1846
 
1845
1847
  function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) {
1846
- bytes memory input = abi.encodePacked(
1847
- rhs.x,
1848
- rhs.y,
1849
- // Fixed G2 point
1850
- uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2),
1851
- uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed),
1852
- uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b),
1853
- uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa),
1854
- lhs.x,
1855
- lhs.y,
1856
- // G2 point from VK
1857
- uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1),
1858
- uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0),
1859
- uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4),
1860
- uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55)
1861
- );
1862
-
1863
- (bool success, bytes memory result) = address(0x08).staticcall(input);
1864
- decodedResult = success && abi.decode(result, (bool));
1848
+ bytes memory input = abi.encodePacked(
1849
+ rhs.x,
1850
+ rhs.y,
1851
+ // Fixed G2 point
1852
+ uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2),
1853
+ uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed),
1854
+ uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b),
1855
+ uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa),
1856
+ lhs.x,
1857
+ lhs.y,
1858
+ // G2 point from VK
1859
+ uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1),
1860
+ uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0),
1861
+ uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4),
1862
+ uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55)
1863
+ );
1864
+
1865
+ (bool success, bytes memory result) = address(0x08).staticcall(input);
1866
+ decodedResult = success && abi.decode(result, (bool));
1865
1867
  }
1866
1868
 
1867
1869
  // Field arithmetic libraries - prevent littering the code with modmul / addmul
1868
1870
 
1871
+ abstract contract BaseZKHonkVerifier is IVerifier {
1872
+ using FrLib for Fr;
1873
+
1874
+ uint256 immutable $N;
1875
+ uint256 immutable $LOG_N;
1876
+ uint256 immutable $VK_HASH;
1877
+ uint256 immutable $NUM_PUBLIC_INPUTS;
1878
+ uint256 immutable $MSMSize;
1879
+
1880
+ constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) {
1881
+ $N = _N;
1882
+ $LOG_N = _logN;
1883
+ $VK_HASH = _vkHash;
1884
+ $NUM_PUBLIC_INPUTS = _numPublicInputs;
1885
+ $MSMSize = NUMBER_UNSHIFTED_ZK + _logN + LIBRA_COMMITMENTS + 2;
1886
+ }
1887
+
1888
+ // Errors
1889
+ error ProofLengthWrong();
1890
+ error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength);
1891
+ error PublicInputsLengthWrong();
1892
+ error SumcheckFailed();
1893
+ error ShpleminiFailed();
1894
+ error GeminiChallengeInSubgroup();
1895
+ error ConsistencyCheckFailed();
1896
+
1897
+ // Constants for proof length calculation (matching UltraKeccakZKFlavor)
1898
+ uint256 constant NUM_WITNESS_ENTITIES = 8 + NUM_MASKING_POLYNOMIALS;
1899
+ uint256 constant NUM_ELEMENTS_COMM = 2; // uint256 elements for curve points
1900
+ uint256 constant NUM_ELEMENTS_FR = 1; // uint256 elements for field elements
1901
+ uint256 constant NUM_LIBRA_EVALUATIONS = 4; // libra evaluations
1902
+
1903
+ // Calculate proof size based on log_n (matching UltraKeccakZKFlavor formula)
1904
+ function calculateProofSize(uint256 logN) internal pure returns (uint256) {
1905
+ // Witness and Libra commitments
1906
+ uint256 proofLength = NUM_WITNESS_ENTITIES * NUM_ELEMENTS_COMM; // witness commitments
1907
+ proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking
1869
1908
 
1909
+ // Sumcheck
1910
+ proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates
1911
+ proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations
1870
1912
 
1913
+ // Libra and Gemini
1914
+ proofLength += NUM_ELEMENTS_FR * 2; // Libra sum, claimed eval
1915
+ proofLength += logN * NUM_ELEMENTS_FR; // Gemini a evaluations
1916
+ proofLength += NUM_LIBRA_EVALUATIONS * NUM_ELEMENTS_FR; // libra evaluations
1871
1917
 
1872
- abstract contract BaseZKHonkVerifier is IVerifier {
1873
- using FrLib for Fr;
1918
+ // PCS commitments
1919
+ proofLength += (logN - 1) * NUM_ELEMENTS_COMM; // Gemini Fold commitments
1920
+ proofLength += NUM_ELEMENTS_COMM * 2; // Shplonk Q and KZG W commitments
1921
+
1922
+ // Pairing points
1923
+ proofLength += PAIRING_POINTS_SIZE; // pairing inputs carried on public inputs
1924
+
1925
+ return proofLength;
1926
+ }
1874
1927
 
1875
- uint256 immutable $N;
1876
- uint256 immutable $LOG_N;
1877
- uint256 immutable $VK_HASH;
1878
- uint256 immutable $NUM_PUBLIC_INPUTS;
1928
+ uint256 constant SHIFTED_COMMITMENTS_START = 30;
1879
1929
 
1880
- constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) {
1881
- $N = _N;
1882
- $LOG_N = _logN;
1883
- $VK_HASH = _vkHash;
1884
- $NUM_PUBLIC_INPUTS = _numPublicInputs;
1930
+ function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory);
1931
+
1932
+ function verify(bytes calldata proof, bytes32[] calldata publicInputs) public view override returns (bool verified) {
1933
+ // Calculate expected proof size based on $LOG_N
1934
+ uint256 expectedProofSize = calculateProofSize($LOG_N);
1935
+
1936
+ // Check the received proof is the expected size where each field element is 32 bytes
1937
+ if (proof.length != expectedProofSize * 32) {
1938
+ revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32);
1885
1939
  }
1886
1940
 
1887
- // Errors
1888
- error ProofLengthWrong();
1889
- error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength);
1890
- error PublicInputsLengthWrong();
1891
- error SumcheckFailed();
1892
- error ShpleminiFailed();
1893
- error GeminiChallengeInSubgroup();
1894
- error ConsistencyCheckFailed();
1941
+ Honk.VerificationKey memory vk = loadVerificationKey();
1942
+ Honk.ZKProof memory p = ZKTranscriptLib.loadProof(proof, $LOG_N);
1895
1943
 
1896
- // Constants for proof length calculation (matching UltraKeccakZKFlavor)
1897
- uint256 constant NUM_WITNESS_ENTITIES = 8;
1898
- uint256 constant NUM_ELEMENTS_COMM = 2; // uint256 elements for curve points
1899
- uint256 constant NUM_ELEMENTS_FR = 1; // uint256 elements for field elements
1900
- uint256 constant NUM_LIBRA_EVALUATIONS = 4; // libra evaluations
1944
+ if (publicInputs.length != vk.publicInputsSize - PAIRING_POINTS_SIZE) {
1945
+ revert PublicInputsLengthWrong();
1946
+ }
1901
1947
 
1902
- // Calculate proof size based on log_n (matching UltraKeccakZKFlavor formula)
1903
- function calculateProofSize(uint256 logN) internal pure returns (uint256) {
1904
- // Witness and Libra commitments
1905
- uint256 proofLength = NUM_WITNESS_ENTITIES * NUM_ELEMENTS_COMM; // witness commitments
1906
- proofLength += NUM_ELEMENTS_COMM * 4; // Libra concat, grand sum, quotient comms + Gemini masking
1948
+ // Generate the fiat shamir challenges for the whole protocol
1949
+ ZKTranscript memory t = ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N);
1907
1950
 
1908
- // Sumcheck
1909
- proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates
1910
- proofLength += NUMBER_OF_ENTITIES * NUM_ELEMENTS_FR; // sumcheck evaluations
1951
+ // Derive public input delta
1952
+ t.relationParameters.publicInputsDelta = computePublicInputDelta(
1953
+ publicInputs,
1954
+ p.pairingPointObject,
1955
+ t.relationParameters.beta,
1956
+ t.relationParameters.gamma /*pubInputsOffset=*/,
1957
+ 1
1958
+ );
1911
1959
 
1912
- // Libra and Gemini
1913
- proofLength += NUM_ELEMENTS_FR * 3; // Libra sum, claimed eval, Gemini masking eval
1914
- proofLength += logN * NUM_ELEMENTS_FR; // Gemini a evaluations
1915
- proofLength += NUM_LIBRA_EVALUATIONS * NUM_ELEMENTS_FR; // libra evaluations
1960
+ // Sumcheck
1961
+ if (!verifySumcheck(p, t)) revert SumcheckFailed();
1916
1962
 
1917
- // PCS commitments
1918
- proofLength += (logN - 1) * NUM_ELEMENTS_COMM; // Gemini Fold commitments
1919
- proofLength += NUM_ELEMENTS_COMM * 2; // Shplonk Q and KZG W commitments
1963
+ if (!verifyShplemini(p, vk, t)) revert ShpleminiFailed();
1920
1964
 
1921
- // Pairing points
1922
- proofLength += PAIRING_POINTS_SIZE; // pairing inputs carried on public inputs
1965
+ verified = true;
1966
+ }
1923
1967
 
1924
- return proofLength;
1925
- }
1968
+ uint256 constant PERMUTATION_ARGUMENT_VALUE_SEPARATOR = 1 << 28;
1926
1969
 
1927
- uint256 constant SHIFTED_COMMITMENTS_START = 30;
1970
+ function computePublicInputDelta(
1971
+ bytes32[] memory publicInputs,
1972
+ Fr[PAIRING_POINTS_SIZE] memory pairingPointObject,
1973
+ Fr beta,
1974
+ Fr gamma,
1975
+ uint256 offset
1976
+ ) internal view returns (Fr publicInputDelta) {
1977
+ Fr numerator = Fr.wrap(1);
1978
+ Fr denominator = Fr.wrap(1);
1928
1979
 
1929
- function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory);
1980
+ Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset));
1981
+ Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1));
1930
1982
 
1931
- function verify(bytes calldata proof, bytes32[] calldata publicInputs)
1932
- public
1933
- view
1934
- override
1935
- returns (bool verified)
1936
1983
  {
1937
- // Calculate expected proof size based on $LOG_N
1938
- uint256 expectedProofSize = calculateProofSize($LOG_N);
1984
+ for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) {
1985
+ Fr pubInput = FrLib.fromBytes32(publicInputs[i]);
1939
1986
 
1940
- // Check the received proof is the expected size where each field element is 32 bytes
1941
- if (proof.length != expectedProofSize * 32) {
1942
- revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32);
1943
- }
1987
+ numerator = numerator * (numeratorAcc + pubInput);
1988
+ denominator = denominator * (denominatorAcc + pubInput);
1944
1989
 
1945
- Honk.VerificationKey memory vk = loadVerificationKey();
1946
- Honk.ZKProof memory p = ZKTranscriptLib.loadProof(proof, $LOG_N);
1990
+ numeratorAcc = numeratorAcc + beta;
1991
+ denominatorAcc = denominatorAcc - beta;
1992
+ }
1947
1993
 
1948
- if (publicInputs.length != vk.publicInputsSize - PAIRING_POINTS_SIZE) {
1949
- revert PublicInputsLengthWrong();
1950
- }
1994
+ for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) {
1995
+ Fr pubInput = pairingPointObject[i];
1951
1996
 
1952
- // Generate the fiat shamir challenges for the whole protocol
1953
- ZKTranscript memory t =
1954
- ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N);
1997
+ numerator = numerator * (numeratorAcc + pubInput);
1998
+ denominator = denominator * (denominatorAcc + pubInput);
1955
1999
 
1956
- // Derive public input delta
1957
- t.relationParameters.publicInputsDelta = computePublicInputDelta(
1958
- publicInputs,
1959
- p.pairingPointObject,
1960
- t.relationParameters.beta,
1961
- t.relationParameters.gamma, /*pubInputsOffset=*/
1962
- 1
1963
- );
2000
+ numeratorAcc = numeratorAcc + beta;
2001
+ denominatorAcc = denominatorAcc - beta;
2002
+ }
2003
+ }
2004
+
2005
+ // Fr delta = numerator / denominator; // TOOO: batch invert later?
2006
+ publicInputDelta = FrLib.div(numerator, denominator);
2007
+ }
2008
+
2009
+ function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) {
2010
+ Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0
2011
+ Fr powPartialEvaluation = Fr.wrap(1);
1964
2012
 
1965
- // Sumcheck
1966
- if (!verifySumcheck(p, t)) revert SumcheckFailed();
2013
+ // We perform sumcheck reductions over log n rounds ( the multivariate degree )
2014
+ for (uint256 round; round < $LOG_N; ++round) {
2015
+ Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round];
2016
+ Fr totalSum = roundUnivariate[0] + roundUnivariate[1];
2017
+ if (totalSum != roundTargetSum) revert SumcheckFailed();
1967
2018
 
1968
- if (!verifyShplemini(p, vk, t)) revert ShpleminiFailed();
2019
+ Fr roundChallenge = tp.sumCheckUChallenges[round];
1969
2020
 
1970
- verified = true;
2021
+ // Update the round target for the next rounf
2022
+ roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge);
2023
+ powPartialEvaluation = powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1)));
1971
2024
  }
1972
2025
 
1973
- uint256 constant PERMUTATION_ARGUMENT_VALUE_SEPARATOR = 1 << 28;
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
+ }
2033
+ Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations(
2034
+ relationsEvaluations,
2035
+ tp.relationParameters,
2036
+ tp.alphas,
2037
+ powPartialEvaluation
2038
+ );
2039
+
2040
+ Fr evaluation = Fr.wrap(1);
2041
+ for (uint256 i = 2; i < $LOG_N; i++) {
2042
+ evaluation = evaluation * tp.sumCheckUChallenges[i];
2043
+ }
2044
+
2045
+ grandHonkRelationSum = grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge;
2046
+ verified = (grandHonkRelationSum == roundTargetSum);
2047
+ }
2048
+
2049
+ // Return the new target sum for the next sumcheck round
2050
+ function computeNextTargetSum(
2051
+ Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates,
2052
+ Fr roundChallenge
2053
+ ) internal view returns (Fr targetSum) {
2054
+ Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [
2055
+ Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80),
2056
+ Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51),
2057
+ Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0),
2058
+ Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31),
2059
+ Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240),
2060
+ Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31),
2061
+ Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0),
2062
+ Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51),
2063
+ Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80)
2064
+ ];
2065
+
2066
+ // To compute the next target sum, we evaluate the given univariate at a point u (challenge).
2067
+
2068
+ // Performing Barycentric evaluations
2069
+ // Compute B(x)
2070
+ Fr numeratorValue = Fr.wrap(1);
2071
+ for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) {
2072
+ numeratorValue = numeratorValue * (roundChallenge - Fr.wrap(i));
2073
+ }
2074
+
2075
+ Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses;
2076
+ for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) {
2077
+ denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i)));
2078
+ }
2079
+
2080
+ for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) {
2081
+ targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i];
2082
+ }
2083
+
2084
+ // Scale the sum by the value of B(x)
2085
+ targetSum = targetSum * numeratorValue;
2086
+ }
2087
+
2088
+ uint256 constant LIBRA_COMMITMENTS = 3;
2089
+ uint256 constant LIBRA_EVALUATIONS = 4;
2090
+ uint256 constant LIBRA_UNIVARIATES_LENGTH = 9;
2091
+
2092
+ struct PairingInputs {
2093
+ Honk.G1Point P_0;
2094
+ Honk.G1Point P_1;
2095
+ }
2096
+
2097
+ function verifyShplemini(
2098
+ Honk.ZKProof memory proof,
2099
+ Honk.VerificationKey memory vk,
2100
+ ZKTranscript memory tp
2101
+ ) internal view returns (bool verified) {
2102
+ CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack
2103
+
2104
+ // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size
2105
+ Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N);
2106
+ // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings
2107
+ Fr[] memory scalars = new Fr[]($MSMSize);
2108
+ Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize);
2109
+
2110
+ mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert();
2111
+ mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert();
2112
+
2113
+ mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator);
2114
+ mem.shiftedScalar = tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator));
2115
+
2116
+ scalars[0] = Fr.wrap(1);
2117
+ commitments[0] = proof.shplonkQ;
2118
+
2119
+ /* Batch multivariate opening claims, shifted and unshifted
2120
+ * The vector of scalars is populated as follows:
2121
+ * \f[
2122
+ * \left(
2123
+ * - \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right),
2124
+ * \ldots,
2125
+ * - \rho^{i+k-1} \times \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right),
2126
+ * - \rho^{i+k} \times \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right),
2127
+ * \ldots,
2128
+ * - \rho^{k+m-1} \times \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right)
2129
+ * \right)
2130
+ * \f]
2131
+ *
2132
+ * The following vector is concatenated to the vector of commitments:
2133
+ * \f[
2134
+ * f_0, \ldots, f_{m-1}, f_{\text{shift}, 0}, \ldots, f_{\text{shift}, k-1}
2135
+ * \f]
2136
+ *
2137
+ * Simultaneously, the evaluation of the multilinear polynomial
2138
+ * \f[
2139
+ * \sum \rho^i \cdot f_i + \sum \rho^{i+k} \cdot f_{\text{shift}, i}
2140
+ * \f]
2141
+ * at the challenge point \f$ (u_0,\ldots, u_{n-1}) \f$ is computed.
2142
+ *
2143
+ * This approach minimizes the number of iterations over the commitments to multilinear polynomials
2144
+ * and eliminates the need to store the powers of \f$ \rho \f$.
2145
+ */
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
+
2151
+ mem.unshiftedScalarNeg = mem.unshiftedScalar.neg();
2152
+ mem.shiftedScalarNeg = mem.shiftedScalar.neg();
2153
+
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);
2158
+ mem.batchingChallenge = mem.batchingChallenge * tp.rho;
2159
+ }
2160
+ // g commitments are accumulated at r
2161
+ // For each of the to be shifted commitments perform the shift in place by
2162
+ // adding to the unshifted value.
2163
+ // We do so, as the values are to be used in batchMul later, and as
2164
+ // `a * c + b * c = (a + b) * c` this will allow us to reduce memory and compute.
2165
+ // Applied to w1, w2, w3, w4 and zPerm
2166
+ for (uint256 i = 0; i < NUMBER_TO_BE_SHIFTED; ++i) {
2167
+ uint256 scalarOff = i + SHIFTED_COMMITMENTS_START;
2168
+ uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK;
2169
+
2170
+ scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge);
2171
+ mem.batchedEvaluation = mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge);
2172
+ mem.batchingChallenge = mem.batchingChallenge * tp.rho;
2173
+ }
2174
+
2175
+ commitments[1] = proof.geminiMaskingPoly;
2176
+
2177
+ commitments[2] = vk.qm;
2178
+ commitments[3] = vk.qc;
2179
+ commitments[4] = vk.ql;
2180
+ commitments[5] = vk.qr;
2181
+ commitments[6] = vk.qo;
2182
+ commitments[7] = vk.q4;
2183
+ commitments[8] = vk.qLookup;
2184
+ commitments[9] = vk.qArith;
2185
+ commitments[10] = vk.qDeltaRange;
2186
+ commitments[11] = vk.qElliptic;
2187
+ commitments[12] = vk.qMemory;
2188
+ commitments[13] = vk.qNnf;
2189
+ commitments[14] = vk.qPoseidon2External;
2190
+ commitments[15] = vk.qPoseidon2Internal;
2191
+ commitments[16] = vk.s1;
2192
+ commitments[17] = vk.s2;
2193
+ commitments[18] = vk.s3;
2194
+ commitments[19] = vk.s4;
2195
+ commitments[20] = vk.id1;
2196
+ commitments[21] = vk.id2;
2197
+ commitments[22] = vk.id3;
2198
+ commitments[23] = vk.id4;
2199
+ commitments[24] = vk.t1;
2200
+ commitments[25] = vk.t2;
2201
+ commitments[26] = vk.t3;
2202
+ commitments[27] = vk.t4;
2203
+ commitments[28] = vk.lagrangeFirst;
2204
+ commitments[29] = vk.lagrangeLast;
2205
+
2206
+ // Accumulate proof points
2207
+ commitments[30] = proof.w1;
2208
+ commitments[31] = proof.w2;
2209
+ commitments[32] = proof.w3;
2210
+ commitments[33] = proof.w4;
2211
+ commitments[34] = proof.zPerm;
2212
+ commitments[35] = proof.lookupInverses;
2213
+ commitments[36] = proof.lookupReadCounts;
2214
+ commitments[37] = proof.lookupReadTags;
2215
+
2216
+ /* Batch gemini claims from the prover
2217
+ * place the commitments to gemini aᵢ to the vector of commitments, compute the contributions from
2218
+ * aᵢ(−r²ⁱ) for i=1, … , n−1 to the constant term accumulator, add corresponding scalars
2219
+ *
2220
+ * 1. Moves the vector
2221
+ * \f[
2222
+ * \left( \text{com}(A_1), \text{com}(A_2), \ldots, \text{com}(A_{n-1}) \right)
2223
+ * \f]
2224
+ * to the 'commitments' vector.
2225
+ *
2226
+ * 2. Computes the scalars:
2227
+ * \f[
2228
+ * \frac{\nu^{2}}{z + r^2}, \frac{\nu^3}{z + r^4}, \ldots, \frac{\nu^{n-1}}{z + r^{2^{n-1}}}
2229
+ * \f]
2230
+ * and places them into the 'scalars' vector.
2231
+ *
2232
+ * 3. Accumulates the summands of the constant term:
2233
+ * \f[
2234
+ * \sum_{i=2}^{n-1} \frac{\nu^{i} \cdot A_i(-r^{2^i})}{z + r^{2^i}}
2235
+ * \f]
2236
+ * and adds them to the 'constant_term_accumulator'.
2237
+ */
2238
+
2239
+ // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator:
2240
+ // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1
2241
+ Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations(
2242
+ tp.sumCheckUChallenges,
2243
+ mem.batchedEvaluation,
2244
+ proof.geminiAEvaluations,
2245
+ powers_of_evaluation_challenge,
2246
+ $LOG_N
2247
+ );
1974
2248
 
1975
- function computePublicInputDelta(
1976
- bytes32[] memory publicInputs,
1977
- Fr[PAIRING_POINTS_SIZE] memory pairingPointObject,
1978
- Fr beta,
1979
- Fr gamma,
1980
- uint256 offset
1981
- ) internal view returns (Fr publicInputDelta) {
1982
- Fr numerator = Fr.wrap(1);
1983
- Fr denominator = Fr.wrap(1);
2249
+ mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator;
2250
+ mem.constantTermAccumulator = mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator);
1984
2251
 
1985
- Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset));
1986
- Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1));
2252
+ mem.batchingChallenge = tp.shplonkNu.sqr();
2253
+ uint256 boundary = NUMBER_UNSHIFTED_ZK + 1;
1987
2254
 
1988
- {
1989
- for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) {
1990
- Fr pubInput = FrLib.fromBytes32(publicInputs[i]);
2255
+ // Compute Shplonk constant term contributions from Aₗ(± r^{2ˡ}) for l = 1, ..., m-1;
2256
+ // Compute scalar multipliers for each fold commitment
2257
+ for (uint256 i = 0; i < $LOG_N - 1; ++i) {
2258
+ bool dummy_round = i >= ($LOG_N - 1);
1991
2259
 
1992
- numerator = numerator * (numeratorAcc + pubInput);
1993
- denominator = denominator * (denominatorAcc + pubInput);
2260
+ if (!dummy_round) {
2261
+ // Update inverted denominators
2262
+ mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert();
2263
+ mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert();
2264
+
2265
+ // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ]
2266
+ mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator;
2267
+ mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator;
2268
+ scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg();
1994
2269
 
1995
- numeratorAcc = numeratorAcc + beta;
1996
- denominatorAcc = denominatorAcc - beta;
1997
- }
2270
+ // Accumulate the const term contribution given by
2271
+ // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l})
2272
+ Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1];
2273
+ accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1];
2274
+ mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution;
2275
+ }
2276
+ // Update the running power of v
2277
+ mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu;
1998
2278
 
1999
- for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) {
2000
- Fr pubInput = pairingPointObject[i];
2279
+ commitments[boundary + i] = proof.geminiFoldComms[i];
2280
+ }
2001
2281
 
2002
- numerator = numerator * (numeratorAcc + pubInput);
2003
- denominator = denominator * (denominatorAcc + pubInput);
2282
+ boundary += $LOG_N - 1;
2004
2283
 
2005
- numeratorAcc = numeratorAcc + beta;
2006
- denominatorAcc = denominatorAcc - beta;
2007
- }
2008
- }
2284
+ // Finalize the batch opening claim
2285
+ mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR);
2286
+ mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR);
2287
+ mem.denominators[2] = mem.denominators[0];
2288
+ mem.denominators[3] = mem.denominators[0];
2009
2289
 
2010
- // Fr delta = numerator / denominator; // TOOO: batch invert later?
2011
- publicInputDelta = FrLib.div(numerator, denominator);
2290
+ mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu;
2291
+ for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) {
2292
+ Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge;
2293
+ mem.batchingScalars[i] = scalingFactor.neg();
2294
+ mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu;
2295
+ mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i];
2012
2296
  }
2297
+ scalars[boundary] = mem.batchingScalars[0];
2298
+ scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2];
2299
+ scalars[boundary + 2] = mem.batchingScalars[3];
2013
2300
 
2014
- function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) {
2015
- Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0
2016
- Fr powPartialEvaluation = Fr.wrap(1);
2301
+ for (uint256 i = 0; i < LIBRA_COMMITMENTS; i++) {
2302
+ commitments[boundary++] = proof.libraCommitments[i];
2303
+ }
2304
+
2305
+ commitments[boundary] = Honk.G1Point({ x: 1, y: 2 });
2306
+ scalars[boundary++] = mem.constantTermAccumulator;
2017
2307
 
2018
- // We perform sumcheck reductions over log n rounds ( the multivariate degree )
2019
- for (uint256 round; round < $LOG_N; ++round) {
2020
- Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round];
2021
- Fr totalSum = roundUnivariate[0] + roundUnivariate[1];
2022
- if (totalSum != roundTargetSum) revert SumcheckFailed();
2308
+ if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) {
2309
+ revert ConsistencyCheckFailed();
2310
+ }
2023
2311
 
2024
- Fr roundChallenge = tp.sumCheckUChallenges[round];
2312
+ Honk.G1Point memory quotient_commitment = proof.kzgQuotient;
2025
2313
 
2026
- // Update the round target for the next rounf
2027
- roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge);
2028
- powPartialEvaluation =
2029
- powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1)));
2030
- }
2314
+ commitments[boundary] = quotient_commitment;
2315
+ scalars[boundary] = tp.shplonkZ; // evaluation challenge
2031
2316
 
2032
- // Last round
2033
- Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations(
2034
- proof.sumcheckEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation
2035
- );
2317
+ PairingInputs memory pair;
2318
+ pair.P_0 = batchMul(commitments, scalars);
2319
+ pair.P_1 = negateInplace(quotient_commitment);
2036
2320
 
2037
- Fr evaluation = Fr.wrap(1);
2038
- for (uint256 i = 2; i < $LOG_N; i++) {
2039
- evaluation = evaluation * tp.sumCheckUChallenges[i];
2040
- }
2321
+ // Aggregate pairing points
2322
+ Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1);
2323
+ (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = convertPairingPointsToG1(proof.pairingPointObject);
2041
2324
 
2042
- grandHonkRelationSum =
2043
- grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge;
2044
- verified = (grandHonkRelationSum == roundTargetSum);
2325
+ // Validate the points from the proof are on the curve
2326
+ validateOnCurve(P_0_other);
2327
+ validateOnCurve(P_1_other);
2328
+
2329
+ // accumulate with aggregate points in proof
2330
+ pair.P_0 = mulWithSeperator(pair.P_0, P_0_other, recursionSeparator);
2331
+ pair.P_1 = mulWithSeperator(pair.P_1, P_1_other, recursionSeparator);
2332
+
2333
+ return pairing(pair.P_0, pair.P_1);
2334
+ }
2335
+
2336
+ struct SmallSubgroupIpaIntermediates {
2337
+ Fr[SUBGROUP_SIZE] challengePolyLagrange;
2338
+ Fr challengePolyEval;
2339
+ Fr lagrangeFirst;
2340
+ Fr lagrangeLast;
2341
+ Fr rootPower;
2342
+ Fr[SUBGROUP_SIZE] denominators; // this has to disappear
2343
+ Fr diff;
2344
+ }
2345
+
2346
+ function checkEvalsConsistency(
2347
+ Fr[LIBRA_EVALUATIONS] memory libraPolyEvals,
2348
+ Fr geminiR,
2349
+ Fr[CONST_PROOF_SIZE_LOG_N] memory uChallenges,
2350
+ Fr libraEval
2351
+ ) internal view returns (bool check) {
2352
+ Fr one = Fr.wrap(1);
2353
+ Fr vanishingPolyEval = geminiR.pow(SUBGROUP_SIZE) - one;
2354
+ if (vanishingPolyEval == Fr.wrap(0)) {
2355
+ revert GeminiChallengeInSubgroup();
2045
2356
  }
2046
2357
 
2047
- // Return the new target sum for the next sumcheck round
2048
- function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge)
2049
- internal
2050
- view
2051
- returns (Fr targetSum)
2052
- {
2053
- Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [
2054
- Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80),
2055
- Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51),
2056
- Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0),
2057
- Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31),
2058
- Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240),
2059
- Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31),
2060
- Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0),
2061
- Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51),
2062
- Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80)
2063
- ];
2064
-
2065
- // To compute the next target sum, we evaluate the given univariate at a point u (challenge).
2066
-
2067
- // Performing Barycentric evaluations
2068
- // Compute B(x)
2069
- Fr numeratorValue = Fr.wrap(1);
2070
- for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) {
2071
- numeratorValue = numeratorValue * (roundChallenge - Fr.wrap(i));
2072
- }
2073
-
2074
- Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses;
2075
- for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) {
2076
- denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i)));
2077
- }
2078
-
2079
- for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) {
2080
- targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i];
2081
- }
2082
-
2083
- // Scale the sum by the value of B(x)
2084
- targetSum = targetSum * numeratorValue;
2085
- }
2086
-
2087
- uint256 constant LIBRA_COMMITMENTS = 3;
2088
- uint256 constant LIBRA_EVALUATIONS = 4;
2089
- uint256 constant LIBRA_UNIVARIATES_LENGTH = 9;
2090
-
2091
- struct PairingInputs {
2092
- Honk.G1Point P_0;
2093
- Honk.G1Point P_1;
2094
- }
2095
-
2096
- function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp)
2097
- internal
2098
- view
2099
- returns (bool verified)
2100
- {
2101
- CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack
2102
-
2103
- // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size
2104
- Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N);
2105
- // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings
2106
- Fr[] memory scalars = new Fr[](NUMBER_UNSHIFTED + $LOG_N + LIBRA_COMMITMENTS + 3);
2107
- Honk.G1Point[] memory commitments = new Honk.G1Point[](NUMBER_UNSHIFTED + $LOG_N + LIBRA_COMMITMENTS + 3);
2108
-
2109
- mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert();
2110
- mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert();
2111
-
2112
- mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator);
2113
- mem.shiftedScalar =
2114
- tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator));
2115
-
2116
- scalars[0] = Fr.wrap(1);
2117
- commitments[0] = proof.shplonkQ;
2118
-
2119
- /* Batch multivariate opening claims, shifted and unshifted
2120
- * The vector of scalars is populated as follows:
2121
- * \f[
2122
- * \left(
2123
- * - \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right),
2124
- * \ldots,
2125
- * - \rho^{i+k-1} \times \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right),
2126
- * - \rho^{i+k} \times \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right),
2127
- * \ldots,
2128
- * - \rho^{k+m-1} \times \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right)
2129
- * \right)
2130
- * \f]
2131
- *
2132
- * The following vector is concatenated to the vector of commitments:
2133
- * \f[
2134
- * f_0, \ldots, f_{m-1}, f_{\text{shift}, 0}, \ldots, f_{\text{shift}, k-1}
2135
- * \f]
2136
- *
2137
- * Simultaneously, the evaluation of the multilinear polynomial
2138
- * \f[
2139
- * \sum \rho^i \cdot f_i + \sum \rho^{i+k} \cdot f_{\text{shift}, i}
2140
- * \f]
2141
- * at the challenge point \f$ (u_0,\ldots, u_{n-1}) \f$ is computed.
2142
- *
2143
- * This approach minimizes the number of iterations over the commitments to multilinear polynomials
2144
- * and eliminates the need to store the powers of \f$ \rho \f$.
2145
- */
2146
- mem.batchedEvaluation = proof.geminiMaskingEval;
2147
- mem.batchingChallenge = tp.rho;
2148
- mem.unshiftedScalarNeg = mem.unshiftedScalar.neg();
2149
- mem.shiftedScalarNeg = mem.shiftedScalar.neg();
2150
-
2151
- scalars[1] = mem.unshiftedScalarNeg;
2152
- for (uint256 i = 0; i < NUMBER_UNSHIFTED; ++i) {
2153
- scalars[i + 2] = mem.unshiftedScalarNeg * mem.batchingChallenge;
2154
- mem.batchedEvaluation = mem.batchedEvaluation + (proof.sumcheckEvaluations[i] * mem.batchingChallenge);
2155
- mem.batchingChallenge = mem.batchingChallenge * tp.rho;
2156
- }
2157
- // g commitments are accumulated at r
2158
- // For each of the to be shifted commitments perform the shift in place by
2159
- // adding to the unshifted value.
2160
- // We do so, as the values are to be used in batchMul later, and as
2161
- // `a * c + b * c = (a + b) * c` this will allow us to reduce memory and compute.
2162
- // Applied to w1, w2, w3, w4 and zPerm
2163
- for (uint256 i = 0; i < NUMBER_TO_BE_SHIFTED; ++i) {
2164
- uint256 scalarOff = i + SHIFTED_COMMITMENTS_START;
2165
- uint256 evaluationOff = i + NUMBER_UNSHIFTED;
2166
-
2167
- scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge);
2168
- mem.batchedEvaluation =
2169
- mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge);
2170
- mem.batchingChallenge = mem.batchingChallenge * tp.rho;
2171
- }
2172
-
2173
- commitments[1] = proof.geminiMaskingPoly;
2174
-
2175
- commitments[2] = vk.qm;
2176
- commitments[3] = vk.qc;
2177
- commitments[4] = vk.ql;
2178
- commitments[5] = vk.qr;
2179
- commitments[6] = vk.qo;
2180
- commitments[7] = vk.q4;
2181
- commitments[8] = vk.qLookup;
2182
- commitments[9] = vk.qArith;
2183
- commitments[10] = vk.qDeltaRange;
2184
- commitments[11] = vk.qElliptic;
2185
- commitments[12] = vk.qMemory;
2186
- commitments[13] = vk.qNnf;
2187
- commitments[14] = vk.qPoseidon2External;
2188
- commitments[15] = vk.qPoseidon2Internal;
2189
- commitments[16] = vk.s1;
2190
- commitments[17] = vk.s2;
2191
- commitments[18] = vk.s3;
2192
- commitments[19] = vk.s4;
2193
- commitments[20] = vk.id1;
2194
- commitments[21] = vk.id2;
2195
- commitments[22] = vk.id3;
2196
- commitments[23] = vk.id4;
2197
- commitments[24] = vk.t1;
2198
- commitments[25] = vk.t2;
2199
- commitments[26] = vk.t3;
2200
- commitments[27] = vk.t4;
2201
- commitments[28] = vk.lagrangeFirst;
2202
- commitments[29] = vk.lagrangeLast;
2203
-
2204
- // Accumulate proof points
2205
- commitments[30] = proof.w1;
2206
- commitments[31] = proof.w2;
2207
- commitments[32] = proof.w3;
2208
- commitments[33] = proof.w4;
2209
- commitments[34] = proof.zPerm;
2210
- commitments[35] = proof.lookupInverses;
2211
- commitments[36] = proof.lookupReadCounts;
2212
- commitments[37] = proof.lookupReadTags;
2213
-
2214
- /* Batch gemini claims from the prover
2215
- * place the commitments to gemini aᵢ to the vector of commitments, compute the contributions from
2216
- * aᵢ(−r²ⁱ) for i=1, … , n−1 to the constant term accumulator, add corresponding scalars
2217
- *
2218
- * 1. Moves the vector
2219
- * \f[
2220
- * \left( \text{com}(A_1), \text{com}(A_2), \ldots, \text{com}(A_{n-1}) \right)
2221
- * \f]
2222
- * to the 'commitments' vector.
2223
- *
2224
- * 2. Computes the scalars:
2225
- * \f[
2226
- * \frac{\nu^{2}}{z + r^2}, \frac{\nu^3}{z + r^4}, \ldots, \frac{\nu^{n-1}}{z + r^{2^{n-1}}}
2227
- * \f]
2228
- * and places them into the 'scalars' vector.
2229
- *
2230
- * 3. Accumulates the summands of the constant term:
2231
- * \f[
2232
- * \sum_{i=2}^{n-1} \frac{\nu^{i} \cdot A_i(-r^{2^i})}{z + r^{2^i}}
2233
- * \f]
2234
- * and adds them to the 'constant_term_accumulator'.
2235
- */
2236
-
2237
- // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator:
2238
- // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1
2239
- Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations(
2240
- tp.sumCheckUChallenges,
2241
- mem.batchedEvaluation,
2242
- proof.geminiAEvaluations,
2243
- powers_of_evaluation_challenge,
2244
- $LOG_N
2245
- );
2246
-
2247
- mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator;
2248
- mem.constantTermAccumulator =
2249
- mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator);
2250
-
2251
- mem.batchingChallenge = tp.shplonkNu.sqr();
2252
- uint256 boundary = NUMBER_UNSHIFTED + 2;
2253
-
2254
- // Compute Shplonk constant term contributions from Aₗ(± r^{2ˡ}) for l = 1, ..., m-1;
2255
- // Compute scalar multipliers for each fold commitment
2256
- for (uint256 i = 0; i < $LOG_N - 1; ++i) {
2257
- bool dummy_round = i >= ($LOG_N - 1);
2258
-
2259
- if (!dummy_round) {
2260
- // Update inverted denominators
2261
- mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert();
2262
- mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert();
2263
-
2264
- // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ]
2265
- mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator;
2266
- mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator;
2267
- scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg();
2268
-
2269
- // Accumulate the const term contribution given by
2270
- // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l})
2271
- Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1];
2272
- accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1];
2273
- mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution;
2274
- }
2275
- // Update the running power of v
2276
- mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu;
2277
-
2278
- commitments[boundary + i] = proof.geminiFoldComms[i];
2279
- }
2280
-
2281
- boundary += $LOG_N - 1;
2282
-
2283
- // Finalize the batch opening claim
2284
- mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR);
2285
- mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR);
2286
- mem.denominators[2] = mem.denominators[0];
2287
- mem.denominators[3] = mem.denominators[0];
2288
-
2289
- mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu;
2290
- for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) {
2291
- Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge;
2292
- mem.batchingScalars[i] = scalingFactor.neg();
2293
- mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu;
2294
- mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i];
2295
- }
2296
- scalars[boundary] = mem.batchingScalars[0];
2297
- scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2];
2298
- scalars[boundary + 2] = mem.batchingScalars[3];
2299
-
2300
- for (uint256 i = 0; i < LIBRA_COMMITMENTS; i++) {
2301
- commitments[boundary++] = proof.libraCommitments[i];
2302
- }
2303
-
2304
- commitments[boundary] = Honk.G1Point({x: 1, y: 2});
2305
- scalars[boundary++] = mem.constantTermAccumulator;
2306
-
2307
- if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) {
2308
- revert ConsistencyCheckFailed();
2309
- }
2310
-
2311
- Honk.G1Point memory quotient_commitment = proof.kzgQuotient;
2312
-
2313
- commitments[boundary] = quotient_commitment;
2314
- scalars[boundary] = tp.shplonkZ; // evaluation challenge
2315
-
2316
- PairingInputs memory pair;
2317
- pair.P_0 = batchMul(commitments, scalars);
2318
- pair.P_1 = negateInplace(quotient_commitment);
2319
-
2320
- // Aggregate pairing points
2321
- Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1);
2322
- (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) =
2323
- convertPairingPointsToG1(proof.pairingPointObject);
2324
-
2325
- // Validate the points from the proof are on the curve
2326
- validateOnCurve(P_0_other);
2327
- validateOnCurve(P_1_other);
2328
-
2329
- // accumulate with aggregate points in proof
2330
- pair.P_0 = mulWithSeperator(pair.P_0, P_0_other, recursionSeparator);
2331
- pair.P_1 = mulWithSeperator(pair.P_1, P_1_other, recursionSeparator);
2332
-
2333
- return pairing(pair.P_0, pair.P_1);
2334
- }
2335
-
2336
- struct SmallSubgroupIpaIntermediates {
2337
- Fr[SUBGROUP_SIZE] challengePolyLagrange;
2338
- Fr challengePolyEval;
2339
- Fr lagrangeFirst;
2340
- Fr lagrangeLast;
2341
- Fr rootPower;
2342
- Fr[SUBGROUP_SIZE] denominators; // this has to disappear
2343
- Fr diff;
2344
- }
2345
-
2346
- function checkEvalsConsistency(
2347
- Fr[LIBRA_EVALUATIONS] memory libraPolyEvals,
2348
- Fr geminiR,
2349
- Fr[CONST_PROOF_SIZE_LOG_N] memory uChallenges,
2350
- Fr libraEval
2351
- ) internal view returns (bool check) {
2352
- Fr one = Fr.wrap(1);
2353
- Fr vanishingPolyEval = geminiR.pow(SUBGROUP_SIZE) - one;
2354
- if (vanishingPolyEval == Fr.wrap(0)) {
2355
- revert GeminiChallengeInSubgroup();
2356
- }
2357
-
2358
- SmallSubgroupIpaIntermediates memory mem;
2359
- mem.challengePolyLagrange[0] = one;
2360
- for (uint256 round = 0; round < $LOG_N; round++) {
2361
- uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round;
2362
- mem.challengePolyLagrange[currIdx] = one;
2363
- for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) {
2364
- mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round];
2365
- }
2366
- }
2367
-
2368
- mem.rootPower = one;
2369
- mem.challengePolyEval = Fr.wrap(0);
2370
- for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) {
2371
- mem.denominators[idx] = mem.rootPower * geminiR - one;
2372
- mem.denominators[idx] = mem.denominators[idx].invert();
2373
- mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx];
2374
- mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE;
2375
- }
2376
-
2377
- Fr numerator = vanishingPolyEval * Fr.wrap(SUBGROUP_SIZE).invert();
2378
- mem.challengePolyEval = mem.challengePolyEval * numerator;
2379
- mem.lagrangeFirst = mem.denominators[0] * numerator;
2380
- mem.lagrangeLast = mem.denominators[SUBGROUP_SIZE - 1] * numerator;
2381
-
2382
- mem.diff = mem.lagrangeFirst * libraPolyEvals[2];
2383
-
2384
- mem.diff = mem.diff
2385
- + (geminiR - SUBGROUP_GENERATOR_INVERSE)
2386
- * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval);
2387
- mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3];
2388
-
2389
- check = mem.diff == Fr.wrap(0);
2390
- }
2391
-
2392
- // This implementation is the same as above with different constants
2393
- function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars)
2394
- internal
2395
- view
2396
- returns (Honk.G1Point memory result)
2397
- {
2398
- uint256 limit = NUMBER_UNSHIFTED + $LOG_N + LIBRA_COMMITMENTS + 3;
2358
+ SmallSubgroupIpaIntermediates memory mem;
2359
+ mem.challengePolyLagrange[0] = one;
2360
+ for (uint256 round = 0; round < $LOG_N; round++) {
2361
+ uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round;
2362
+ mem.challengePolyLagrange[currIdx] = one;
2363
+ for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) {
2364
+ mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round];
2365
+ }
2366
+ }
2367
+
2368
+ mem.rootPower = one;
2369
+ mem.challengePolyEval = Fr.wrap(0);
2370
+ for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) {
2371
+ mem.denominators[idx] = mem.rootPower * geminiR - one;
2372
+ mem.denominators[idx] = mem.denominators[idx].invert();
2373
+ mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx];
2374
+ mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE;
2375
+ }
2376
+
2377
+ Fr numerator = vanishingPolyEval * Fr.wrap(SUBGROUP_SIZE).invert();
2378
+ mem.challengePolyEval = mem.challengePolyEval * numerator;
2379
+ mem.lagrangeFirst = mem.denominators[0] * numerator;
2380
+ mem.lagrangeLast = mem.denominators[SUBGROUP_SIZE - 1] * numerator;
2381
+
2382
+ mem.diff = mem.lagrangeFirst * libraPolyEvals[2];
2399
2383
 
2400
- // Validate all points are on the curve
2401
- for (uint256 i = 0; i < limit; ++i) {
2402
- validateOnCurve(base[i]);
2403
- }
2384
+ mem.diff =
2385
+ mem.diff +
2386
+ (geminiR - SUBGROUP_GENERATOR_INVERSE) *
2387
+ (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval);
2388
+ mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3];
2404
2389
 
2405
- bool success = true;
2406
- assembly {
2407
- let free := mload(0x40)
2390
+ check = mem.diff == Fr.wrap(0);
2391
+ }
2408
2392
 
2409
- let count := 0x01
2410
- for {} lt(count, add(limit, 1)) { count := add(count, 1) } {
2411
- // Get loop offsets
2412
- let base_base := add(base, mul(count, 0x20))
2413
- let scalar_base := add(scalars, mul(count, 0x20))
2393
+ // This implementation is the same as above with different constants
2394
+ function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) internal view returns (Honk.G1Point memory result) {
2395
+ uint256 limit = $MSMSize;
2414
2396
 
2415
- mstore(add(free, 0x40), mload(mload(base_base)))
2416
- mstore(add(free, 0x60), mload(add(0x20, mload(base_base))))
2417
- // Add scalar
2418
- mstore(add(free, 0x80), mload(scalar_base))
2397
+ // Validate all points are on the curve
2398
+ for (uint256 i = 0; i < limit; ++i) {
2399
+ validateOnCurve(base[i]);
2400
+ }
2401
+
2402
+ bool success = true;
2403
+ assembly {
2404
+ let free := mload(0x40)
2419
2405
 
2420
- success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40))
2421
- // accumulator = accumulator + accumulator_2
2422
- success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40))
2423
- }
2406
+ let count := 0x01
2407
+ for {} lt(count, add(limit, 1)) {
2408
+ count := add(count, 1)
2409
+ } {
2410
+ // Get loop offsets
2411
+ let base_base := add(base, mul(count, 0x20))
2412
+ let scalar_base := add(scalars, mul(count, 0x20))
2424
2413
 
2425
- // Return the result
2426
- mstore(result, mload(free))
2427
- mstore(add(result, 0x20), mload(add(free, 0x20)))
2428
- }
2414
+ mstore(add(free, 0x40), mload(mload(base_base)))
2415
+ mstore(add(free, 0x60), mload(add(0x20, mload(base_base))))
2416
+ // Add scalar
2417
+ mstore(add(free, 0x80), mload(scalar_base))
2429
2418
 
2430
- require(success, ShpleminiFailed());
2419
+ success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40))
2420
+ // accumulator = accumulator + accumulator_2
2421
+ success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40))
2422
+ }
2423
+
2424
+ // Return the result
2425
+ mstore(result, mload(free))
2426
+ mstore(add(result, 0x20), mload(add(free, 0x20)))
2431
2427
  }
2428
+
2429
+ require(success, ShpleminiFailed());
2430
+ }
2432
2431
  }
2433
2432
 
2434
2433
  contract HonkVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) {
2435
- function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) {
2436
- return HonkVerificationKey.loadVerificationKey();
2437
- }
2434
+ function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) {
2435
+ return HonkVerificationKey.loadVerificationKey();
2436
+ }
2438
2437
  }