@crisp-e3/contracts 0.2.3-test → 0.4.0

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