@aztec/ivc-integration 4.0.0-devnet.1-patch.0 → 4.0.0-devnet.2-patch.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,7 +5,7 @@ pragma solidity >=0.8.21;
5
5
  uint256 constant N = 16777216;
6
6
  uint256 constant LOG_N = 24;
7
7
  uint256 constant NUMBER_OF_PUBLIC_INPUTS = 9;
8
- uint256 constant VK_HASH = 0x185e8edbcab52e2a14a24c022fa732dc0d163521307aeadc47bde108a315ccb9;
8
+ uint256 constant VK_HASH = 0x1a71c9c8e8d1fc37789326bbf84db5728e523d1663d64a3ee4d907db31280798;
9
9
  library HonkVerificationKey {
10
10
  function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) {
11
11
  Honk.VerificationKey memory vk = Honk.VerificationKey({
@@ -13,76 +13,76 @@ library HonkVerificationKey {
13
13
  logCircuitSize: uint256(24),
14
14
  publicInputsSize: uint256(9),
15
15
  ql: Honk.G1Point({
16
- x: uint256(0x2e48850898f93635006347a1ad7e6eadcc69fbe122e84f99d01270c7e43ac052),
17
- y: uint256(0x21d6304540d7dd4df08fa77760f511ec666d4da3b272b85e80408841a70e8cfd)
16
+ x: uint256(0x288949ddd65c505eb5cc75f154e5758f88ffca8d2b27ecec1a8c18d64836555e),
17
+ y: uint256(0x16bdc5f6b2acfec698224a75fb369f408f10203d39d18c59f1e7fd2a06cd203e)
18
18
  }),
19
19
  qr: Honk.G1Point({
20
- x: uint256(0x173917d83223386ff1c152923991f1855759eef9a198286154570ca1d36a98ce),
21
- y: uint256(0x0674ee85da2f54b8e14dd5af4b32a2b0cb3e3a3dba56ce7ae0971659b0e03361)
20
+ x: uint256(0x0f00e9b07cdc3cb5ec6c7f0c3f015d52b77d22309cb6ce0231a4969c4ac47488),
21
+ y: uint256(0x173daa0ad01139b438315c03c09114c7054ed90ff4562466afb6e2cc48d69eae)
22
22
  }),
23
23
  qo: Honk.G1Point({
24
- x: uint256(0x0c8b021554e5de1a813945e3feb978627b07a82fa76a84ec367faf3ed696d9bc),
25
- y: uint256(0x2b9da23955ef9e29333475fda1fb429036772a6afe62a0e5515fb7d41a5b1d35)
24
+ x: uint256(0x2158a434ef091cbe48d362fda5c78de82eaaac1227a0c411ea21ad7c7d09bb89),
25
+ y: uint256(0x11e9f2ece557a9ab50b059decabaa2c3e102080b7389aa51335094c527a4399b)
26
26
  }),
27
27
  q4: Honk.G1Point({
28
- x: uint256(0x01b8d410fbccc82fa729c2c263210b4659a796b8e3fd1026aa431f4f0989d8e0),
29
- y: uint256(0x0982b0f18433072ed7869c4247cb58fb2d4a64ffe6c898fbcdaeb87a7b20ad47)
28
+ x: uint256(0x0b4c24c473fadd17b6fc761b52fc51cabd6dfb6b93a889128f7d5443d76b4e37),
29
+ y: uint256(0x2bcbb260096016d41e7b099758e6c7a4b2487a30728fa877f64163da404d2aa6)
30
30
  }),
31
31
  qm: Honk.G1Point({
32
- x: uint256(0x16d71bca7760db37db62b1e721d195a72aec16d892354445330aef8d5d725932),
33
- y: uint256(0x0d6c4b694bdc2dd7a8a9f3e42f24916c3aef5b512fdb54238651d40891200a4b)
32
+ x: uint256(0x305785ae74b36fc97f5cfbfcd19e965f14c3cb5fc07a866b4da8c40dfa43ea09),
33
+ y: uint256(0x1c618b8fb74db18bb65237cd45de3060ae7abd8931f5d6fbc0a3d18019f9a34c)
34
34
  }),
35
35
  qc: Honk.G1Point({
36
- x: uint256(0x142217e2103c3450547ae67759d97f66575d7c63940217a45284431a7a117e6d),
37
- y: uint256(0x2ec2495feb460bc480585b3067059b1924e74a7136cb93891a5dfea7ca77ac4f)
36
+ x: uint256(0x1e7d370a010cbec0e4a7a5be0832a59a0ed151216aa2ef780e40945770f04256),
37
+ y: uint256(0x19645746fcec15c93f0b8f620fbaf63b2d973d174b52ac8dc006b0292bc4d75a)
38
38
  }),
39
39
  qLookup: Honk.G1Point({
40
40
  x: uint256(0x22011c91613251ef53fd12a397e4bd6165b1ed309dcd94b33ac4226d34b68889),
41
41
  y: uint256(0x1fb02875a3542a3a6f4426ad912b70cf32b444a8e439701da6e8e7b30029cfc7)
42
42
  }),
43
43
  qArith: Honk.G1Point({
44
- x: uint256(0x28615af41fbe0e563e4ef6b3d59ef22f1c16c338a010872ecead3044233fda80),
45
- y: uint256(0x0f04123bd5340a798d470ac888bf2e61da67a9a0eaa0e01a07918037608e7696)
44
+ x: uint256(0x1d87e7db98f2289def77fcd6917b3195a04c8ea4fba2fbecb4953d53e2a5fa5e),
45
+ y: uint256(0x1e747aa5ce9c23534fba30d6fb86e148f55ff741cdc40aa72669b48fae5e78da)
46
46
  }),
47
47
  qDeltaRange: Honk.G1Point({
48
- x: uint256(0x2a97a0e73360e64dabb19e63466406ee9e3aecfb9be4dd0fbd0b569cdba79790),
49
- y: uint256(0x2bea56b178a8cb9f5edde063961f14313fd7f8420af097bcaf6db029d59aa03e)
48
+ x: uint256(0x197382111c5e19efd384fad7b441ab910eea06a90e613c0ac1d3558d5d1fe9bb),
49
+ y: uint256(0x2409d8e704a1df2a931db78f4b9eff415259f325e2c1d91752018ca1367bc813)
50
50
  }),
51
51
  qElliptic: Honk.G1Point({
52
- x: uint256(0x0db0817f2c2f4d554564462fbde5ae84015116b450e599dbc6bf3eb04a7ea55f),
53
- y: uint256(0x30142e494639de5de6cfaea6095ef4d45355475117a73ceadfb4080c1ba81d7e)
52
+ x: uint256(0x2f7bf7a96beccc2dcb1c979e895a5cf553347e85d3f09ac4ad25968c48ee6c0f),
53
+ y: uint256(0x0b9eb2604a17d6ee22545ecefc418886ee29c45e1f367a73781af51b70b5e59a)
54
54
  }),
55
55
  qMemory: Honk.G1Point({
56
- x: uint256(0x174ee95ad6c141bb397f1a7e5d89200e0d58932abdddbaf2667c697d51099c7d),
57
- y: uint256(0x193ed7e179456df124aa877fe5ede0df66cf4207ff18adb03bde8f9723bcd282)
56
+ x: uint256(0x067081ee7301cf94893b575b8a4607a50631612578b3230b0f63434e6ec1dcf3),
57
+ y: uint256(0x12e2288cdb86fd9caa98689e3ad9926b0372d1e7afeeb1ec6f7b251a176eb3e1)
58
58
  }),
59
59
  qNnf: Honk.G1Point({
60
- x: uint256(0x2336cc3bd8c6b07a39443e96a9059a378379f226df29d928e2ffa8690dd2d675),
61
- y: uint256(0x26449ddbade441702ce2020930bdbc0394b1d08296f2abbba61fa6304ee177d2)
60
+ x: uint256(0x303e26adafa2a01b02f559580ab06f571fe0f80473552ea7f4a47255af7b25f3),
61
+ y: uint256(0x2f599def60b1f4640366dba9daaf25de7a311eed3243dd96004ef5cb76dd4eef)
62
62
  }),
63
63
  qPoseidon2External: Honk.G1Point({
64
- x: uint256(0x2210bac445fbd0fae760783c2f4f3603af6391197a43406ae442c7bbd8717290),
65
- y: uint256(0x0f319739b840b0ed610f7dc786d3756eabbac54cf78d19aa2d2f2074551da344)
64
+ x: uint256(0x26c42fb5290e1f109c68ae1aadb78005efbe391925d37ff2682eb2fb9f673d81),
65
+ y: uint256(0x23d32838bb651192c72a84f8d35ad9ed23bfc60bd6a95aafe5f400ac79bb91a6)
66
66
  }),
67
67
  qPoseidon2Internal: Honk.G1Point({
68
- x: uint256(0x0d25e78b75e5e55b36c5803035b4a41f4f60325fcffb756828c643b94bd7f3e2),
69
- y: uint256(0x0ba15fff0aff16780820b71b18c6fd013a4834a3073ef223bcf5e5071301e129)
68
+ x: uint256(0x0e257562358c0fa333e2e92fb24893b10b6606582657ac902463c4125a8d76b4),
69
+ y: uint256(0x0a45aedd100c830266aa7501650f16d4c1a340da32528e2dc0ffcfe01de89c65)
70
70
  }),
71
71
  s1: Honk.G1Point({
72
- x: uint256(0x1757ec7be947d7cdb4c29ff16743fff8d9c2c36ccf9a86426a15d80fdcc9b66b),
73
- y: uint256(0x053af357505dd44258826f31f15c868cca22956d96e678ff9f97db6c5b4acfb9)
72
+ x: uint256(0x157c4a46714f7cc4bd0065ac8726ead6d925e1c3e2cb03ec167bde135d933eeb),
73
+ y: uint256(0x191e130de881a7b6a3560a6dc7f8f517abcc324c5f16b8e4dc901d5079ec7387)
74
74
  }),
75
75
  s2: Honk.G1Point({
76
- x: uint256(0x0bcc0029fe745278256418c2e9205b976762ca89ee511c820f242a6f90f79fc9),
77
- y: uint256(0x154cfcb63eb0fd1002608a3d3c01bb002df2ba807f38657bf773ebc79bfb1778)
76
+ x: uint256(0x0d02a27696ba0a08b32422113dc489b15acc111cb2fcd7f674ef4b903adcbdb3),
77
+ y: uint256(0x03499c1fd603f04963deab2fdbb152e7d8f88267d6ca8c817eea2643e608de29)
78
78
  }),
79
79
  s3: Honk.G1Point({
80
- x: uint256(0x233ae57153bc002d5920ccd54a0027d5e7861c9efc260fa750b8a65db2a180b6),
81
- y: uint256(0x015be97e1325a9ee23c55cdfcda66e3a38025ce873c17a2f1baba0fb2d37edac)
80
+ x: uint256(0x19c00dd88d170187c1717dfa303fe1c20604decf14cc58c4d6fe9ab346ae3f67),
81
+ y: uint256(0x0e3991d6fd47060ba758b4ed58736ffd3f082af15af7b84fafe5041b568dcff2)
82
82
  }),
83
83
  s4: Honk.G1Point({
84
- x: uint256(0x0a58eeeb7fe74c9d07a3a9f87047bf3d468f5530ca1cda46ccd5ca344b10ffa3),
85
- y: uint256(0x1eb2ab6dffc801912b77c8fedfc4fdd00a0debe3b09c047b80068684782466e3)
84
+ x: uint256(0x2ddedcc10b1b78151f4638978bc1ed9ceafa34911d7c8a2ffcdecc092f5f53bd),
85
+ y: uint256(0x2fee038cad71230c95a0497bd2a116171a431193a214d5a9677570fa45de2fda)
86
86
  }),
87
87
  t1: Honk.G1Point({
88
88
  x: uint256(0x099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d26),
@@ -101,28 +101,28 @@ library HonkVerificationKey {
101
101
  y: uint256(0x261522c4089330646aff96736194949330952ae74c573d1686d9cb4a00733854)
102
102
  }),
103
103
  id1: Honk.G1Point({
104
- x: uint256(0x0e56f3c4e9812e1a072462f1fb2b878ac131720d02340b0a5d2f79aad9fc781f),
105
- y: uint256(0x04bbc61db8f9c52a191f5a4fdcfaaf763aedf5a991e99ceaaa3746137b86e103)
104
+ x: uint256(0x11a02b4dd65f30c8d3b70888267f766e6abaaa0a1a5ad0bfa8cb1b597fedc630),
105
+ y: uint256(0x195cb3d252708ee60a385f0a3a84167322910971be20715e2965a4d97c1d1084)
106
106
  }),
107
107
  id2: Honk.G1Point({
108
- x: uint256(0x071aeeb0ab9ebb6c3dfd8c42f293f2b24038966fd6db76a8af196cfdb2082af2),
109
- y: uint256(0x2bf8640f4e90c75ace30f4d3ab583e95db1be8d2d92762e41ed2e0485485648b)
108
+ x: uint256(0x2a775e6191621460b6ae8f22d1cef5f92ec91bf820478d9619fc74d4e2956801),
109
+ y: uint256(0x057c47f7fa5fdfe685bd62595390454071e5749841dbabb0bcd45aee00ac9af7)
110
110
  }),
111
111
  id3: Honk.G1Point({
112
- x: uint256(0x1f701690072a25f55277d3368623d91c53e4c12c767df4e7a3e6b947839f68ac),
113
- y: uint256(0x20d2c98afe946420cb1dbc07c595b7bd4003f164c00201542107e1a2df919db9)
112
+ x: uint256(0x2ae753680356fd3211ca6b4210fe2682ebacc40d78c1ac3319be365682860bbd),
113
+ y: uint256(0x18aa3b045ae71c20a8b673b91ccf5ea61ba25bff9c958f9bad138e7e126a257a)
114
114
  }),
115
115
  id4: Honk.G1Point({
116
- x: uint256(0x07c33542cdea5dd8c3d6511c91d628b46de5eb1c4d55de36cb2a47df733c6ac2),
117
- y: uint256(0x05ad177da520f03c2131bb547a152af5a424f1623ca749a117441b4bc3ee4022)
116
+ x: uint256(0x2e12e92c2de2ec043befd1cb502a2e48458c7ea1dc19243640df4f6ec699b0be),
117
+ y: uint256(0x12655a8e9a32a038c570a5c83aa5dabe77df900b5c78bab8a64a252de136cdfc)
118
118
  }),
119
119
  lagrangeFirst: Honk.G1Point({
120
120
  x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001),
121
121
  y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002)
122
122
  }),
123
123
  lagrangeLast: Honk.G1Point({
124
- x: uint256(0x1cbaaf995ae271f946d0e6693442e1618f4aec34cb79b287b2512d73f230b985),
125
- y: uint256(0x010bf4d08073c88bfa78a3b46711cb165983ee1b89c410eb1f71e9167c984b4e)
124
+ x: uint256(0x04a0bdd0751a2184a98670fd8df17f372fac62b55691c8232c1484f51282cd7e),
125
+ y: uint256(0x121349b0521c633e5b12c42ec4f8cb0c1a3bdadcafe6350d901ae7cf13e7b5bd)
126
126
  })
127
127
  });
128
128
  return vk;
@@ -135,13 +135,37 @@ interface IVerifier {
135
135
  function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool);
136
136
  }
