Skip to content

Commit 97b8022

Browse files
authored
feat(sol): apply shifted entities trick (#16065)
## Overview Apply gemini shifted entities trick saves 36k gas across the board
1 parent 7854235 commit 97b8022

File tree

4 files changed

+88
-74
lines changed

4 files changed

+88
-74
lines changed

barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1722,6 +1722,7 @@ abstract contract BaseHonkVerifier is IVerifier {
17221722
17231723
// Number of field elements in a ultra honk proof, including pairing point object.
17241724
uint256 constant PROOF_SIZE = 457;
1725+
uint256 constant SHIFTED_COMMITMENTS_START = 29;
17251726
17261727
function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory);
17271728
@@ -1897,8 +1898,8 @@ abstract contract BaseHonkVerifier is IVerifier {
18971898
CommitmentSchemeLib.computeSquares(tp.geminiR);
18981899
18991900
// Arrays hold values that will be linearly combined for the gemini and shplonk batch openings
1900-
Fr[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2] memory scalars;
1901-
Honk.G1Point[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2] memory commitments;
1901+
Fr[NUMBER_UNSHIFTED + CONST_PROOF_SIZE_LOG_N + 2] memory scalars;
1902+
Honk.G1Point[NUMBER_UNSHIFTED + CONST_PROOF_SIZE_LOG_N + 2] memory commitments;
19021903
19031904
mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert();
19041905
mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert();
@@ -1946,9 +1947,18 @@ abstract contract BaseHonkVerifier is IVerifier {
19461947
mem.batchingChallenge = mem.batchingChallenge * tp.rho;
19471948
}
19481949
// g commitments are accumulated at r
1949-
for (uint256 i = NUMBER_UNSHIFTED + 1; i <= NUMBER_OF_ENTITIES; ++i) {
1950-
scalars[i] = mem.shiftedScalar.neg() * mem.batchingChallenge;
1951-
mem.batchedEvaluation = mem.batchedEvaluation + (proof.sumcheckEvaluations[i - 1] * mem.batchingChallenge);
1950+
// For each of the to be shifted commitments perform the shift in place by
1951+
// adding to the unshifted value.
1952+
// We do so, as the values are to be used in batchMul later, and as
1953+
// `a * c + b * c = (a + b) * c` this will allow us to reduce memory and compute.
1954+
// Applied to w1, w2, w3, w4 and zPerm
1955+
for (uint256 i = 0; i < NUMBER_TO_BE_SHIFTED; ++i) {
1956+
uint256 scalarOff = i + SHIFTED_COMMITMENTS_START;
1957+
uint256 evaluationOff = i + NUMBER_UNSHIFTED;
1958+
1959+
scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalar.neg() * mem.batchingChallenge);
1960+
mem.batchedEvaluation =
1961+
mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge);
19521962
mem.batchingChallenge = mem.batchingChallenge * tp.rho;
19531963
}
19541964
@@ -1991,13 +2001,6 @@ abstract contract BaseHonkVerifier is IVerifier {
19912001
commitments[35] = convertProofPoint(proof.lookupReadCounts);
19922002
commitments[36] = convertProofPoint(proof.lookupReadTags);
19932003
1994-
// to be Shifted
1995-
commitments[37] = convertProofPoint(proof.w1);
1996-
commitments[38] = convertProofPoint(proof.w2);
1997-
commitments[39] = convertProofPoint(proof.w3);
1998-
commitments[40] = convertProofPoint(proof.w4);
1999-
commitments[41] = convertProofPoint(proof.zPerm);
2000-
20012004
/* Batch gemini claims from the prover
20022005
* place the commitments to gemini aᵢ to the vector of commitments, compute the contributions from
20032006
* aᵢ(−r²ⁱ) for i=1, … , n−1 to the constant term accumulator, add corresponding scalars
@@ -2051,7 +2054,7 @@ abstract contract BaseHonkVerifier is IVerifier {
20512054
mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator;
20522055
mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator;
20532056
// [Aₗ] is multiplied by -v^{2l}/(z-r^{2^l}) - v^{2l+1} /(z+ r^{2^l})
2054-
scalars[NUMBER_OF_ENTITIES + 1 + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg();
2057+
scalars[NUMBER_UNSHIFTED + 1 + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg();
20552058
20562059
// Accumulate the const term contribution given by
20572060
// v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l})
@@ -2062,17 +2065,17 @@ abstract contract BaseHonkVerifier is IVerifier {
20622065
mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu;
20632066
}
20642067
2065-
commitments[NUMBER_OF_ENTITIES + 1 + i] = convertProofPoint(proof.geminiFoldComms[i]);
2068+
commitments[NUMBER_UNSHIFTED + 1 + i] = convertProofPoint(proof.geminiFoldComms[i]);
20662069
}
20672070
20682071
// Finalise the batch opening claim
2069-
commitments[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N] = Honk.G1Point({x: 1, y: 2});
2070-
scalars[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N] = mem.constantTermAccumulator;
2072+
commitments[NUMBER_UNSHIFTED + CONST_PROOF_SIZE_LOG_N] = Honk.G1Point({x: 1, y: 2});
2073+
scalars[NUMBER_UNSHIFTED + CONST_PROOF_SIZE_LOG_N] = mem.constantTermAccumulator;
20712074
20722075
Honk.G1Point memory quotient_commitment = convertProofPoint(proof.kzgQuotient);
20732076
2074-
commitments[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 1] = quotient_commitment;
2075-
scalars[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 1] = tp.shplonkZ; // evaluation challenge
2077+
commitments[NUMBER_UNSHIFTED + CONST_PROOF_SIZE_LOG_N + 1] = quotient_commitment;
2078+
scalars[NUMBER_UNSHIFTED + CONST_PROOF_SIZE_LOG_N + 1] = tp.shplonkZ; // evaluation challenge
20762079
20772080
Honk.G1Point memory P_0_agg = batchMul(commitments, scalars);
20782081
Honk.G1Point memory P_1_agg = negateInplace(quotient_commitment);
@@ -2094,10 +2097,10 @@ abstract contract BaseHonkVerifier is IVerifier {
20942097
}
20952098
20962099
function batchMul(
2097-
Honk.G1Point[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2] memory base,
2098-
Fr[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2] memory scalars
2100+
Honk.G1Point[NUMBER_UNSHIFTED + CONST_PROOF_SIZE_LOG_N + 2] memory base,
2101+
Fr[NUMBER_UNSHIFTED + CONST_PROOF_SIZE_LOG_N + 2] memory scalars
20992102
) internal view returns (Honk.G1Point memory result) {
2100-
uint256 limit = NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2;
2103+
uint256 limit = NUMBER_UNSHIFTED + CONST_PROOF_SIZE_LOG_N + 2;
21012104
21022105
// Validate all points are on the curve
21032106
for (uint256 i = 0; i < limit; ++i) {

barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_zk_contract.hpp

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1786,6 +1786,7 @@ abstract contract BaseZKHonkVerifier is IVerifier {
17861786
17871787
// Number of field elements in a ultra honk zero knowledge proof
17881788
uint256 constant PROOF_SIZE = 508;
1789+
uint256 constant SHIFTED_COMMITMENTS_START = 30;
17891790
17901791
function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory);
17911792
@@ -1961,8 +1962,8 @@ abstract contract BaseZKHonkVerifier is IVerifier {
19611962
Fr[CONST_PROOF_SIZE_LOG_N] memory powers_of_evaluation_challenge =
19621963
CommitmentSchemeLib.computeSquares(tp.geminiR);
19631964
// Arrays hold values that will be linearly combined for the gemini and shplonk batch openings
1964-
Fr[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + LIBRA_COMMITMENTS + 3] memory scalars;
1965-
Honk.G1Point[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + LIBRA_COMMITMENTS + 3] memory commitments;
1965+
Fr[NUMBER_UNSHIFTED + CONST_PROOF_SIZE_LOG_N + LIBRA_COMMITMENTS + 3] memory scalars;
1966+
Honk.G1Point[NUMBER_UNSHIFTED + CONST_PROOF_SIZE_LOG_N + LIBRA_COMMITMENTS + 3] memory commitments;
19661967
19671968
mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert();
19681969
mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert();
@@ -2010,9 +2011,18 @@ abstract contract BaseZKHonkVerifier is IVerifier {
20102011
mem.batchingChallenge = mem.batchingChallenge * tp.rho;
20112012
}
20122013
// g commitments are accumulated at r
2013-
for (uint256 i = NUMBER_UNSHIFTED; i < NUMBER_OF_ENTITIES; ++i) {
2014-
scalars[i + 2] = mem.shiftedScalar.neg() * mem.batchingChallenge;
2015-
mem.batchedEvaluation = mem.batchedEvaluation + (proof.sumcheckEvaluations[i] * mem.batchingChallenge);
2014+
// For each of the to be shifted commitments perform the shift in place by
2015+
// adding to the unshifted value.
2016+
// We do so, as the values are to be used in batchMul later, and as
2017+
// `a * c + b * c = (a + b) * c` this will allow us to reduce memory and compute.
2018+
// Applied to w1, w2, w3, w4 and zPerm
2019+
for (uint256 i = 0; i < NUMBER_TO_BE_SHIFTED; ++i) {
2020+
uint256 scalarOff = i + SHIFTED_COMMITMENTS_START;
2021+
uint256 evaluationOff = i + NUMBER_UNSHIFTED;
2022+
2023+
scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalar.neg() * mem.batchingChallenge);
2024+
mem.batchedEvaluation =
2025+
mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge);
20162026
mem.batchingChallenge = mem.batchingChallenge * tp.rho;
20172027
}
20182028
@@ -2057,13 +2067,6 @@ abstract contract BaseZKHonkVerifier is IVerifier {
20572067
commitments[36] = convertProofPoint(proof.lookupReadCounts);
20582068
commitments[37] = convertProofPoint(proof.lookupReadTags);
20592069
2060-
// to be Shifted
2061-
commitments[38] = convertProofPoint(proof.w1);
2062-
commitments[39] = convertProofPoint(proof.w2);
2063-
commitments[40] = convertProofPoint(proof.w3);
2064-
commitments[41] = convertProofPoint(proof.w4);
2065-
commitments[42] = convertProofPoint(proof.zPerm);
2066-
20672070
/* Batch gemini claims from the prover
20682071
* place the commitments to gemini aᵢ to the vector of commitments, compute the contributions from
20692072
* aᵢ(−r²ⁱ) for i=1, … , n−1 to the constant term accumulator, add corresponding scalars
@@ -2102,7 +2105,7 @@ abstract contract BaseZKHonkVerifier is IVerifier {
21022105
mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator);
21032106
21042107
mem.batchingChallenge = tp.shplonkNu.sqr();
2105-
uint256 boundary = NUMBER_OF_ENTITIES + 2;
2108+
uint256 boundary = NUMBER_UNSHIFTED + 2;
21062109
21072110
// Compute Shplonk constant term contributions from Aₗ(± r^{2ˡ}) for l = 1, ..., m-1;
21082111
// Compute scalar multipliers for each fold commitment
@@ -2244,10 +2247,10 @@ abstract contract BaseZKHonkVerifier is IVerifier {
22442247
22452248
// This implementation is the same as above with different constants
22462249
function batchMul(
2247-
Honk.G1Point[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + LIBRA_COMMITMENTS + 3] memory base,
2248-
Fr[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + LIBRA_COMMITMENTS + 3] memory scalars
2250+
Honk.G1Point[NUMBER_UNSHIFTED + CONST_PROOF_SIZE_LOG_N + LIBRA_COMMITMENTS + 3] memory base,
2251+
Fr[NUMBER_UNSHIFTED + CONST_PROOF_SIZE_LOG_N + LIBRA_COMMITMENTS + 3] memory scalars
22492252
) internal view returns (Honk.G1Point memory result) {
2250-
uint256 limit = NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + LIBRA_COMMITMENTS + 3;
2253+
uint256 limit = NUMBER_UNSHIFTED + CONST_PROOF_SIZE_LOG_N + LIBRA_COMMITMENTS + 3;
22512254
22522255
// Validate all points are on the curve
22532256
for (uint256 i = 0; i < limit; ++i) {

barretenberg/sol/src/honk/BaseHonkVerifier.sol

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
NUMBER_OF_SUBRELATIONS,
1111
NUMBER_OF_ALPHAS,
1212
NUMBER_UNSHIFTED,
13+
NUMBER_TO_BE_SHIFTED,
1314
BATCHED_RELATION_PARTIAL_LENGTH,
1415
CONST_PROOF_SIZE_LOG_N,
1516
PAIRING_POINTS_SIZE
@@ -48,6 +49,7 @@ abstract contract BaseHonkVerifier is IVerifier {
4849

4950
// Number of field elements in a ultra honk proof, including pairing point object.
5051
uint256 constant PROOF_SIZE = 457;
52+
uint256 constant SHIFTED_COMMITMENTS_START = 29;
5153

5254
function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory);
5355

@@ -229,8 +231,8 @@ abstract contract BaseHonkVerifier is IVerifier {
229231
CommitmentSchemeLib.computeSquares(tp.geminiR);
230232

231233
// Arrays hold values that will be linearly combined for the gemini and shplonk batch openings
232-
Fr[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2] memory scalars;
233-
Honk.G1Point[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2] memory commitments;
234+
Fr[NUMBER_UNSHIFTED + CONST_PROOF_SIZE_LOG_N + 2] memory scalars;
235+
Honk.G1Point[NUMBER_UNSHIFTED + CONST_PROOF_SIZE_LOG_N + 2] memory commitments;
234236

235237
mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert();
236238
mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert();
@@ -278,9 +280,18 @@ abstract contract BaseHonkVerifier is IVerifier {
278280
mem.batchingChallenge = mem.batchingChallenge * tp.rho;
279281
}
280282
// g commitments are accumulated at r
281-
for (uint256 i = NUMBER_UNSHIFTED + 1; i <= NUMBER_OF_ENTITIES; ++i) {
282-
scalars[i] = mem.shiftedScalar.neg() * mem.batchingChallenge;
283-
mem.batchedEvaluation = mem.batchedEvaluation + (proof.sumcheckEvaluations[i - 1] * mem.batchingChallenge);
283+
// For each of the to be shifted commitments perform the shift in place by
284+
// adding to the unshifted value.
285+
// We do so, as the values are to be used in batchMul later, and as
286+
// `a * c + b * c = (a + b) * c` this will allow us to reduce memory and compute.
287+
// Applied to w1, w2, w3, w4 and zPerm
288+
for (uint256 i = 0; i < NUMBER_TO_BE_SHIFTED; ++i) {
289+
uint256 scalarOff = i + SHIFTED_COMMITMENTS_START;
290+
uint256 evaluationOff = i + NUMBER_UNSHIFTED;
291+
292+
scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalar.neg() * mem.batchingChallenge);
293+
mem.batchedEvaluation =
294+
mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge);
284295
mem.batchingChallenge = mem.batchingChallenge * tp.rho;
285296
}
286297

@@ -323,13 +334,6 @@ abstract contract BaseHonkVerifier is IVerifier {
323334
commitments[35] = convertProofPoint(proof.lookupReadCounts);
324335
commitments[36] = convertProofPoint(proof.lookupReadTags);
325336

326-
// to be Shifted
327-
commitments[37] = convertProofPoint(proof.w1);
328-
commitments[38] = convertProofPoint(proof.w2);
329-
commitments[39] = convertProofPoint(proof.w3);
330-
commitments[40] = convertProofPoint(proof.w4);
331-
commitments[41] = convertProofPoint(proof.zPerm);
332-
333337
/* Batch gemini claims from the prover
334338
* place the commitments to gemini aᵢ to the vector of commitments, compute the contributions from
335339
* aᵢ(−r²ⁱ) for i=1, … , n−1 to the constant term accumulator, add corresponding scalars
@@ -383,7 +387,7 @@ abstract contract BaseHonkVerifier is IVerifier {
383387
mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator;
384388
mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator;
385389
// [Aₗ] is multiplied by -v^{2l}/(z-r^{2^l}) - v^{2l+1} /(z+ r^{2^l})
386-
scalars[NUMBER_OF_ENTITIES + 1 + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg();
390+
scalars[NUMBER_UNSHIFTED + 1 + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg();
387391

388392
// Accumulate the const term contribution given by
389393
// v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l})
@@ -394,17 +398,17 @@ abstract contract BaseHonkVerifier is IVerifier {
394398
mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu;
395399
}
396400

397-
commitments[NUMBER_OF_ENTITIES + 1 + i] = convertProofPoint(proof.geminiFoldComms[i]);
401+
commitments[NUMBER_UNSHIFTED + 1 + i] = convertProofPoint(proof.geminiFoldComms[i]);
398402
}
399403

400404
// Finalise the batch opening claim
401-
commitments[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N] = Honk.G1Point({x: 1, y: 2});
402-
scalars[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N] = mem.constantTermAccumulator;
405+
commitments[NUMBER_UNSHIFTED + CONST_PROOF_SIZE_LOG_N] = Honk.G1Point({x: 1, y: 2});
406+
scalars[NUMBER_UNSHIFTED + CONST_PROOF_SIZE_LOG_N] = mem.constantTermAccumulator;
403407

404408
Honk.G1Point memory quotient_commitment = convertProofPoint(proof.kzgQuotient);
405409

406-
commitments[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 1] = quotient_commitment;
407-
scalars[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 1] = tp.shplonkZ; // evaluation challenge
410+
commitments[NUMBER_UNSHIFTED + CONST_PROOF_SIZE_LOG_N + 1] = quotient_commitment;
411+
scalars[NUMBER_UNSHIFTED + CONST_PROOF_SIZE_LOG_N + 1] = tp.shplonkZ; // evaluation challenge
408412

409413
Honk.G1Point memory P_0_agg = batchMul(commitments, scalars);
410414
Honk.G1Point memory P_1_agg = negateInplace(quotient_commitment);
@@ -426,10 +430,10 @@ abstract contract BaseHonkVerifier is IVerifier {
426430
}
427431

428432
function batchMul(
429-
Honk.G1Point[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2] memory base,
430-
Fr[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2] memory scalars
433+
Honk.G1Point[NUMBER_UNSHIFTED + CONST_PROOF_SIZE_LOG_N + 2] memory base,
434+
Fr[NUMBER_UNSHIFTED + CONST_PROOF_SIZE_LOG_N + 2] memory scalars
431435
) internal view returns (Honk.G1Point memory result) {
432-
uint256 limit = NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2;
436+
uint256 limit = NUMBER_UNSHIFTED + CONST_PROOF_SIZE_LOG_N + 2;
433437

434438
// Validate all points are on the curve
435439
for (uint256 i = 0; i < limit; ++i) {

0 commit comments

Comments
 (0)