@crisp-e3/contracts 0.5.9 → 0.5.11

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