137
137
 
138
+ /**
139
+ * @notice Library of error codes
140
+ * @dev You can run `forge inspect Errors errors` to get the selectors for the optimised verifier
141
+ */
142
+ library Errors {
143
+ error ValueGeLimbMax();
144
+ error ValueGeGroupOrder();
145
+ error ValueGeFieldOrder();
146
+
147
+ error InvertOfZero();
148
+ error NotPowerOfTwo();
149
+ error ModExpFailed();
150
+
151
+ error ProofLengthWrong();
152
+ error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength);
153
+ error PublicInputsLengthWrong();
154
+ error SumcheckFailed();
155
+ error ShpleminiFailed();
156
+
157
+ error PointAtInfinity();
158
+
159
+ error ConsistencyCheckFailed();
160
+ error GeminiChallengeInSubgroup();
161
+ }
162
+
138
163
  type Fr is uint256;
139
164
 
140
165
  using {add as +} for Fr global;
141
166
  using {sub as -} for Fr global;
142
167
  using {mul as *} for Fr global;
143
168
 
144
- using {exp as ^} for Fr global;
145
169
  using {notEqual as !=} for Fr global;
146
170
  using {equal as ==} for Fr global;
147
171
 
@@ -156,26 +180,12 @@ Fr constant ZERO = Fr.wrap(0);
156
180
  // Instantiation
157
181
 
158
182
  library FrLib {
159
- function from(uint256 value) internal pure returns (Fr) {
160
- unchecked {
161
- return Fr.wrap(value % MODULUS);
162
- }
163
- }
164
-
165
- function fromBytes32(bytes32 value) internal pure returns (Fr) {
166
- unchecked {
167
- return Fr.wrap(uint256(value) % MODULUS);
168
- }
169
- }
170
-
171
- function toBytes32(Fr value) internal pure returns (bytes32) {
172
- unchecked {
173
- return bytes32(Fr.unwrap(value));
174
- }
175
- }
183
+ bytes4 internal constant FRLIB_MODEXP_FAILED_SELECTOR = 0xf8d61709;
176
184
 
177
185
  function invert(Fr value) internal view returns (Fr) {
178
186
  uint256 v = Fr.unwrap(value);
187
+ require(v != 0, Errors.InvertOfZero());
188
+
179
189
  uint256 result;
180
190
 
181
191
  // Call the modexp precompile to invert in the field
@@ -185,14 +195,15 @@ library FrLib {
185
195
  mstore(add(free, 0x20), 0x20)
186
196
  mstore(add(free, 0x40), 0x20)
187
197
  mstore(add(free, 0x60), v)
188
- mstore(add(free, 0x80), sub(MODULUS, 2))
198
+ mstore(add(free, 0x80), sub(MODULUS, 2))
189
199
  mstore(add(free, 0xa0), MODULUS)
190
200
  let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20)
191
201
  if iszero(success) {
192
- revert(0, 0)
202
+ mstore(0x00, FRLIB_MODEXP_FAILED_SELECTOR)
203
+ revert(0, 0x04)
193
204
  }
194
205
  result := mload(0x00)
195
- mstore(0x40, add(free, 0x80))
206
+ mstore(0x40, add(free, 0xc0))
196
207
  }
197
208
 
198
209
  return Fr.wrap(result);
@@ -200,6 +211,8 @@ library FrLib {
200
211
 
201
212
  function pow(Fr base, uint256 v) internal view returns (Fr) {
202
213
  uint256 b = Fr.unwrap(base);
214
+ // Only works for power of 2
215
+ require(v > 0 && (v & (v - 1)) == 0, Errors.NotPowerOfTwo());
203
216
  uint256 result;
204
217
 
205
218
  // Call the modexp precompile to invert in the field
@@ -209,14 +222,15 @@ library FrLib {
209
222
  mstore(add(free, 0x20), 0x20)
210
223
  mstore(add(free, 0x40), 0x20)
211
224
  mstore(add(free, 0x60), b)
212
- mstore(add(free, 0x80), v)
225
+ mstore(add(free, 0x80), v)
213
226
  mstore(add(free, 0xa0), MODULUS)
214
227
  let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20)
215
228
  if iszero(success) {
216
- revert(0, 0)
229
+ mstore(0x00, FRLIB_MODEXP_FAILED_SELECTOR)
230
+ revert(0, 0x04)
217
231
  }
218
232
  result := mload(0x00)
219
- mstore(0x40, add(free, 0x80))
233
+ mstore(0x40, add(free, 0xc0))
220
234
  }
221
235
 
222
236
  return Fr.wrap(result);
@@ -245,6 +259,27 @@ library FrLib {
245
259
  return Fr.wrap(MODULUS - Fr.unwrap(value));
246
260
  }
247
261
  }
262
+
263
+ function from(uint256 value) internal pure returns (Fr) {
264
+ unchecked {
265
+ require(value < MODULUS, Errors.ValueGeFieldOrder());
266
+ return Fr.wrap(value);
267
+ }
268
+ }
269
+
270
+ function fromBytes32(bytes32 value) internal pure returns (Fr) {
271
+ unchecked {
272
+ uint256 v = uint256(value);
273
+ require(v < MODULUS, Errors.ValueGeFieldOrder());
274
+ return Fr.wrap(v);
275
+ }
276
+ }
277
+
278
+ function toBytes32(Fr value) internal pure returns (bytes32) {
279
+ unchecked {
280
+ return bytes32(Fr.unwrap(value));
281
+ }
282
+ }
248
283
  }
249
284
 
250
285
  // Free functions
@@ -266,15 +301,6 @@ function sub(Fr a, Fr b) pure returns (Fr) {
266
301
  }
267
302
  }
268
303
 
269
- function exp(Fr base, Fr exponent) pure returns (Fr) {
270
- if (Fr.unwrap(exponent) == 0) return Fr.wrap(1);
271
- // Implement exponent with a loop as we will overflow otherwise
272
- for (uint256 i = 1; i < Fr.unwrap(exponent); i += i) {
273
- base = base * base;
274
- }
275
- return base;
276
- }
277
-
278
304
  function notEqual(Fr a, Fr b) pure returns (bool) {
279
305
  unchecked {
280
306
  return Fr.unwrap(a) != Fr.unwrap(b);
@@ -486,7 +512,7 @@ library TranscriptLib {
486
512
  uint256 vkHash,
487
513
  uint256 publicInputsSize,
488
514
  uint256 logN
489
- ) internal view returns (Transcript memory t) {
515
+ ) internal pure returns (Transcript memory t) {
490
516
  Fr previousChallenge;
491
517
  (t.relationParameters, previousChallenge) =
492
518
  generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge);
@@ -513,8 +539,8 @@ library TranscriptLib {
513
539
  // Split into two equal 127-bit chunks (254/2)
514
540
  uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits
515
541
  uint256 hi = challengeU256 >> 127;
516
- first = FrLib.fromBytes32(bytes32(lo));
517
- second = FrLib.fromBytes32(bytes32(hi));
542
+ first = FrLib.from(lo);
543
+ second = FrLib.from(hi);
518
544
  }
519
545
 
520
546
  function generateRelationParametersChallenges(
@@ -539,7 +565,8 @@ library TranscriptLib {
539
565
  round0[0] = bytes32(vkHash);
540
566
 
541
567
  for (uint256 i = 0; i < publicInputsSize - PAIRING_POINTS_SIZE; i++) {
542
- round0[1 + i] = bytes32(publicInputs[i]);
568
+ require(uint256(publicInputs[i]) < P, Errors.ValueGeFieldOrder());
569
+ round0[1 + i] = publicInputs[i];
543
570
  }
544
571
  for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) {
545
572
  round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]);
@@ -554,7 +581,7 @@ library TranscriptLib {
554
581
  round0[1 + publicInputsSize + 4] = bytes32(proof.w3.x);
555
582
  round0[1 + publicInputsSize + 5] = bytes32(proof.w3.y);
556
583
 
557
- previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0)));
584
+ previousChallenge = FrLib.from(uint256(keccak256(abi.encodePacked(round0))) % P);
558
585
  (eta,) = splitChallenge(previousChallenge);
559
586
  }
560
587
 
@@ -572,7 +599,7 @@ library TranscriptLib {
572
599
  round1[5] = bytes32(proof.w4.x);
573
600
  round1[6] = bytes32(proof.w4.y);
574
601
 
575
- nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1)));
602
+ nextPreviousChallenge = FrLib.from(uint256(keccak256(abi.encodePacked(round1))) % P);
576
603
  (beta, gamma) = splitChallenge(nextPreviousChallenge);
577
604
  }
578
605
 
@@ -590,7 +617,7 @@ library TranscriptLib {
590
617
  alpha0[3] = proof.zPerm.x;
591
618
  alpha0[4] = proof.zPerm.y;
592
619
 
593
- nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0)));
620
+ nextPreviousChallenge = FrLib.from(uint256(keccak256(abi.encodePacked(alpha0))) % P);
594
621
  Fr alpha;
595
622
  (alpha,) = splitChallenge(nextPreviousChallenge);
596
623
 
@@ -606,7 +633,7 @@ library TranscriptLib {
606
633
  pure
607
634
  returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge)
608
635
  {
609
- previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))));
636
+ previousChallenge = FrLib.from(uint256(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))) % P);
610
637
  (gateChallenges[0],) = splitChallenge(previousChallenge);
611
638
  for (uint256 i = 1; i < logN; i++) {
612
639
  gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1];
@@ -626,7 +653,7 @@ library TranscriptLib {
626
653
  for (uint256 j = 0; j < BATCHED_RELATION_PARTIAL_LENGTH; j++) {
627
654
  univariateChal[j + 1] = proof.sumcheckUnivariates[i][j];
628
655
  }
629
- prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal)));
656
+ prevChallenge = FrLib.from(uint256(keccak256(abi.encodePacked(univariateChal))) % P);
630
657
  Fr unused;
631
658
  (sumcheckChallenges[i], unused) = splitChallenge(prevChallenge);
632
659
  }
@@ -645,7 +672,7 @@ library TranscriptLib {
645
672
  rhoChallengeElements[i + 1] = proof.sumcheckEvaluations[i];
646
673
  }
647
674
 
648
- nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements)));
675
+ nextPreviousChallenge = FrLib.from(uint256(keccak256(abi.encodePacked(rhoChallengeElements))) % P);
649
676
  Fr unused;
650
677
  (rho, unused) = splitChallenge(nextPreviousChallenge);
651
678
  }
@@ -663,7 +690,7 @@ library TranscriptLib {
663
690
  gR[2 + i * 2] = proof.geminiFoldComms[i].y;
664
691
  }
665
692
 
666
- nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR)));
693
+ nextPreviousChallenge = FrLib.from(uint256(keccak256(abi.encodePacked(gR))) % P);
667
694
  Fr unused;
668
695
  (geminiR, unused) = splitChallenge(nextPreviousChallenge);
669
696
  }
@@ -680,14 +707,14 @@ library TranscriptLib {
680
707
  shplonkNuChallengeElements[i + 1] = Fr.unwrap(proof.geminiAEvaluations[i]);
681
708
  }
682
709
 
683
- nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements)));
710
+ nextPreviousChallenge = FrLib.from(uint256(keccak256(abi.encodePacked(shplonkNuChallengeElements))) % P);
684
711
  Fr unused;
685
712
  (shplonkNu, unused) = splitChallenge(nextPreviousChallenge);
686
713
  }
687
714
 
688
715
  function generateShplonkZChallenge(Honk.Proof memory proof, Fr prevChallenge)
689
716
  internal
690
- view
717
+ pure
691
718
  returns (Fr shplonkZ, Fr nextPreviousChallenge)
692
719
  {
693
720
  uint256[3] memory shplonkZChallengeElements;
@@ -696,7 +723,7 @@ library TranscriptLib {
696
723
  shplonkZChallengeElements[1] = proof.shplonkQ.x;
697
724
  shplonkZChallengeElements[2] = proof.shplonkQ.y;
698
725
 
699
- nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements)));
726
+ nextPreviousChallenge = FrLib.from(uint256(keccak256(abi.encodePacked(shplonkZChallengeElements))) % P);
700
727
  Fr unused;
701
728
  (shplonkZ, unused) = splitChallenge(nextPreviousChallenge);
702
729
  }
@@ -706,7 +733,10 @@ library TranscriptLib {
706
733
 
707
734
  // Pairing point object
708
735
  for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) {
709
- p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]);
736
+ uint256 limb = uint256(bytes32(proof[boundary:boundary + FIELD_ELEMENT_SIZE]));
737
+ // lo limbs (even index) < 2^136, hi limbs (odd index) < 2^120
738
+ require(limb < 2 ** (i % 2 == 0 ? 136 : 120), Errors.ValueGeLimbMax());
739
+ p.pairingPointObject[i] = FrLib.from(limb);
710
740
  boundary += FIELD_ELEMENT_SIZE;
711
741
  }
712
742
  // Commitments
@@ -763,10 +793,91 @@ library TranscriptLib {
763
793
  }
764
794
  }
765
795
 
766
- // Field arithmetic libraries
767
-
768
796
  library RelationsLib {
797
+ struct EllipticParams {
798
+ // Points
799
+ Fr x_1;
800
+ Fr y_1;
801
+ Fr x_2;
802
+ Fr y_2;
803
+ Fr y_3;
804
+ Fr x_3;
805
+ // push accumulators into memory
806
+ Fr x_double_identity;
807
+ }
808
+
809
+ // Parameters used within the Memory Relation
810
+ // A struct is used to work around stack too deep. This relation has alot of variables
811
+ struct MemParams {
812
+ Fr memory_record_check;
813
+ Fr partial_record_check;
814
+ Fr next_gate_access_type;
815
+ Fr record_delta;
816
+ Fr index_delta;
817
+ Fr adjacent_values_match_if_adjacent_indices_match;
818
+ Fr adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation;
819
+ Fr access_check;
820
+ Fr next_gate_access_type_is_boolean;
821
+ Fr ROM_consistency_check_identity;
822
+ Fr RAM_consistency_check_identity;
823
+ Fr timestamp_delta;
824
+ Fr RAM_timestamp_check_identity;
825
+ Fr memory_identity;
826
+ Fr index_is_monotonically_increasing;
827
+ }
828
+
829
+ // Parameters used within the Non-Native Field Relation
830
+ // A struct is used to work around stack too deep. This relation has alot of variables
831
+ struct NnfParams {
832
+ Fr limb_subproduct;
833
+ Fr non_native_field_gate_1;
834
+ Fr non_native_field_gate_2;
835
+ Fr non_native_field_gate_3;
836
+ Fr limb_accumulator_1;
837
+ Fr limb_accumulator_2;
838
+ Fr nnf_identity;
839
+ }
840
+
841
+ struct PoseidonExternalParams {
842
+ Fr s1;
843
+ Fr s2;
844
+ Fr s3;
845
+ Fr s4;
846
+ Fr u1;
847
+ Fr u2;
848
+ Fr u3;
849
+ Fr u4;
850
+ Fr t0;
851
+ Fr t1;
852
+ Fr t2;
853
+ Fr t3;
854
+ Fr v1;
855
+ Fr v2;
856
+ Fr v3;
857
+ Fr v4;
858
+ Fr q_pos_by_scaling;
859
+ }
860
+
861
+ struct PoseidonInternalParams {
862
+ Fr u1;
863
+ Fr u2;
864
+ Fr u3;
865
+ Fr u4;
866
+ Fr u_sum;
867
+ Fr v1;
868
+ Fr v2;
869
+ Fr v3;
870
+ Fr v4;
871
+ Fr s1;
872
+ Fr q_pos_by_scaling;
873
+ }
874
+
769
875
  Fr internal constant GRUMPKIN_CURVE_B_PARAMETER_NEGATED = Fr.wrap(17); // -(-17)
876
+ uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000;
877
+
878
+ // Constants for the Non-native Field relation
879
+ Fr internal constant LIMB_SIZE = Fr.wrap(uint256(1) << 68);
880
+ Fr internal constant SUBLIMB_SHIFT = Fr.wrap(uint256(1) << 14);
770
881
 
771
882
  function accumulateRelationEvaluations(
772
883
  Fr[NUMBER_OF_ENTITIES] memory purportedEvaluations,
@@ -800,12 +911,10 @@ library RelationsLib {
800
911
  return p[uint256(_wire)];
801
912
  }
802
913
 
803
- uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000;
804
914
  /**
805
915
  * Ultra Arithmetic Relation
806
916
  *
807
917
  */
808
-
809
918
  function accumulateArithmeticRelation(
810
919
  Fr[NUMBER_OF_ENTITIES] memory p,
811
920
  Fr[NUMBER_OF_SUBRELATIONS] memory evals,
@@ -992,18 +1101,6 @@ library RelationsLib {
992
1101
  }
993
1102
  }
994
1103
 
995
- struct EllipticParams {
996
- // Points
997
- Fr x_1;
998
- Fr y_1;
999
- Fr x_2;
1000
- Fr y_2;
1001
- Fr y_3;
1002
- Fr x_3;
1003
- // push accumulators into memory
1004
- Fr x_double_identity;
1005
- }
1006
-
1007
1104
  function accumulateEllipticRelation(
1008
1105
  Fr[NUMBER_OF_ENTITIES] memory p,
1009
1106
  Fr[NUMBER_OF_SUBRELATIONS] memory evals,
@@ -1072,26 +1169,6 @@ library RelationsLib {
1072
1169
  }
1073
1170
  }
1074
1171
 
1075
- // Parameters used within the Memory Relation
1076
- // A struct is used to work around stack too deep. This relation has alot of variables
1077
- struct MemParams {
1078
- Fr memory_record_check;
1079
- Fr partial_record_check;
1080
- Fr next_gate_access_type;
1081
- Fr record_delta;
1082
- Fr index_delta;
1083
- Fr adjacent_values_match_if_adjacent_indices_match;
1084
- Fr adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation;
1085
- Fr access_check;
1086
- Fr next_gate_access_type_is_boolean;
1087
- Fr ROM_consistency_check_identity;
1088
- Fr RAM_consistency_check_identity;
1089
- Fr timestamp_delta;
1090
- Fr RAM_timestamp_check_identity;
1091
- Fr memory_identity;
1092
- Fr index_is_monotonically_increasing;
1093
- }
1094
-
1095
1172
  function accumulateMemoryRelation(
1096
1173
  Fr[NUMBER_OF_ENTITIES] memory p,
1097
1174
  Honk.RelationParameters memory rp,
@@ -1261,22 +1338,6 @@ library RelationsLib {
1261
1338
  evals[13] = ap.memory_identity;
1262
1339
  }
1263
1340
 
1264
- // Constants for the Non-native Field relation
1265
- Fr constant LIMB_SIZE = Fr.wrap(uint256(1) << 68);
1266
- Fr constant SUBLIMB_SHIFT = Fr.wrap(uint256(1) << 14);
1267
-
1268
- // Parameters used within the Non-Native Field Relation
1269
- // A struct is used to work around stack too deep. This relation has alot of variables
1270
- struct NnfParams {
1271
- Fr limb_subproduct;
1272
- Fr non_native_field_gate_1;
1273
- Fr non_native_field_gate_2;
1274
- Fr non_native_field_gate_3;
1275
- Fr limb_accumulator_1;
1276
- Fr limb_accumulator_2;
1277
- Fr nnf_identity;
1278
- }
1279
-
1280
1341
  function accumulateNnfRelation(
1281
1342
  Fr[NUMBER_OF_ENTITIES] memory p,
1282
1343
  Fr[NUMBER_OF_SUBRELATIONS] memory evals,
@@ -1353,26 +1414,6 @@ library RelationsLib {
1353
1414
  evals[19] = ap.nnf_identity;
1354
1415
  }
1355
1416
 
1356
- struct PoseidonExternalParams {
1357
- Fr s1;
1358
- Fr s2;
1359
- Fr s3;
1360
- Fr s4;
1361
- Fr u1;
1362
- Fr u2;
1363
- Fr u3;
1364
- Fr u4;
1365
- Fr t0;
1366
- Fr t1;
1367
- Fr t2;
1368
- Fr t3;
1369
- Fr v1;
1370
- Fr v2;
1371
- Fr v3;
1372
- Fr v4;
1373
- Fr q_pos_by_scaling;
1374
- }
1375
-
1376
1417
  function accumulatePoseidonExternalRelation(
1377
1418
  Fr[NUMBER_OF_ENTITIES] memory p,
1378
1419
  Fr[NUMBER_OF_SUBRELATIONS] memory evals,
@@ -1415,20 +1456,6 @@ library RelationsLib {
1415
1456
  evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT));
1416
1457
  }
1417
1458
 
1418
- struct PoseidonInternalParams {
1419
- Fr u1;
1420
- Fr u2;
1421
- Fr u3;
1422
- Fr u4;
1423
- Fr u_sum;
1424
- Fr v1;
1425
- Fr v2;
1426
- Fr v3;
1427
- Fr v4;
1428
- Fr s1;
1429
- Fr q_pos_by_scaling;
1430
- }
1431
-
1432
1459
  function accumulatePoseidonInternalRelation(
1433
1460
  Fr[NUMBER_OF_ENTITIES] memory p,
1434
1461
  Fr[NUMBER_OF_SUBRELATIONS] memory evals,
@@ -1484,8 +1511,6 @@ library RelationsLib {
1484
1511
  }
1485
1512
  }
1486
1513
 
1487
- // Field arithmetic libraries - prevent littering the code with modmul / addmul
1488
-
1489
1514
  library CommitmentSchemeLib {
1490
1515
  using FrLib for Fr;
1491
1516
 
@@ -1515,16 +1540,7 @@ library CommitmentSchemeLib {
1515
1540
  Fr[] foldPosEvaluations;
1516
1541
  }
1517
1542
 
1518
- function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) {
1519
- Fr[] memory squares = new Fr[](logN);
1520
- squares[0] = r;
1521
- for (uint256 i = 1; i < logN; ++i) {
1522
- squares[i] = squares[i - 1].sqr();
1523
- }
1524
- return squares;
1525
- }
1526
1543
  // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., m-1
1527
-
1528
1544
  function computeFoldPosEvaluations(
1529
1545
  Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckUChallenges,
1530
1546
  Fr batchedEvalAccumulator,
@@ -1547,23 +1563,19 @@ library CommitmentSchemeLib {
1547
1563
  }
1548
1564
  return foldPosEvaluations;
1549
1565
  }
1550
- }
1551
-
1552
- uint256 constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; // EC group order. F_q
1553
1566
 
1554
- function bytes32ToString(bytes32 value) pure returns (string memory result) {
1555
- bytes memory alphabet = "0123456789abcdef";
1556
-
1557
- bytes memory str = new bytes(66);
1558
- str[0] = "0";
1559
- str[1] = "x";
1560
- for (uint256 i = 0; i < 32; i++) {
1561
- str[2 + i * 2] = alphabet[uint8(value[i] >> 4)];
1562
- str[3 + i * 2] = alphabet[uint8(value[i] & 0x0f)];
1567
+ function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) {
1568
+ Fr[] memory squares = new Fr[](logN);
1569
+ squares[0] = r;
1570
+ for (uint256 i = 1; i < logN; ++i) {
1571
+ squares[i] = squares[i - 1].sqr();
1572
+ }
1573
+ return squares;
1563
1574
  }
1564
- result = string(str);
1565
1575
  }
1566
1576
 
1577
+ uint256 constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; // EC group order. F_q
1578
+
1567
1579
  // Fr utility
1568
1580
 
1569
1581
  function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) {
@@ -1572,20 +1584,31 @@ function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) {
1572
1584
 
1573
1585
  // EC Point utilities
1574
1586
  function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) {
1575
- point = Honk.G1Point({
1576
- x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q
1577
- });
1587
+ uint256 x = uint256(bytes32(proofSection[0x00:0x20]));
1588
+ uint256 y = uint256(bytes32(proofSection[0x20:0x40]));
1589
+ require(x < Q && y < Q, Errors.ValueGeGroupOrder());
1590
+
1591
+ // Reject the point at infinity (0,0). EVM precompiles silently treat (0,0)
1592
+ // as the identity element, which could zero out commitments.
1593
+ // On-curve validation (y² = x³ + 3) is handled by the ecAdd/ecMul precompiles
1594
+ // per EIP-196, so we only need to catch this special case here.
1595
+ require((x | y) != 0, Errors.PointAtInfinity());
1596
+
1597
+ point = Honk.G1Point({x: x, y: y});
1578
1598
  }
1579
1599
 
1580
1600
  function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) {
1581
- point.y = (Q - point.y) % Q;
1601
+ // When y == 0 (order-2 point), negation is the same point. Q - 0 = Q which is >= Q.
1602
+ if (point.y != 0) {
1603
+ point.y = Q - point.y;
1604
+ }
1582
1605
  return point;
1583
1606
  }
1584
1607
 
1585
1608
  /**
1586
1609
  * Convert the pairing points to G1 points.
1587
1610
  *
1588
- * The pairing points are serialised as an array of 136-bit limbs representing two points
1611
+ * The pairing points are serialised as an array of 2 limbs representing two points
1589
1612
  * (P0 and P1, used for lhs and rhs of pairing operation).
1590
1613
  *
1591
1614
  * There are 2 limbs (lo, hi) for each coordinate, so 4 limbs per point, 8 total.
@@ -1599,22 +1622,28 @@ function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints)
1599
1622
  pure
1600
1623
  returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs)
1601
1624
  {
1602
- // P0 (lhs): x = lo + hi << 136
1625
+ // P0 (lhs): x = lo | (hi << 136)
1603
1626
  uint256 lhsX = Fr.unwrap(pairingPoints[0]);
1604
1627
  lhsX |= Fr.unwrap(pairingPoints[1]) << 136;
1605
- lhs.x = lhsX;
1606
1628
 
1607
1629
  uint256 lhsY = Fr.unwrap(pairingPoints[2]);
1608
1630
  lhsY |= Fr.unwrap(pairingPoints[3]) << 136;
1609
- lhs.y = lhsY;
1610
1631
 
1611
- // P1 (rhs): x = lo + hi << 136
1632
+ // P1 (rhs): x = lo | (hi << 136)
1612
1633
  uint256 rhsX = Fr.unwrap(pairingPoints[4]);
1613
1634
  rhsX |= Fr.unwrap(pairingPoints[5]) << 136;
1614
- rhs.x = rhsX;
1615
1635
 
1616
1636
  uint256 rhsY = Fr.unwrap(pairingPoints[6]);
1617
1637
  rhsY |= Fr.unwrap(pairingPoints[7]) << 136;
1638
+
1639
+ // Reconstructed coordinates must be < Q to prevent malleability.
1640
+ // Without this, two different limb encodings could map to the same curve point
1641
+ // (via mulmod reduction in on-curve checks) but produce different transcript hashes.
1642
+ require(lhsX < Q && lhsY < Q && rhsX < Q && rhsY < Q, Errors.ValueGeGroupOrder());
1643
+
1644
+ lhs.x = lhsX;
1645
+ lhs.y = lhsY;
1646
+ rhs.x = rhsX;
1618
1647
  rhs.y = rhsY;
1619
1648
  }
1620
1649
 
@@ -1652,7 +1681,7 @@ function generateRecursionSeparator(
1652
1681
  recursionSeparatorElements[6] = accRhs.x;
1653
1682
  recursionSeparatorElements[7] = accRhs.y;
1654
1683
 
1655
- recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements)));
1684
+ recursionSeparator = FrLib.from(uint256(keccak256(abi.encodePacked(recursionSeparatorElements))) % P);
1656
1685
  }
1657
1686
 
1658
1687
  /**
@@ -1770,17 +1799,20 @@ function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (H
1770
1799
  return result;
1771
1800
  }
1772
1801
 
1773
- function validateOnCurve(Honk.G1Point memory point) pure {
1774
- uint256 x = point.x;
1775
- uint256 y = point.y;
1802
+ function rejectPointAtInfinity(Honk.G1Point memory point) pure {
1803
+ require((point.x | point.y) != 0, Errors.PointAtInfinity());
1804
+ }
1776
1805
 
1777
- bool success = false;
1778
- assembly {
1779
- let xx := mulmod(x, x, Q)
1780
- success := eq(mulmod(y, y, Q), addmod(mulmod(x, xx, Q), 3, Q))
1806
+ /**
1807
+ * Check if pairing point limbs are all zero (default/infinity).
1808
+ * Default pairing points indicate no recursive verification occurred.
1809
+ */
1810
+ function arePairingPointsDefault(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) pure returns (bool) {
1811
+ uint256 acc = 0;
1812
+ for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) {
1813
+ acc |= Fr.unwrap(pairingPoints[i]);
1781
1814
  }
1782
-
1783
- require(success, "point is not on the curve");
1815
+ return acc == 0;
1784
1816
  }
1785
1817
 
1786
1818
  function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) {
@@ -1805,18 +1837,24 @@ function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns
1805
1837
  decodedResult = success && abi.decode(result, (bool));
1806
1838
  }
1807
1839
 
1808
- // Field arithmetic libraries - prevent littering the code with modmul / addmul
1809
-
1840
+ abstract contract BaseHonkVerifier is IVerifier {
1841
+ using FrLib for Fr;
1810
1842
 
1843
+ // Constants for proof length calculation (matching UltraKeccakFlavor)
1844
+ uint256 internal constant NUM_WITNESS_ENTITIES = 8;
1845
+ uint256 internal constant NUM_ELEMENTS_COMM = 2; // uint256 elements for curve points
1846
+ uint256 internal constant NUM_ELEMENTS_FR = 1; // uint256 elements for field elements
1811
1847
 
1848
+ // Number of field elements in a ultra keccak honk proof for log_n = 25, including pairing point object.
1849
+ uint256 internal constant PROOF_SIZE = 350; // Legacy constant - will be replaced by calculateProofSize($LOG_N)
1850
+ uint256 internal constant SHIFTED_COMMITMENTS_START = 29;
1812
1851
 
1813
- abstract contract BaseHonkVerifier is IVerifier {
1814
- using FrLib for Fr;
1852
+ uint256 internal constant PERMUTATION_ARGUMENT_VALUE_SEPARATOR = 1 << 28;
1815
1853
 
1816
- uint256 immutable $N;
1817
- uint256 immutable $LOG_N;
1818
- uint256 immutable $VK_HASH;
1819
- uint256 immutable $NUM_PUBLIC_INPUTS;
1854
+ uint256 internal immutable $N;
1855
+ uint256 internal immutable $LOG_N;
1856
+ uint256 internal immutable $VK_HASH;
1857
+ uint256 internal immutable $NUM_PUBLIC_INPUTS;
1820
1858
 
1821
1859
  constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) {
1822
1860
  $N = _N;
@@ -1825,59 +1863,19 @@ abstract contract BaseHonkVerifier is IVerifier {
1825
1863
  $NUM_PUBLIC_INPUTS = _numPublicInputs;
1826
1864
  }
1827
1865
 
1828
- // Errors
1829
- error ProofLengthWrong();
1830
- error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength);
1831
- error PublicInputsLengthWrong();
1832
- error SumcheckFailed();
1833
- error ShpleminiFailed();
1834
-
1835
- // Constants for proof length calculation (matching UltraKeccakFlavor)
1836
- uint256 constant NUM_WITNESS_ENTITIES = 8;
1837
- uint256 constant NUM_ELEMENTS_COMM = 2; // uint256 elements for curve points
1838
- uint256 constant NUM_ELEMENTS_FR = 1; // uint256 elements for field elements
1839
-
1840
- // Calculate proof size based on log_n (matching UltraKeccakFlavor formula)
1841
- function calculateProofSize(uint256 logN) internal pure returns (uint256) {
1842
- // Witness commitments
1843
- uint256 proofLength = NUM_WITNESS_ENTITIES * NUM_ELEMENTS_COMM; // witness commitments
1844
-
1845
- // Sumcheck
1846
- proofLength += logN * BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates
1847
- proofLength += NUMBER_OF_ENTITIES * NUM_ELEMENTS_FR; // sumcheck evaluations
1848
-
1849
- // Gemini
1850
- proofLength += (logN - 1) * NUM_ELEMENTS_COMM; // Gemini Fold commitments
1851
- proofLength += logN * NUM_ELEMENTS_FR; // Gemini evaluations
1852
-
1853
- // Shplonk and KZG commitments
1854
- proofLength += NUM_ELEMENTS_COMM * 2; // Shplonk Q and KZG W commitments
1855
-
1856
- // Pairing points
1857
- proofLength += PAIRING_POINTS_SIZE; // pairing inputs carried on public inputs
1858
-
1859
- return proofLength;
1860
- }
1861
-
1862
- // Number of field elements in a ultra keccak honk proof for log_n = 25, including pairing point object.
1863
- uint256 constant PROOF_SIZE = 350; // Legacy constant - will be replaced by calculateProofSize($LOG_N)
1864
- uint256 constant SHIFTED_COMMITMENTS_START = 29;
1865
-
1866
- function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory);
1867
-
1868
1866
  function verify(bytes calldata proof, bytes32[] calldata publicInputs) public view override returns (bool) {
1869
1867
  // Calculate expected proof size based on $LOG_N
1870
1868
  uint256 expectedProofSize = calculateProofSize($LOG_N);
1871
1869
 
1872
1870
  // Check the received proof is the expected size where each field element is 32 bytes
1873
1871
  if (proof.length != expectedProofSize * 32) {
1874
- revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32);
1872
+ revert Errors.ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32);
1875
1873
  }
1876
1874
 
1877
1875
  Honk.VerificationKey memory vk = loadVerificationKey();
1878
1876
  Honk.Proof memory p = TranscriptLib.loadProof(proof, $LOG_N);
1879
1877
  if (publicInputs.length != vk.publicInputsSize - PAIRING_POINTS_SIZE) {
1880
- revert PublicInputsLengthWrong();
1878
+ revert Errors.PublicInputsLengthWrong();
1881
1879
  }
1882
1880
 
1883
1881
  // Generate the fiat shamir challenges for the whole protocol
@@ -1894,16 +1892,14 @@ abstract contract BaseHonkVerifier is IVerifier {
1894
1892
 
1895
1893
  // Sumcheck
1896
1894
  bool sumcheckVerified = verifySumcheck(p, t);
1897
- if (!sumcheckVerified) revert SumcheckFailed();
1895
+ if (!sumcheckVerified) revert Errors.SumcheckFailed();
1898
1896
 
1899
1897
  bool shpleminiVerified = verifyShplemini(p, vk, t);
1900
- if (!shpleminiVerified) revert ShpleminiFailed();
1898
+ if (!shpleminiVerified) revert Errors.ShpleminiFailed();
1901
1899
 
1902
1900
  return sumcheckVerified && shpleminiVerified; // Boolean condition not required - nice for vanity :)
1903
1901
  }
1904
1902
 
1905
- uint256 constant PERMUTATION_ARGUMENT_VALUE_SEPARATOR = 1 << 28;
1906
-
1907
1903
  function computePublicInputDelta(
1908
1904
  bytes32[] memory publicInputs,
1909
1905
  Fr[PAIRING_POINTS_SIZE] memory pairingPointObject,
@@ -1950,7 +1946,7 @@ abstract contract BaseHonkVerifier is IVerifier {
1950
1946
  for (uint256 round = 0; round < $LOG_N; ++round) {
1951
1947
  Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round];
1952
1948
  bool valid = checkSum(roundUnivariate, roundTarget);
1953
- if (!valid) revert SumcheckFailed();
1949
+ if (!valid) revert Errors.SumcheckFailed();
1954
1950
 
1955
1951
  Fr roundChallenge = tp.sumCheckUChallenges[round];
1956
1952
 
@@ -1966,15 +1962,6 @@ abstract contract BaseHonkVerifier is IVerifier {
1966
1962
  verified = (grandHonkRelationSum == roundTarget);
1967
1963
  }
1968
1964
 
1969
- function checkSum(Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate, Fr roundTarget)
1970
- internal
1971
- pure
1972
- returns (bool checked)
1973
- {
1974
- Fr totalSum = roundUnivariate[0] + roundUnivariate[1];
1975
- checked = totalSum == roundTarget;
1976
- }
1977
-
1978
1965
  // Return the new target sum for the next sumcheck round
1979
1966
  function computeNextTargetSum(Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge)
1980
1967
  internal
@@ -2018,16 +2005,6 @@ abstract contract BaseHonkVerifier is IVerifier {
2018
2005
  targetSum = targetSum * numeratorValue;
2019
2006
  }
2020
2007
 
2021
- // Univariate evaluation of the monomial ((1-X_l) + X_l.B_l) at the challenge point X_l=u_l
2022
- function partiallyEvaluatePOW(Fr gateChallenge, Fr currentEvaluation, Fr roundChallenge)
2023
- internal
2024
- pure
2025
- returns (Fr newEvaluation)
2026
- {
2027
- Fr univariateEval = ONE + (roundChallenge * (gateChallenge - ONE));
2028
- newEvaluation = currentEvaluation * univariateEval;
2029
- }
2030
-
2031
2008
  function verifyShplemini(Honk.Proof memory proof, Honk.VerificationKey memory vk, Transcript memory tp)
2032
2009
  internal
2033
2010
  view
@@ -2220,18 +2197,20 @@ abstract contract BaseHonkVerifier is IVerifier {
2220
2197
  Honk.G1Point memory P_0_agg = batchMul(commitments, scalars);
2221
2198
  Honk.G1Point memory P_1_agg = negateInplace(quotient_commitment);
2222
2199
 
2223
- // Aggregate pairing points
2224
- Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, P_0_agg, P_1_agg);
2225
- (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) =
2226
- convertPairingPointsToG1(proof.pairingPointObject);
2200
+ // Aggregate pairing points (skip if default/infinity — no recursive verification occurred)
2201
+ if (!arePairingPointsDefault(proof.pairingPointObject)) {
2202
+ Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, P_0_agg, P_1_agg);
2203
+ (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) =
2204
+ convertPairingPointsToG1(proof.pairingPointObject);
2227
2205
 
2228
- // Validate the points from the proof are on the curve
2229
- validateOnCurve(P_0_other);
2230
- validateOnCurve(P_1_other);
2206
+ // Validate the points from the proof are on the curve
2207
+ rejectPointAtInfinity(P_0_other);
2208
+ rejectPointAtInfinity(P_1_other);
2231
2209
 
2232
- // accumulate with aggregate points in proof
2233
- P_0_agg = mulWithSeperator(P_0_agg, P_0_other, recursionSeparator);
2234
- P_1_agg = mulWithSeperator(P_1_agg, P_1_other, recursionSeparator);
2210
+ // accumulate with aggregate points in proof
2211
+ P_0_agg = mulWithSeperator(P_0_agg, P_0_other, recursionSeparator);
2212
+ P_1_agg = mulWithSeperator(P_1_agg, P_1_other, recursionSeparator);
2213
+ }
2235
2214
 
2236
2215
  return pairing(P_0_agg, P_1_agg);
2237
2216
  }
@@ -2245,7 +2224,7 @@ abstract contract BaseHonkVerifier is IVerifier {
2245
2224
 
2246
2225
  // Validate all points are on the curve
2247
2226
  for (uint256 i = 0; i < limit; ++i) {
2248
- validateOnCurve(base[i]);
2227
+ rejectPointAtInfinity(base[i]);
2249
2228
  }
2250
2229
 
2251
2230
  bool success = true;
@@ -2273,8 +2252,51 @@ abstract contract BaseHonkVerifier is IVerifier {
2273
2252
  mstore(add(result, 0x20), mload(add(free, 0x20)))
2274
2253
  }
2275
2254
 
2276
- require(success, ShpleminiFailed());
2255
+ require(success, Errors.ShpleminiFailed());
2256
+ }
2257
+
2258
+ // Calculate proof size based on log_n (matching UltraKeccakFlavor formula)
2259
+ function calculateProofSize(uint256 logN) internal pure returns (uint256) {
2260
+ // Witness commitments
2261
+ uint256 proofLength = NUM_WITNESS_ENTITIES * NUM_ELEMENTS_COMM; // witness commitments
2262
+
2263
+ // Sumcheck
2264
+ proofLength += logN * BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates
2265
+ proofLength += NUMBER_OF_ENTITIES * NUM_ELEMENTS_FR; // sumcheck evaluations
2266
+
2267
+ // Gemini
2268
+ proofLength += (logN - 1) * NUM_ELEMENTS_COMM; // Gemini Fold commitments
2269
+ proofLength += logN * NUM_ELEMENTS_FR; // Gemini evaluations
2270
+
2271
+ // Shplonk and KZG commitments
2272
+ proofLength += NUM_ELEMENTS_COMM * 2; // Shplonk Q and KZG W commitments
2273
+
2274
+ // Pairing points
2275
+ proofLength += PAIRING_POINTS_SIZE; // pairing inputs carried on public inputs
2276
+
2277
+ return proofLength;
2278
+ }
2279
+
2280
+ function checkSum(Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate, Fr roundTarget)
2281
+ internal
2282
+ pure
2283
+ returns (bool checked)
2284
+ {
2285
+ Fr totalSum = roundUnivariate[0] + roundUnivariate[1];
2286
+ checked = totalSum == roundTarget;
2287
+ }
2288
+
2289
+ // Univariate evaluation of the monomial ((1-X_l) + X_l.B_l) at the challenge point X_l=u_l
2290
+ function partiallyEvaluatePOW(Fr gateChallenge, Fr currentEvaluation, Fr roundChallenge)
2291
+ internal
2292
+ pure
2293
+ returns (Fr newEvaluation)
2294
+ {
2295
+ Fr univariateEval = ONE + (roundChallenge * (gateChallenge - ONE));
2296
+ newEvaluation = currentEvaluation * univariateEval;
2277
2297
  }
2298
+
2299
+ function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory);
2278
2300
  }
2279
2301
 
2280
2302
  contract HonkVerifier is BaseHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) {