From f6b126c8ef06540bd75aa841a121df54122f8ee3 Mon Sep 17 00:00:00 2001 From: Maddiaa <47148561+Maddiaa0@users.noreply.github.com> Date: Tue, 29 Oct 2024 07:35:01 +0000 Subject: [PATCH] Revert "feat: sol shplemini in acir tests + contract_gen (#8874)" This reverts commit 1c0275db18510fd7d55b400e4a910447859f4acc. --- barretenberg/acir_tests/sol-test/package.json | 2 +- barretenberg/acir_tests/sol-test/src/index.js | 4 +- barretenberg/acir_tests/sol-test/yarn.lock | 8 +- barretenberg/cpp/src/barretenberg/bb/main.cpp | 7 + .../dsl/acir_proofs/honk_contract.hpp | 824 ++++-------------- barretenberg/sol/src/honk/HonkTypes.sol | 2 +- barretenberg/sol/src/honk/HonkVerifier.sol | 327 +++++++ barretenberg/sol/src/honk/Relations.sol | 12 +- barretenberg/sol/src/honk/Transcript.sol | 2 +- .../sol/src/honk/instance/Add2Honk.sol | 4 +- .../sol/src/honk/instance/BlakeHonk.sol | 4 +- .../sol/src/honk/instance/EcdsaHonk.sol | 4 +- yarn-project/cli/package.json | 2 +- yarn-project/end-to-end/package.json | 2 +- .../ethereum/src/deploy_l1_contracts.ts | 2 +- yarn-project/yarn.lock | 29 +- 16 files changed, 559 insertions(+), 676 deletions(-) create mode 100644 barretenberg/sol/src/honk/HonkVerifier.sol diff --git a/barretenberg/acir_tests/sol-test/package.json b/barretenberg/acir_tests/sol-test/package.json index a13cd369c14..f346b9e42f6 100644 --- a/barretenberg/acir_tests/sol-test/package.json +++ b/barretenberg/acir_tests/sol-test/package.json @@ -9,6 +9,6 @@ }, "dependencies": { "ethers": "^6.8.1", - "solc": "^0.8.27" + "solc": "^0.8.22" } } diff --git a/barretenberg/acir_tests/sol-test/src/index.js b/barretenberg/acir_tests/sol-test/src/index.js index 42749f88d96..25a25484ba6 100644 --- a/barretenberg/acir_tests/sol-test/src/index.js +++ b/barretenberg/acir_tests/sol-test/src/index.js @@ -5,7 +5,9 @@ import { ethers } from "ethers"; import solc from "solc"; const NUMBER_OF_FIELDS_IN_PLONK_PROOF = 93; -const NUMBER_OF_FIELDS_IN_HONK_PROOF = 447; +// TODO(https://github.com/AztecProtocol/barretenberg/issues/1093): This is the size of the proof up to Sumcheck, without public inputs, as the Honk contract does not currently have a PCS. +// This needs to be changed once Shplemini is implemented in the smart contract. +const NUMBER_OF_FIELDS_IN_HONK_PROOF = 303; // We use the solcjs compiler version in this test, although it is slower than foundry, to run the test end to end // it simplifies of parallelising the test suite diff --git a/barretenberg/acir_tests/sol-test/yarn.lock b/barretenberg/acir_tests/sol-test/yarn.lock index c0a90997654..af80282ea95 100644 --- a/barretenberg/acir_tests/sol-test/yarn.lock +++ b/barretenberg/acir_tests/sol-test/yarn.lock @@ -77,10 +77,10 @@ semver@^5.5.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -solc@^0.8.27: - version "0.8.27" - resolved "https://registry.yarnpkg.com/solc/-/solc-0.8.27.tgz#cb8e7246cceadad8df65ceccffe640e106106bb4" - integrity sha512-BNxMol2tUAbkH7HKlXBcBqrGi2aqgv+uMHz26mJyTtlVgWmBA4ktiw0qVKHfkjf2oaHbwtbtaSeE2dhn/gTAKw== +solc@^0.8.22: + version "0.8.22" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.8.22.tgz#6df0bb688b9a58bbf10932730301374a6ccfb862" + integrity sha512-bA2tMZXx93R8L5LUH7TlB/f+QhkVyxrrY6LmgJnFFZlRknrhYVlBK1e3uHIdKybwoFabOFSzeaZjPeL/GIpFGQ== dependencies: command-exists "^1.2.8" commander "^8.1.0" diff --git a/barretenberg/cpp/src/barretenberg/bb/main.cpp b/barretenberg/cpp/src/barretenberg/bb/main.cpp index 052ea3774e6..07fabf7029d 100644 --- a/barretenberg/cpp/src/barretenberg/bb/main.cpp +++ b/barretenberg/cpp/src/barretenberg/bb/main.cpp @@ -1110,6 +1110,13 @@ void prove_honk(const std::string& bytecodePath, const std::string& witnessPath, // Construct Honk proof Prover prover = compute_valid_prover(bytecodePath, witnessPath); auto proof = prover.construct_proof(); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1093): As the Smart contract doesn't verify the PCS and + // Shplemini is not constant size, we slice the proof up to sumcheck so calculation of public inputs is correct. + // This hack will be subsequently removed. + if constexpr (std::same_as) { + auto num_public_inputs = static_cast(prover.proving_key->proving_key.num_public_inputs); + proof.erase(proof.begin() + num_public_inputs + 303, proof.end()); + } if (outputPath == "-") { writeRawBytesToStdout(to_buffer(proof)); vinfo("proof written to stdout"); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp index 95e37c1fbeb..7a451cd81f2 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp @@ -5,8 +5,6 @@ // Source code for the Ultrahonk Solidity verifier. // It's expected that the AcirComposer will inject a library which will load the verification key into memory. const std::string HONK_CONTRACT_SOURCE = R"( -pragma solidity ^0.8.27; - type Fr is uint256; using { add as + } for Fr global; @@ -93,18 +91,6 @@ library FrLib { return numerator * invert(denominator); } - - function sqr(Fr value) internal pure returns (Fr) { - return value * value; - } - - function unwrap(Fr value) internal pure returns (uint256) { - return Fr.unwrap(value); - } - - function neg(Fr value) internal pure returns (Fr) { - return Fr.wrap(MODULUS - Fr.unwrap(value)); - } } // Free functions @@ -125,8 +111,9 @@ function sub(Fr a, Fr b) pure returns(Fr) function exp(Fr base, Fr exponent) pure returns(Fr) { - if (Fr.unwrap(exponent) == 0) return Fr.wrap(1); - + if (Fr.unwrap(exponent) == 0) + return Fr.wrap(1); + // Implement exponent with a loop as we will overflow otherwise for (uint256 i = 1; i < Fr.unwrap(exponent); i += i) { base = base * base; } @@ -276,11 +263,6 @@ library Honk { // Sumcheck Fr[BATCHED_RELATION_PARTIAL_LENGTH][CONST_PROOF_SIZE_LOG_N] sumcheckUnivariates; Fr[NUMBER_OF_ENTITIES] sumcheckEvaluations; - // Shplemini - Honk.G1ProofPoint[CONST_PROOF_SIZE_LOG_N - 1] geminiFoldComms; - Fr[CONST_PROOF_SIZE_LOG_N] geminiAEvaluations; - Honk.G1ProofPoint shplonkQ; - Honk.G1ProofPoint kzgQuotient; } } @@ -295,19 +277,15 @@ struct Transcript { Fr[NUMBER_OF_ALPHAS] alphas; Fr[CONST_PROOF_SIZE_LOG_N] gateChallenges; Fr[CONST_PROOF_SIZE_LOG_N] sumCheckUChallenges; - // Shplemini - Fr rho; - Fr geminiR; - Fr shplonkNu; - Fr shplonkZ; // Derived Fr publicInputsDelta; + Fr lookupGrandProductDelta; } library TranscriptLib { function generateTranscript(Honk.Proof memory proof, bytes32[] calldata publicInputs, uint256 publicInputsSize) internal - pure + view returns (Transcript memory t) { Fr previousChallenge; @@ -321,14 +299,6 @@ library TranscriptLib { (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge); - (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); - - (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge); - - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge); - - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); - return t; } @@ -342,31 +312,31 @@ library TranscriptLib { function generateEtaChallenge(Honk.Proof memory proof, bytes32[] calldata publicInputs, uint256 publicInputsSize) internal - pure + view returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { - bytes32[] memory round0 = new bytes32[](3 + publicInputsSize + 12); + bytes32[] memory round0 = new bytes32[](3 + NUMBER_OF_PUBLIC_INPUTS + 12); round0[0] = bytes32(proof.circuitSize); round0[1] = bytes32(proof.publicInputsSize); round0[2] = bytes32(proof.publicInputsOffset); - for (uint256 i = 0; i < publicInputsSize; i++) { + for (uint256 i = 0; i < NUMBER_OF_PUBLIC_INPUTS; i++) { round0[3 + i] = bytes32(publicInputs[i]); } // Create the first challenge // Note: w4 is added to the challenge later on - round0[3 + publicInputsSize] = bytes32(proof.w1.x_0); - round0[3 + publicInputsSize + 1] = bytes32(proof.w1.x_1); - round0[3 + publicInputsSize + 2] = bytes32(proof.w1.y_0); - round0[3 + publicInputsSize + 3] = bytes32(proof.w1.y_1); - round0[3 + publicInputsSize + 4] = bytes32(proof.w2.x_0); - round0[3 + publicInputsSize + 5] = bytes32(proof.w2.x_1); - round0[3 + publicInputsSize + 6] = bytes32(proof.w2.y_0); - round0[3 + publicInputsSize + 7] = bytes32(proof.w2.y_1); - round0[3 + publicInputsSize + 8] = bytes32(proof.w3.x_0); - round0[3 + publicInputsSize + 9] = bytes32(proof.w3.x_1); - round0[3 + publicInputsSize + 10] = bytes32(proof.w3.y_0); - round0[3 + publicInputsSize + 11] = bytes32(proof.w3.y_1); + round0[3 + NUMBER_OF_PUBLIC_INPUTS] = bytes32(proof.w1.x_0); + round0[3 + NUMBER_OF_PUBLIC_INPUTS + 1] = bytes32(proof.w1.x_1); + round0[3 + NUMBER_OF_PUBLIC_INPUTS + 2] = bytes32(proof.w1.y_0); + round0[3 + NUMBER_OF_PUBLIC_INPUTS + 3] = bytes32(proof.w1.y_1); + round0[3 + NUMBER_OF_PUBLIC_INPUTS + 4] = bytes32(proof.w2.x_0); + round0[3 + NUMBER_OF_PUBLIC_INPUTS + 5] = bytes32(proof.w2.x_1); + round0[3 + NUMBER_OF_PUBLIC_INPUTS + 6] = bytes32(proof.w2.y_0); + round0[3 + NUMBER_OF_PUBLIC_INPUTS + 7] = bytes32(proof.w2.y_1); + round0[3 + NUMBER_OF_PUBLIC_INPUTS + 8] = bytes32(proof.w3.x_0); + round0[3 + NUMBER_OF_PUBLIC_INPUTS + 9] = bytes32(proof.w3.x_1); + round0[3 + NUMBER_OF_PUBLIC_INPUTS + 10] = bytes32(proof.w3.y_0); + round0[3 + NUMBER_OF_PUBLIC_INPUTS + 11] = bytes32(proof.w3.y_1); previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); (eta, etaTwo) = splitChallenge(previousChallenge); @@ -375,10 +345,7 @@ library TranscriptLib { (etaThree, unused) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.Proof memory proof) - internal - pure - returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) + function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.Proof memory proof) internal view returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { bytes32[13] memory round1; round1[0] = FrLib.toBytes32(previousChallenge); @@ -400,10 +367,7 @@ library TranscriptLib { } // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges(Fr previousChallenge, Honk.Proof memory proof) - internal - pure - returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) + function generateAlphaChallenges(Fr previousChallenge, Honk.Proof memory proof) internal view returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) { // Generate the original sumcheck alpha 0 by hashing zPerm and zLookup uint256[9] memory alpha0; @@ -431,10 +395,7 @@ library TranscriptLib { } } - function generateGateChallenges(Fr previousChallenge) - internal - pure - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) + function generateGateChallenges(Fr previousChallenge) internal view returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) { for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); @@ -444,15 +405,13 @@ library TranscriptLib { nextPreviousChallenge = previousChallenge; } - function generateSumcheckChallenges(Honk.Proof memory proof, Fr prevChallenge) - internal - pure - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) + function generateSumcheckChallenges(Honk.Proof memory proof, Fr prevChallenge) internal view returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) { for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { Fr[BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; univariateChal[0] = prevChallenge; + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1098): memcpy for (uint256 j = 0; j < BATCHED_RELATION_PARTIAL_LENGTH; j++) { univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; } @@ -462,218 +421,6 @@ library TranscriptLib { } nextPreviousChallenge = prevChallenge; } - - function generateRhoChallenge(Honk.Proof memory proof, Fr prevChallenge) - internal - pure - returns (Fr rho, Fr nextPreviousChallenge) - { - Fr[NUMBER_OF_ENTITIES + 1] memory rhoChallengeElements; - rhoChallengeElements[0] = prevChallenge; - - for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - rhoChallengeElements[i + 1] = proof.sumcheckEvaluations[i]; - } - - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); - Fr unused; - (rho, unused) = splitChallenge(nextPreviousChallenge); - } - - function generateGeminiRChallenge(Honk.Proof memory proof, Fr prevChallenge) - internal - pure - returns (Fr geminiR, Fr nextPreviousChallenge) - { - uint256[(CONST_PROOF_SIZE_LOG_N - 1) * 4 + 1] memory gR; - gR[0] = Fr.unwrap(prevChallenge); - - for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N - 1; i++) { - gR[1 + i * 4] = proof.geminiFoldComms[i].x_0; - gR[2 + i * 4] = proof.geminiFoldComms[i].x_1; - gR[3 + i * 4] = proof.geminiFoldComms[i].y_0; - gR[4 + i * 4] = proof.geminiFoldComms[i].y_1; - } - - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); - Fr unused; - (geminiR, unused) = splitChallenge(nextPreviousChallenge); - } - - function generateShplonkNuChallenge(Honk.Proof memory proof, Fr prevChallenge) - internal - pure - returns (Fr shplonkNu, Fr nextPreviousChallenge) - { - uint256[(CONST_PROOF_SIZE_LOG_N) + 1] memory shplonkNuChallengeElements; - shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); - - for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { - shplonkNuChallengeElements[i + 1] = Fr.unwrap(proof.geminiAEvaluations[i]); - } - - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); - Fr unused; - (shplonkNu, unused) = splitChallenge(nextPreviousChallenge); - } - - function generateShplonkZChallenge(Honk.Proof memory proof, Fr prevChallenge) - internal - pure - returns (Fr shplonkZ, Fr nextPreviousChallenge) - { - uint256[5] memory shplonkZChallengeElements; - shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); - - shplonkZChallengeElements[1] = proof.shplonkQ.x_0; - shplonkZChallengeElements[2] = proof.shplonkQ.x_1; - shplonkZChallengeElements[3] = proof.shplonkQ.y_0; - shplonkZChallengeElements[4] = proof.shplonkQ.y_1; - - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); - Fr unused; - (shplonkZ, unused) = splitChallenge(nextPreviousChallenge); - } - - function loadProof(bytes calldata proof) internal pure returns (Honk.Proof memory) { - Honk.Proof memory p; - - // Metadata - p.circuitSize = uint256(bytes32(proof[0x00:0x20])); - p.publicInputsSize = uint256(bytes32(proof[0x20:0x40])); - p.publicInputsOffset = uint256(bytes32(proof[0x40:0x60])); - - // Commitments - p.w1 = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[0x60:0x80])), - x_1: uint256(bytes32(proof[0x80:0xa0])), - y_0: uint256(bytes32(proof[0xa0:0xc0])), - y_1: uint256(bytes32(proof[0xc0:0xe0])) - }); - - p.w2 = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[0xe0:0x100])), - x_1: uint256(bytes32(proof[0x100:0x120])), - y_0: uint256(bytes32(proof[0x120:0x140])), - y_1: uint256(bytes32(proof[0x140:0x160])) - }); - p.w3 = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[0x160:0x180])), - x_1: uint256(bytes32(proof[0x180:0x1a0])), - y_0: uint256(bytes32(proof[0x1a0:0x1c0])), - y_1: uint256(bytes32(proof[0x1c0:0x1e0])) - }); - - // Lookup / Permutation Helper Commitments - p.lookupReadCounts = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[0x1e0:0x200])), - x_1: uint256(bytes32(proof[0x200:0x220])), - y_0: uint256(bytes32(proof[0x220:0x240])), - y_1: uint256(bytes32(proof[0x240:0x260])) - }); - p.lookupReadTags = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[0x260:0x280])), - x_1: uint256(bytes32(proof[0x280:0x2a0])), - y_0: uint256(bytes32(proof[0x2a0:0x2c0])), - y_1: uint256(bytes32(proof[0x2c0:0x2e0])) - }); - p.w4 = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[0x2e0:0x300])), - x_1: uint256(bytes32(proof[0x300:0x320])), - y_0: uint256(bytes32(proof[0x320:0x340])), - y_1: uint256(bytes32(proof[0x340:0x360])) - }); - p.lookupInverses = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[0x360:0x380])), - x_1: uint256(bytes32(proof[0x380:0x3a0])), - y_0: uint256(bytes32(proof[0x3a0:0x3c0])), - y_1: uint256(bytes32(proof[0x3c0:0x3e0])) - }); - p.zPerm = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[0x3e0:0x400])), - x_1: uint256(bytes32(proof[0x400:0x420])), - y_0: uint256(bytes32(proof[0x420:0x440])), - y_1: uint256(bytes32(proof[0x440:0x460])) - }); - - // Boundary represents a pointer to the head of the unread part of the proof - uint256 boundary = 0x460; - - // Sumcheck univariates - for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { - // The loop boundary of i, this will shift forward on each evaluation - uint256 loop_boundary = boundary + (i * 0x20 * BATCHED_RELATION_PARTIAL_LENGTH); - - for (uint256 j = 0; j < BATCHED_RELATION_PARTIAL_LENGTH; j++) { - uint256 start = loop_boundary + (j * 0x20); - uint256 end = start + 0x20; - p.sumcheckUnivariates[i][j] = FrLib.fromBytes32(bytes32(proof[start:end])); - } - } - - boundary = boundary + (CONST_PROOF_SIZE_LOG_N * BATCHED_RELATION_PARTIAL_LENGTH * 0x20); - // Sumcheck evaluations - for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - uint256 start = boundary + (i * 0x20); - uint256 end = start + 0x20; - p.sumcheckEvaluations[i] = FrLib.fromBytes32(bytes32(proof[start:end])); - } - - boundary = boundary + (NUMBER_OF_ENTITIES * 0x20); - - // Gemini - // Read gemini fold univariates - for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N - 1; i++) { - uint256 xStart = boundary + (i * 0x80); - uint256 xEnd = xStart + 0x20; - - uint256 x1Start = xEnd; - uint256 x1End = x1Start + 0x20; - - uint256 yStart = x1End; - uint256 yEnd = yStart + 0x20; - - uint256 y1Start = yEnd; - uint256 y1End = y1Start + 0x20; - p.geminiFoldComms[i] = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[xStart:xEnd])), - x_1: uint256(bytes32(proof[x1Start:x1End])), - y_0: uint256(bytes32(proof[yStart:yEnd])), - y_1: uint256(bytes32(proof[y1Start:y1End])) - }); - } - - boundary = boundary + ((CONST_PROOF_SIZE_LOG_N - 1) * 0x80); - - // Read gemini a evaluations - for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { - uint256 start = boundary + (i * 0x20); - uint256 end = start + 0x20; - p.geminiAEvaluations[i] = FrLib.fromBytes32(bytes32(proof[start:end])); - } - - boundary = boundary + (CONST_PROOF_SIZE_LOG_N * 0x20); - - // Shplonk - p.shplonkQ = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[boundary:boundary + 0x20])), - x_1: uint256(bytes32(proof[boundary + 0x20:boundary + 0x40])), - y_0: uint256(bytes32(proof[boundary + 0x40:boundary + 0x60])), - y_1: uint256(bytes32(proof[boundary + 0x60:boundary + 0x80])) - }); - - boundary = boundary + 0x80; - - // KZG - p.kzgQuotient = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[boundary:boundary + 0x20])), - x_1: uint256(bytes32(proof[boundary + 0x20:boundary + 0x40])), - y_0: uint256(bytes32(proof[boundary + 0x40:boundary + 0x60])), - y_1: uint256(bytes32(proof[boundary + 0x60:boundary + 0x80])) - }); - - return p; - } } // EC Point utilities @@ -721,7 +468,7 @@ library RelationsLib { function accumulateRelationEvaluations(Honk.Proof memory proof, Transcript memory tp, Fr powPartialEval) internal - pure + view returns (Fr accumulator) { Fr[NUMBER_OF_ENTITIES] memory purportedEvaluations = proof.sumcheckEvaluations; @@ -734,22 +481,24 @@ library RelationsLib { accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); accumulateAuxillaryRelation(purportedEvaluations, tp, evaluations, powPartialEval); - accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonExternalRelation(purportedEvaluations, tp, evaluations, powPartialEval); + accumulatePoseidonInternalRelation(purportedEvaluations, tp, evaluations, powPartialEval); // batch the subrelations with the alpha challenges to obtain the full honk relation accumulator = scaleAndBatchSubrelations(evaluations, tp.alphas); } /** - * Aesthetic helper function that is used to index by enum into proof.sumcheckEvaluations, it avoids + * WIRE + * + * Wire is an aesthetic helper function that is used to index by enum into proof.sumcheckEvaluations, it avoids * the relation checking code being cluttered with uint256 type casting, which is often a different colour in code * editors, and thus is noisy. */ - function wire(Fr[NUMBER_OF_ENTITIES] memory p, WIRE _wire) internal pure returns (Fr) { + function wire(Fr[NUMBER_OF_ENTITIES] memory p, WIRE _wire) internal pure returns(Fr) + { return p[uint256(_wire)]; } - uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** * Ultra Arithmetic Relation * @@ -758,15 +507,16 @@ library RelationsLib { Fr[NUMBER_OF_ENTITIES] memory p, Fr[NUMBER_OF_SUBRELATIONS] memory evals, Fr domainSep - ) internal pure { + ) internal view { // Relation 0 Fr q_arith = wire(p, WIRE.Q_ARITH); { - Fr neg_half = Fr.wrap(NEG_HALF_MODULO_P); + Fr neg_half = Fr.wrap(0) - (FrLib.invert(Fr.wrap(2))); Fr accum = (q_arith - Fr.wrap(3)) * (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * neg_half; - accum = accum + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) - + (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); + accum = accum + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (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); accum = accum + (q_arith - Fr.wrap(1)) * wire(p, WIRE.W_4_SHIFT); accum = accum * q_arith; accum = accum * domainSep; @@ -831,11 +581,9 @@ library RelationsLib { } function accumulateLogDerivativeLookupRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Transcript memory tp, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { + Fr[NUMBER_OF_ENTITIES] memory p, Transcript memory tp, Fr[NUMBER_OF_SUBRELATIONS] memory evals, Fr domainSep) + internal view + { Fr write_term; Fr read_term; @@ -876,7 +624,7 @@ library RelationsLib { Fr[NUMBER_OF_ENTITIES] memory p, Fr[NUMBER_OF_SUBRELATIONS] memory evals, Fr domainSep - ) internal pure { + ) internal view { Fr minus_one = Fr.wrap(0) - Fr.wrap(1); Fr minus_two = Fr.wrap(0) - Fr.wrap(2); Fr minus_three = Fr.wrap(0) - Fr.wrap(3); @@ -944,11 +692,10 @@ library RelationsLib { Fr x_double_identity; } - function accumulateEllipticRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { + function + accumulateEllipticRelation(Fr[NUMBER_OF_ENTITIES] memory p, Fr[NUMBER_OF_SUBRELATIONS] memory evals, Fr domainSep) + internal view + { EllipticParams memory ep; ep.x_1 = wire(p, WIRE.W_R); ep.y_1 = wire(p, WIRE.W_O); @@ -1043,12 +790,11 @@ library RelationsLib { Fr auxiliary_identity; } - function accumulateAuxillaryRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Transcript memory tp, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { + function + accumulateAuxillaryRelation( + Fr[NUMBER_OF_ENTITIES] memory p, Transcript memory tp, Fr[NUMBER_OF_SUBRELATIONS] memory evals, Fr domainSep) + internal pure + { AuxParams memory ap; /** @@ -1184,14 +930,16 @@ library RelationsLib { ap.index_is_monotonically_increasing = ap.index_delta * ap.index_delta - ap.index_delta; // deg 2 - ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + Fr.wrap(1)) * ap.record_delta; // deg 2 + ap.adjacent_values_match_if_adjacent_indices_match = + (ap.index_delta * MINUS_ONE + Fr.wrap(1)) * ap.record_delta; // deg 2 - evals[13] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) - * (wire(p, WIRE.Q_AUX) * domainSep); // deg 5 - evals[14] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) - * (wire(p, WIRE.Q_AUX) * domainSep); // deg 5 + evals[13] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * + (wire(p, WIRE.Q_AUX) * domainSep); // deg 5 + evals[14] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * + (wire(p, WIRE.Q_AUX) * domainSep); // deg 5 - ap.ROM_consistency_check_identity = ap.memory_record_check * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)); // deg 3 or 7 + ap.ROM_consistency_check_identity = + ap.memory_record_check * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)); // deg 3 or 7 /** * Contributions 15,16,17 @@ -1213,17 +961,18 @@ library RelationsLib { * with a WRITE operation. */ Fr access_type = (wire(p, WIRE.W_4) - ap.partial_record_check); // will be 0 or 1 for honest Prover; deg 1 or 4 - ap.access_check = access_type * access_type - access_type; // check value is 0 or 1; deg 2 or 8 + ap.access_check = access_type * access_type - access_type; // check value is 0 or 1; deg 2 or 8 + // deg 1 or 4 ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * tp.etaThree; ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * tp.etaTwo); ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * tp.eta); ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = ( - ap.index_delta * MINUS_ONE + Fr.wrap(1) - ) * value_delta * (ap.next_gate_access_type * MINUS_ONE + Fr.wrap(1)); // deg 3 or 6 + ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + Fr.wrap(1)) * value_delta * + (ap.next_gate_access_type * MINUS_ONE + Fr.wrap(1)); // deg 3 or 6 // We can't apply the RAM consistency check identity on the final entry in the sorted list (the wires in the // next gate would make the identity fail). We need to validate that its 'access type' bool is correct. Can't @@ -1234,10 +983,12 @@ library RelationsLib { ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; // Putting it all together... - evals[15] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation - * (wire(p, WIRE.Q_ARITH)) * (wire(p, WIRE.Q_AUX) * domainSep); // deg 5 or 8 - evals[16] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_ARITH)) * (wire(p, WIRE.Q_AUX) * domainSep); // deg 4 - evals[17] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_ARITH)) * (wire(p, WIRE.Q_AUX) * domainSep); // deg 4 or 6 + evals[15] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * + (wire(p, WIRE.Q_ARITH)) * (wire(p, WIRE.Q_AUX) * domainSep); // deg 5 or 8 + evals[16] = + ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_ARITH)) * (wire(p, WIRE.Q_AUX) * domainSep); // deg 4 + evals[17] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_ARITH)) * + (wire(p, WIRE.Q_AUX) * domainSep); // deg 4 or 6 ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_ARITH)); // deg 3 or 9 @@ -1264,8 +1015,9 @@ library RelationsLib { ap.memory_identity = ap.ROM_consistency_check_identity; // deg 3 or 6 ap.memory_identity = ap.memory_identity + ap.RAM_timestamp_check_identity * (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 - ap.memory_identity = ap.memory_identity + ap.memory_record_check * (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 - ap.memory_identity = ap.memory_identity + ap.RAM_consistency_check_identity; // deg 3 or 9 + ap.memory_identity = + ap.memory_identity + ap.memory_record_check * (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = ap.memory_identity + ap.RAM_consistency_check_identity; // deg 3 or 9 // (deg 3 or 9) + (deg 4) + (deg 3) ap.auxiliary_identity = ap.memory_identity + non_native_field_identity + limb_accumulator_identity; @@ -1295,8 +1047,9 @@ library RelationsLib { function accumulatePoseidonExternalRelation( Fr[NUMBER_OF_ENTITIES] memory p, + Transcript memory tp, // I think this is not needed Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep + Fr domainSep // i guess this is the scaling factor? ) internal pure { PoseidonExternalParams memory ep; @@ -1351,11 +1104,11 @@ library RelationsLib { function accumulatePoseidonInternalRelation( Fr[NUMBER_OF_ENTITIES] memory p, + Transcript memory tp, // I think this is not needed Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep + Fr domainSep // i guess this is the scaling factor? ) internal pure { PoseidonInternalParams memory ip; - Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), @@ -1405,7 +1158,6 @@ library RelationsLib { // Errors error PublicInputsLengthWrong(); error SumcheckFailed(); -error ShpleminiFailed(); interface IVerifier { function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool); @@ -1414,11 +1166,10 @@ interface IVerifier { // Smart contract verifier of honk proofs contract HonkVerifier is IVerifier { - using FrLib for Fr; function verify(bytes calldata proof, bytes32[] calldata publicInputs) public view override returns (bool) { Honk.VerificationKey memory vk = loadVerificationKey(); - Honk.Proof memory p = TranscriptLib.loadProof(proof); + Honk.Proof memory p = loadProof(proof); if (publicInputs.length != vk.publicInputsSize) { revert PublicInputsLengthWrong(); @@ -1430,21 +1181,110 @@ contract HonkVerifier is IVerifier // Compute the public input delta t.publicInputsDelta = computePublicInputDelta(publicInputs, t.beta, t.gamma, vk.circuitSize, p.publicInputsOffset); - // Sumcheck bool sumcheckVerified = verifySumcheck(p, t); if (!sumcheckVerified) revert SumcheckFailed(); - bool shpleminiVerified = verifyShplemini(p, vk, t); - if (!shpleminiVerified) revert ShpleminiFailed(); - - return sumcheckVerified && shpleminiVerified; // Boolean condition not required - nice for vanity :) + return sumcheckVerified; // Boolean condition not required - nice for vanity :) } - function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { + function loadVerificationKey() internal view returns (Honk.VerificationKey memory) { return HonkVerificationKey.loadVerificationKey(); } + // TODO: mod q proof points + // TODO: Preprocess all of the memory locations + // TODO: Adjust proof point serde away from poseidon forced field elements + function loadProof(bytes calldata proof) internal view returns (Honk.Proof memory) { + Honk.Proof memory p; + + // Metadata + p.circuitSize = uint256(bytes32(proof[0x00:0x20])); + p.publicInputsSize = uint256(bytes32(proof[0x20:0x40])); + p.publicInputsOffset = uint256(bytes32(proof[0x40:0x60])); + + // Commitments + p.w1 = Honk.G1ProofPoint({ + x_0: uint256(bytes32(proof[0x60:0x80])), + x_1: uint256(bytes32(proof[0x80:0xa0])), + y_0: uint256(bytes32(proof[0xa0:0xc0])), + y_1: uint256(bytes32(proof[0xc0:0xe0])) + }); + + p.w2 = Honk.G1ProofPoint({ + x_0: uint256(bytes32(proof[0xe0:0x100])), + x_1: uint256(bytes32(proof[0x100:0x120])), + y_0: uint256(bytes32(proof[0x120:0x140])), + y_1: uint256(bytes32(proof[0x140:0x160])) + }); + p.w3 = Honk.G1ProofPoint({ + x_0: uint256(bytes32(proof[0x160:0x180])), + x_1: uint256(bytes32(proof[0x180:0x1a0])), + y_0: uint256(bytes32(proof[0x1a0:0x1c0])), + y_1: uint256(bytes32(proof[0x1c0:0x1e0])) + }); + + // Lookup / Permutation Helper Commitments + p.lookupReadCounts = Honk.G1ProofPoint({ + x_0: uint256(bytes32(proof[0x1e0:0x200])), + x_1: uint256(bytes32(proof[0x200:0x220])), + y_0: uint256(bytes32(proof[0x220:0x240])), + y_1: uint256(bytes32(proof[0x240:0x260])) + }); + p.lookupReadTags = Honk.G1ProofPoint({ + x_0: uint256(bytes32(proof[0x260:0x280])), + x_1: uint256(bytes32(proof[0x280:0x2a0])), + y_0: uint256(bytes32(proof[0x2a0:0x2c0])), + y_1: uint256(bytes32(proof[0x2c0:0x2e0])) + }); + p.w4 = Honk.G1ProofPoint({ + x_0: uint256(bytes32(proof[0x2e0:0x300])), + x_1: uint256(bytes32(proof[0x300:0x320])), + y_0: uint256(bytes32(proof[0x320:0x340])), + y_1: uint256(bytes32(proof[0x340:0x360])) + }); + p.lookupInverses = Honk.G1ProofPoint({ + x_0: uint256(bytes32(proof[0x360:0x380])), + x_1: uint256(bytes32(proof[0x380:0x3a0])), + y_0: uint256(bytes32(proof[0x3a0:0x3c0])), + y_1: uint256(bytes32(proof[0x3c0:0x3e0])) + }); + p.zPerm = Honk.G1ProofPoint({ + x_0: uint256(bytes32(proof[0x3e0:0x400])), + x_1: uint256(bytes32(proof[0x400:0x420])), + y_0: uint256(bytes32(proof[0x420:0x440])), + y_1: uint256(bytes32(proof[0x440:0x460])) + }); + + // TEMP the boundary of what has already been read + uint256 boundary = 0x460; + + // Sumcheck univariates + // TODO: in this case we know what log_n is - so we hard code it, we would want this to be included in + // a cpp template for different circuit sizes + for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { + // The loop boundary of i, this will shift forward on each evaluation + uint256 loop_boundary = boundary + (i * 0x20 * BATCHED_RELATION_PARTIAL_LENGTH); + + for (uint256 j = 0; j < BATCHED_RELATION_PARTIAL_LENGTH; j++) { + uint256 start = loop_boundary + (j * 0x20); + uint256 end = start + 0x20; + p.sumcheckUnivariates[i][j] = FrLib.fromBytes32(bytes32(proof[start:end])); + } + } + + boundary = boundary + (CONST_PROOF_SIZE_LOG_N * BATCHED_RELATION_PARTIAL_LENGTH * 0x20); + // Sumcheck evaluations + for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { + uint256 start = boundary + (i * 0x20); + uint256 end = start + 0x20; + p.sumcheckEvaluations[i] = FrLib.fromBytes32(bytes32(proof[start:end])); + } + + boundary = boundary + (NUMBER_OF_ENTITIES * 0x20); + return p; + } + function computePublicInputDelta( bytes32[] memory publicInputs, Fr beta, @@ -1485,9 +1325,7 @@ contract HonkVerifier is IVerifier Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; bool valid = checkSum(roundUnivariate, roundTarget); if (!valid) revert SumcheckFailed(); - Fr roundChallenge = tp.sumCheckUChallenges[round]; - // Update the round target for the next rounf roundTarget = computeNextTargetSum(roundUnivariate, roundChallenge); powPartialEvaluation = partiallyEvaluatePOW(tp, powPartialEvaluation, roundChallenge, round); @@ -1500,7 +1338,7 @@ contract HonkVerifier is IVerifier function checkSum(Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate, Fr roundTarget) internal - pure + view returns (bool checked) { Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; @@ -1513,6 +1351,7 @@ contract HonkVerifier is IVerifier view returns (Fr targetSum) { + // TODO: inline Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000002d0), @@ -1536,6 +1375,7 @@ contract HonkVerifier is IVerifier ]; // To compute the next target sum, we evaluate the given univariate at a point u (challenge). + // TODO: opt: use same array mem for each iteratioon // Performing Barycentric evaluations // Compute B(x) Fr numeratorValue = Fr.wrap(1); @@ -1543,7 +1383,7 @@ contract HonkVerifier is IVerifier numeratorValue = numeratorValue * (roundChallenge - Fr.wrap(i)); } - // Calculate domain size N of inverses + // Calculate domain size N of inverses -- TODO: montgomery's trick Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; for (uint256 i; i < BATCHED_RELATION_PARTIAL_LENGTH; ++i) { Fr inv = BARYCENTRIC_LAGRANGE_DENOMINATORS[i]; @@ -1571,315 +1411,6 @@ contract HonkVerifier is IVerifier Fr univariateEval = Fr.wrap(1) + (roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); newEvaluation = currentEvaluation * univariateEval; } - - // Avoid stack too deep - struct ShpleminiIntermediates { - // i-th unshifted commitment is multiplied by −ρⁱ and the unshifted_scalar ( 1/(z−r) + ν/(z+r) ) - Fr unshiftedScalar; - // i-th shifted commitment is multiplied by −ρⁱ⁺ᵏ and the shifted_scalar r⁻¹ ⋅ (1/(z−r) − ν/(z+r)) - Fr shiftedScalar; - // Scalar to be multiplied by [1]₁ - Fr constantTermAccumulator; - // Accumulator for powers of rho - Fr batchingChallenge; - // Linear combination of multilinear (sumcheck) evaluations and powers of rho - Fr batchedEvaluation; - } - - function verifyShplemini(Honk.Proof memory proof, Honk.VerificationKey memory vk, Transcript memory tp) - internal - view - returns (bool verified) - { - ShpleminiIntermediates memory mem; // stack - - // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size, I think this should be CONST_PROOF_SIZE - Fr[CONST_PROOF_SIZE_LOG_N] memory powers_of_evaluation_challenge = computeSquares(tp.geminiR); - - // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings - Fr[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2] memory scalars; - Honk.G1Point[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2] memory commitments; - - Fr[CONST_PROOF_SIZE_LOG_N + 1] memory inverse_vanishing_evals = - computeInvertedGeminiDenominators(tp, powers_of_evaluation_challenge); - - mem.unshiftedScalar = inverse_vanishing_evals[0] + (tp.shplonkNu * inverse_vanishing_evals[1]); - mem.shiftedScalar = - tp.geminiR.invert() * (inverse_vanishing_evals[0] - (tp.shplonkNu * inverse_vanishing_evals[1])); - - scalars[0] = Fr.wrap(1); - commitments[0] = convertProofPoint(proof.shplonkQ); - - /* Batch multivariate opening claims, shifted and unshifted - * The vector of scalars is populated as follows: - * \f[ - * \left( - * - \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right), - * \ldots, - * - \rho^{i+k-1} \times \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right), - * - \rho^{i+k} \times \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right), - * \ldots, - * - \rho^{k+m-1} \times \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right) - * \right) - * \f] - * - * The following vector is concatenated to the vector of commitments: - * \f[ - * f_0, \ldots, f_{m-1}, f_{\text{shift}, 0}, \ldots, f_{\text{shift}, k-1} - * \f] - * - * Simultaneously, the evaluation of the multilinear polynomial - * \f[ - * \sum \rho^i \cdot f_i + \sum \rho^{i+k} \cdot f_{\text{shift}, i} - * \f] - * at the challenge point \f$ (u_0,\ldots, u_{n-1}) \f$ is computed. - * - * This approach minimizes the number of iterations over the commitments to multilinear polynomials - * and eliminates the need to store the powers of \f$ \rho \f$. - */ - mem.batchingChallenge = Fr.wrap(1); - mem.batchedEvaluation = Fr.wrap(0); - - for (uint256 i = 1; i <= NUMBER_UNSHIFTED; ++i) { - scalars[i] = mem.unshiftedScalar.neg() * mem.batchingChallenge; - mem.batchedEvaluation = mem.batchedEvaluation + (proof.sumcheckEvaluations[i - 1] * mem.batchingChallenge); - mem.batchingChallenge = mem.batchingChallenge * tp.rho; - } - // g commitments are accumulated at r - for (uint256 i = NUMBER_UNSHIFTED + 1; i <= NUMBER_OF_ENTITIES; ++i) { - scalars[i] = mem.shiftedScalar.neg() * mem.batchingChallenge; - mem.batchedEvaluation = mem.batchedEvaluation + (proof.sumcheckEvaluations[i - 1] * mem.batchingChallenge); - mem.batchingChallenge = mem.batchingChallenge * tp.rho; - } - - commitments[1] = vk.qm; - commitments[2] = vk.qc; - commitments[3] = vk.ql; - commitments[4] = vk.qr; - commitments[5] = vk.qo; - commitments[6] = vk.q4; - commitments[7] = vk.qArith; - commitments[8] = vk.qDeltaRange; - commitments[9] = vk.qElliptic; - commitments[10] = vk.qAux; - commitments[11] = vk.qLookup; - commitments[12] = vk.qPoseidon2External; - commitments[13] = vk.qPoseidon2Internal; - commitments[14] = vk.s1; - commitments[15] = vk.s2; - commitments[16] = vk.s3; - commitments[17] = vk.s4; - commitments[18] = vk.id1; - commitments[19] = vk.id2; - commitments[20] = vk.id3; - commitments[21] = vk.id4; - commitments[22] = vk.t1; - commitments[23] = vk.t2; - commitments[24] = vk.t3; - commitments[25] = vk.t4; - commitments[26] = vk.lagrangeFirst; - commitments[27] = vk.lagrangeLast; - - // Accumulate proof points - commitments[28] = convertProofPoint(proof.w1); - commitments[29] = convertProofPoint(proof.w2); - commitments[30] = convertProofPoint(proof.w3); - commitments[31] = convertProofPoint(proof.w4); - commitments[32] = convertProofPoint(proof.zPerm); - commitments[33] = convertProofPoint(proof.lookupInverses); - commitments[34] = convertProofPoint(proof.lookupReadCounts); - commitments[35] = convertProofPoint(proof.lookupReadTags); - - // to be Shifted - commitments[36] = vk.t1; - commitments[37] = vk.t2; - commitments[38] = vk.t3; - commitments[39] = vk.t4; - commitments[40] = convertProofPoint(proof.w1); - commitments[41] = convertProofPoint(proof.w2); - commitments[42] = convertProofPoint(proof.w3); - commitments[43] = convertProofPoint(proof.w4); - commitments[44] = convertProofPoint(proof.zPerm); - - /* Batch gemini claims from the prover - * place the commitments to gemini aᵢ to the vector of commitments, compute the contributions from - * aᵢ(−r²ⁱ) for i=1, … , n−1 to the constant term accumulator, add corresponding scalars - * - * 1. Moves the vector - * \f[ - * \left( \text{com}(A_1), \text{com}(A_2), \ldots, \text{com}(A_{n-1}) \right) - * \f] - * to the 'commitments' vector. - * - * 2. Computes the scalars: - * \f[ - * \frac{\nu^{2}}{z + r^2}, \frac{\nu^3}{z + r^4}, \ldots, \frac{\nu^{n-1}}{z + r^{2^{n-1}}} - * \f] - * and places them into the 'scalars' vector. - * - * 3. Accumulates the summands of the constant term: - * \f[ - * \sum_{i=2}^{n-1} \frac{\nu^{i} \cdot A_i(-r^{2^i})}{z + r^{2^i}} - * \f] - * and adds them to the 'constant_term_accumulator'. - */ - mem.constantTermAccumulator = Fr.wrap(0); - mem.batchingChallenge = tp.shplonkNu.sqr(); - - for (uint256 i; i < CONST_PROOF_SIZE_LOG_N - 1; ++i) { - bool dummy_round = i >= (LOG_N - 1); - - Fr scalingFactor = Fr.wrap(0); - if (!dummy_round) { - scalingFactor = mem.batchingChallenge * inverse_vanishing_evals[i + 2]; - scalars[NUMBER_OF_ENTITIES + 1 + i] = scalingFactor.neg(); - } - - mem.constantTermAccumulator = - mem.constantTermAccumulator + (scalingFactor * proof.geminiAEvaluations[i + 1]); - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - - commitments[NUMBER_OF_ENTITIES + 1 + i] = convertProofPoint(proof.geminiFoldComms[i]); - } - - // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: - // Compute evaluation A₀(r) - Fr a_0_pos = computeGeminiBatchedUnivariateEvaluation( - tp, mem.batchedEvaluation, proof.geminiAEvaluations, powers_of_evaluation_challenge - ); - - mem.constantTermAccumulator = mem.constantTermAccumulator + (a_0_pos * inverse_vanishing_evals[0]); - mem.constantTermAccumulator = - mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * inverse_vanishing_evals[1]); - - // Finalise the batch opening claim - commitments[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N] = Honk.G1Point({x: 1, y: 2}); - scalars[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N] = mem.constantTermAccumulator; - - Honk.G1Point memory quotient_commitment = convertProofPoint(proof.kzgQuotient); - - commitments[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 1] = quotient_commitment; - scalars[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 1] = tp.shplonkZ; // evaluation challenge - - Honk.G1Point memory P_0 = batchMul(commitments, scalars); - Honk.G1Point memory P_1 = negateInplace(quotient_commitment); - - return pairing(P_0, P_1); - } - - function computeSquares(Fr r) internal pure returns (Fr[CONST_PROOF_SIZE_LOG_N] memory squares) { - squares[0] = r; - for (uint256 i = 1; i < CONST_PROOF_SIZE_LOG_N; ++i) { - squares[i] = squares[i - 1].sqr(); - } - } - - function computeInvertedGeminiDenominators( - Transcript memory tp, - Fr[CONST_PROOF_SIZE_LOG_N] memory eval_challenge_powers - ) internal view returns (Fr[CONST_PROOF_SIZE_LOG_N + 1] memory inverse_vanishing_evals) { - Fr eval_challenge = tp.shplonkZ; - inverse_vanishing_evals[0] = (eval_challenge - eval_challenge_powers[0]).invert(); - - for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; ++i) { - Fr round_inverted_denominator = Fr.wrap(0); - if (i <= LOG_N + 1) { - round_inverted_denominator = (eval_challenge + eval_challenge_powers[i]).invert(); - } - inverse_vanishing_evals[i + 1] = round_inverted_denominator; - } - } - - function computeGeminiBatchedUnivariateEvaluation( - Transcript memory tp, - Fr batchedEvalAccumulator, - Fr[CONST_PROOF_SIZE_LOG_N] memory geminiEvaluations, - Fr[CONST_PROOF_SIZE_LOG_N] memory geminiEvalChallengePowers - ) internal view returns (Fr a_0_pos) { - for (uint256 i = CONST_PROOF_SIZE_LOG_N; i > 0; --i) { - Fr challengePower = geminiEvalChallengePowers[i - 1]; - Fr u = tp.sumCheckUChallenges[i - 1]; - Fr evalNeg = geminiEvaluations[i - 1]; - - Fr batchedEvalRoundAcc = ( - (challengePower * batchedEvalAccumulator * Fr.wrap(2)) - - evalNeg * (challengePower * (Fr.wrap(1) - u) - u) - ); - // Divide by the denominator - batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (Fr.wrap(1) - u) + u).invert(); - - bool is_dummy_round = (i > LOG_N); - if (!is_dummy_round) { - batchedEvalAccumulator = batchedEvalRoundAcc; - } - } - - a_0_pos = batchedEvalAccumulator; - } - - // This implementation is the same as above with different constants - function batchMul( - Honk.G1Point[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2] memory base, - Fr[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2] memory scalars - ) internal view returns (Honk.G1Point memory result) { - uint256 limit = NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2; - assembly { - let success := 0x01 - let free := mload(0x40) - - // Write the original into the accumulator - // Load into memory for ecMUL, leave offset for eccAdd result - // base is an array of pointers, so we have to dereference them - mstore(add(free, 0x40), mload(mload(base))) - mstore(add(free, 0x60), mload(add(0x20, mload(base)))) - // Add scalar - mstore(add(free, 0x80), mload(scalars)) - success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, free, 0x40)) - - let count := 0x01 - for {} lt(count, limit) { count := add(count, 1) } { - // Get loop offsets - let base_base := add(base, mul(count, 0x20)) - let scalar_base := add(scalars, mul(count, 0x20)) - - mstore(add(free, 0x40), mload(mload(base_base))) - mstore(add(free, 0x60), mload(add(0x20, mload(base_base)))) - // Add scalar - mstore(add(free, 0x80), mload(scalar_base)) - - success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) - // accumulator = accumulator + accumulator_2 - success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) - } - - // Return the result - i hate this - mstore(result, mload(free)) - mstore(add(result, 0x20), mload(add(free, 0x20))) - } - } - - function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) internal view returns (bool) { - bytes memory input = abi.encodePacked( - rhs.x, - rhs.y, - // Fixed G1 point - uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), - uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), - uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), - uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), - lhs.x, - lhs.y, - // G1 point from VK - uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), - uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), - uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), - uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) - ); - - (bool success, bytes memory result) = address(0x08).staticcall(input); - bool decodedResult = abi.decode(result, (bool)); - return success && decodedResult; - } } // Conversion util - Duplicated as we cannot template LOG_N @@ -1891,7 +1422,6 @@ function convertPoints(Honk.G1ProofPoint[LOG_N + 1] memory commitments) converted[i] = convertProofPoint(commitments[i]); } } - )"; inline std::string get_honk_solidity_verifier(auto const& verification_key) diff --git a/barretenberg/sol/src/honk/HonkTypes.sol b/barretenberg/sol/src/honk/HonkTypes.sol index 55176738d07..3f5e5f76cb1 100644 --- a/barretenberg/sol/src/honk/HonkTypes.sol +++ b/barretenberg/sol/src/honk/HonkTypes.sol @@ -136,7 +136,7 @@ library Honk { // Sumcheck Fr[BATCHED_RELATION_PARTIAL_LENGTH][CONST_PROOF_SIZE_LOG_N] sumcheckUnivariates; Fr[NUMBER_OF_ENTITIES] sumcheckEvaluations; - // Shplemini + // Gemini Honk.G1ProofPoint[CONST_PROOF_SIZE_LOG_N - 1] geminiFoldComms; Fr[CONST_PROOF_SIZE_LOG_N] geminiAEvaluations; Honk.G1ProofPoint shplonkQ; diff --git a/barretenberg/sol/src/honk/HonkVerifier.sol b/barretenberg/sol/src/honk/HonkVerifier.sol new file mode 100644 index 00000000000..635d4188e71 --- /dev/null +++ b/barretenberg/sol/src/honk/HonkVerifier.sol @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Aztec Labs +pragma solidity >=0.8.21; + +import {IVerifier} from "../interfaces/IVerifier.sol"; +import {Add2HonkVerificationKey as VK, N, LOG_N} from "./keys/Add2HonkVerificationKey.sol"; + +import { + Honk, + WIRE, + NUMBER_OF_ENTITIES, + NUMBER_OF_SUBRELATIONS, + NUMBER_OF_ALPHAS, + NUMBER_UNSHIFTED, + BATCHED_RELATION_PARTIAL_LENGTH, + CONST_PROOF_SIZE_LOG_N +} from "./HonkTypes.sol"; + +import {ecMul, ecAdd, ecSub, negateInplace, convertProofPoint} from "./utils.sol"; + +// Field arithmetic libraries - prevent littering the code with modmul / addmul +import {MODULUS as P, MINUS_ONE, Fr, FrLib} from "./Fr.sol"; +// Transcript library to generate fiat shamir challenges +import {Transcript, TranscriptLib} from "./Transcript.sol"; + +import {RelationsLib} from "./Relations.sol"; + +error PublicInputsLengthWrong(); +error SumcheckFailed(); + +/// Smart contract verifier of honk proofs +abstract contract BaseHonkVerifier is IVerifier { + Fr internal constant GRUMPKIN_CURVE_B_PARAMETER_NEGATED = Fr.wrap(17); // -(-17) + + function verify(bytes calldata proof, bytes32[] calldata publicInputs) public view override returns (bool) { + Honk.VerificationKey memory vk = loadVerificationKey(); + Honk.Proof memory p = TranscriptLib.loadProof(proof); + + if (publicInputs.length != vk.publicInputsSize) { + revert PublicInputsLengthWrong(); + } + + // Generate the fiat shamir challenges for the whole protocol + Transcript memory t = TranscriptLib.generateTranscript(p, publicInputs, vk.publicInputsSize); + + // Compute the public input delta + t.publicInputsDelta = + computePublicInputDelta(publicInputs, t.beta, t.gamma, vk.circuitSize, p.publicInputsOffset); + + // Sumcheck + bool sumcheckVerified = verifySumcheck(p, t); + if (!sumcheckVerified) revert SumcheckFailed(); + + return sumcheckVerified; // Boolean condition not required - nice for vanity :) + } + + function loadVerificationKey() internal view returns (Honk.VerificationKey memory) { + return VK.loadVerificationKey(); + } + + function computePublicInputDelta( + bytes32[] memory publicInputs, + Fr beta, + Fr gamma, + uint256 domainSize, + uint256 offset + ) internal view returns (Fr publicInputDelta) { + Fr numerator = Fr.wrap(1); + Fr denominator = Fr.wrap(1); + + Fr numeratorAcc = gamma + (beta * FrLib.from(domainSize + offset)); + Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); + + { + for (uint256 i = 0; i < publicInputs.length; i++) { + Fr pubInput = FrLib.fromBytes32(publicInputs[i]); + + numerator = numerator * (numeratorAcc + pubInput); + denominator = denominator * (denominatorAcc + pubInput); + + numeratorAcc = numeratorAcc + beta; + denominatorAcc = denominatorAcc - beta; + } + } + + // Fr delta = numerator / denominator; // TOOO: batch invert later? + publicInputDelta = FrLib.div(numerator, denominator); + } + + uint256 constant ROUND_TARGET = 0; + + function verifySumcheck(Honk.Proof memory proof, Transcript memory tp) internal view returns (bool verified) { + Fr roundTarget; + Fr powPartialEvaluation = Fr.wrap(1); + + // We perform sumcheck reductions over log n rounds ( the multivariate degree ) + for (uint256 round; round < LOG_N; ++round) { + Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; + bool valid = checkSum(roundUnivariate, roundTarget); + if (!valid) revert SumcheckFailed(); + + Fr roundChallenge = tp.sumCheckUChallenges[round]; + + // Update the round target for the next rounf + roundTarget = computeNextTargetSum(roundUnivariate, roundChallenge); + powPartialEvaluation = partiallyEvaluatePOW(tp, powPartialEvaluation, roundChallenge, round); + } + + // Last round + Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations(proof, tp, powPartialEvaluation); + verified = (grandHonkRelationSum == roundTarget); + } + + function checkSum(Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate, Fr roundTarget) + internal + view + returns (bool checked) + { + Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; + checked = totalSum == roundTarget; + } + + // Return the new target sum for the next sumcheck round + function computeNextTargetSum(Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) + internal + view + returns (Fr targetSum) + { + // TODO: inline + Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000002d0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff11), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000090), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff71), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000000f0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000013b0) + ]; + + Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_DOMAIN = [ + Fr.wrap(0x00), + Fr.wrap(0x01), + Fr.wrap(0x02), + Fr.wrap(0x03), + Fr.wrap(0x04), + Fr.wrap(0x05), + Fr.wrap(0x06), + Fr.wrap(0x07) + ]; + // To compute the next target sum, we evaluate the given univariate at a point u (challenge). + + // TODO: opt: use same array mem for each iteratioon + // Performing Barycentric evaluations + // Compute B(x) + Fr numeratorValue = Fr.wrap(1); + for (uint256 i; i < BATCHED_RELATION_PARTIAL_LENGTH; ++i) { + numeratorValue = numeratorValue * (roundChallenge - Fr.wrap(i)); + } + + // Calculate domain size N of inverses -- TODO: montgomery's trick + Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; + for (uint256 i; i < BATCHED_RELATION_PARTIAL_LENGTH; ++i) { + Fr inv = BARYCENTRIC_LAGRANGE_DENOMINATORS[i]; + inv = inv * (roundChallenge - BARYCENTRIC_DOMAIN[i]); + inv = FrLib.invert(inv); + denominatorInverses[i] = inv; + } + + for (uint256 i; i < BATCHED_RELATION_PARTIAL_LENGTH; ++i) { + Fr term = roundUnivariates[i]; + term = term * denominatorInverses[i]; + targetSum = targetSum + term; + } + + // Scale the sum by the value of B(x) + targetSum = targetSum * numeratorValue; + } + + // Univariate evaluation of the monomial ((1-X_l) + X_l.B_l) at the challenge point X_l=u_l + function partiallyEvaluatePOW(Transcript memory tp, Fr currentEvaluation, Fr roundChallenge, uint256 round) + internal + pure + returns (Fr newEvaluation) + { + Fr univariateEval = Fr.wrap(1) + (roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); + newEvaluation = currentEvaluation * univariateEval; + } + + // TODO: Implement Shplemini, functions above are left here in case they are useful + + // TODO: TODO: TODO: optimize + // Scalar Mul and acumulate into total + function batchMul(Honk.G1Point[LOG_N + 1] memory base, Fr[LOG_N + 1] memory scalars) + internal + view + returns (Honk.G1Point memory result) + { + uint256 limit = LOG_N + 1; + assembly { + let success := 0x01 + let free := mload(0x40) + + // Write the original into the accumulator + // Load into memory for ecMUL, leave offset for eccAdd result + // base is an array of pointers, so we have to dereference them + mstore(add(free, 0x40), mload(mload(base))) + mstore(add(free, 0x60), mload(add(0x20, mload(base)))) + // Add scalar + mstore(add(free, 0x80), mload(scalars)) + success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, free, 0x40)) + + let count := 0x01 + + for {} lt(count, limit) { count := add(count, 1) } { + // Get loop offsets + let base_base := add(base, mul(count, 0x20)) + let scalar_base := add(scalars, mul(count, 0x20)) + + mstore(add(free, 0x40), mload(mload(base_base))) + mstore(add(free, 0x60), mload(add(0x20, mload(base_base)))) + // Add scalar + mstore(add(free, 0x80), mload(scalar_base)) + + success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) + success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) + } + + mstore(result, mload(free)) + mstore(add(result, 0x20), mload(add(free, 0x20))) + } + } + + // This implementation is the same as above with different constants + function batchMul2( + Honk.G1Point[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 1] memory base, + Fr[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 1] memory scalars + ) internal view returns (Honk.G1Point memory result) { + uint256 limit = NUMBER_OF_ENTITIES + LOG_N + 1; + assembly { + let success := 0x01 + let free := mload(0x40) + + // Write the original into the accumulator + // Load into memory for ecMUL, leave offset for eccAdd result + // base is an array of pointers, so we have to dereference them + mstore(add(free, 0x40), mload(mload(base))) + mstore(add(free, 0x60), mload(add(0x20, mload(base)))) + // Add scalar + mstore(add(free, 0x80), mload(scalars)) + success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, free, 0x40)) + + let count := 0x01 + for {} lt(count, limit) { count := add(count, 1) } { + // Get loop offsets + let base_base := add(base, mul(count, 0x20)) + let scalar_base := add(scalars, mul(count, 0x20)) + + mstore(add(free, 0x40), mload(mload(base_base))) + mstore(add(free, 0x60), mload(add(0x20, mload(base_base)))) + // Add scalar + mstore(add(free, 0x80), mload(scalar_base)) + + success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) + } + + // Return the result - i hate this + mstore(result, mload(free)) + mstore(add(result, 0x20), mload(add(free, 0x20))) + } + } + + // function kzgReduceVerify( + // Honk.Proof memory proof, + // Transcript memory tp, + // Fr evaluation, + // Honk.G1Point memory commitment + // ) internal view returns (bool) { + // Honk.G1Point memory quotient_commitment = convertProofPoint(proof.zmPi); + // Honk.G1Point memory ONE = Honk.G1Point({x: 1, y: 2}); + + // Honk.G1Point memory P0 = commitment; + // P0 = ecAdd(P0, ecMul(quotient_commitment, tp.zmX)); + + // Honk.G1Point memory evalAsPoint = ecMul(ONE, evaluation); + // P0 = ecSub(P0, evalAsPoint); + + // Honk.G1Point memory P1 = negateInplace(quotient_commitment); + + // // Perform pairing check + // return pairing(P0, P1); + // } + + function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) internal view returns (bool) { + bytes memory input = abi.encodePacked( + rhs.x, + rhs.y, + // Fixed G1 point + uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), + uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), + uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), + uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), + lhs.x, + lhs.y, + // G1 point from VK + uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), + uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), + uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), + uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) + ); + + (bool success, bytes memory result) = address(0x08).staticcall(input); + return abi.decode(result, (bool)); + } +} + +// Conversion util - Duplicated as we cannot template LOG_N +function convertPoints(Honk.G1ProofPoint[LOG_N + 1] memory commitments) + pure + returns (Honk.G1Point[LOG_N + 1] memory converted) +{ + for (uint256 i; i < LOG_N + 1; ++i) { + converted[i] = convertProofPoint(commitments[i]); + } +} diff --git a/barretenberg/sol/src/honk/Relations.sol b/barretenberg/sol/src/honk/Relations.sol index 9ca4868cc44..685797de47f 100644 --- a/barretenberg/sol/src/honk/Relations.sol +++ b/barretenberg/sol/src/honk/Relations.sol @@ -24,7 +24,7 @@ library RelationsLib { function accumulateRelationEvaluations(Honk.Proof memory proof, Transcript memory tp, Fr powPartialEval) internal - pure + view returns (Fr accumulator) { Fr[NUMBER_OF_ENTITIES] memory purportedEvaluations = proof.sumcheckEvaluations; @@ -37,7 +37,7 @@ library RelationsLib { accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); accumulateAuxillaryRelation(purportedEvaluations, tp, evaluations, powPartialEval); - accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonExternalRelation(purportedEvaluations, tp, evaluations, powPartialEval); accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); // batch the subrelations with the alpha challenges to obtain the full honk relation accumulator = scaleAndBatchSubrelations(evaluations, tp.alphas); @@ -52,7 +52,6 @@ library RelationsLib { return p[uint256(_wire)]; } - uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** * Ultra Arithmetic Relation * @@ -61,11 +60,11 @@ library RelationsLib { Fr[NUMBER_OF_ENTITIES] memory p, Fr[NUMBER_OF_SUBRELATIONS] memory evals, Fr domainSep - ) internal pure { + ) internal view { // Relation 0 Fr q_arith = wire(p, WIRE.Q_ARITH); { - Fr neg_half = Fr.wrap(NEG_HALF_MODULO_P); + Fr neg_half = Fr.wrap(0) - (FrLib.invert(Fr.wrap(2))); Fr accum = (q_arith - Fr.wrap(3)) * (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * neg_half; accum = accum + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) @@ -179,7 +178,7 @@ library RelationsLib { Fr[NUMBER_OF_ENTITIES] memory p, Fr[NUMBER_OF_SUBRELATIONS] memory evals, Fr domainSep - ) internal pure { + ) internal view { Fr minus_one = Fr.wrap(0) - Fr.wrap(1); Fr minus_two = Fr.wrap(0) - Fr.wrap(2); Fr minus_three = Fr.wrap(0) - Fr.wrap(3); @@ -602,6 +601,7 @@ library RelationsLib { function accumulatePoseidonExternalRelation( Fr[NUMBER_OF_ENTITIES] memory p, + Transcript memory tp, // I think this is not needed Fr[NUMBER_OF_SUBRELATIONS] memory evals, Fr domainSep // i guess this is the scaling factor? ) internal pure { diff --git a/barretenberg/sol/src/honk/Transcript.sol b/barretenberg/sol/src/honk/Transcript.sol index ca4e1449716..8576b180c99 100644 --- a/barretenberg/sol/src/honk/Transcript.sol +++ b/barretenberg/sol/src/honk/Transcript.sol @@ -31,7 +31,7 @@ struct Transcript { library TranscriptLib { function generateTranscript(Honk.Proof memory proof, bytes32[] calldata publicInputs, uint256 publicInputsSize) internal - pure + view returns (Transcript memory t) { Fr previousChallenge; diff --git a/barretenberg/sol/src/honk/instance/Add2Honk.sol b/barretenberg/sol/src/honk/instance/Add2Honk.sol index 00e26a8ca1b..a56a3b256a0 100644 --- a/barretenberg/sol/src/honk/instance/Add2Honk.sol +++ b/barretenberg/sol/src/honk/instance/Add2Honk.sol @@ -198,9 +198,9 @@ contract Add2HonkVerifier is IVerifier { Fr shiftedScalar; // Scalar to be multiplied by [1]₁ Fr constantTermAccumulator; - // Accumulator for powers of rho - Fr batchingChallenge; // Linear combination of multilinear (sumcheck) evaluations and powers of rho + Fr batchingChallenge; + // Accumulator for powers of rho Fr batchedEvaluation; } diff --git a/barretenberg/sol/src/honk/instance/BlakeHonk.sol b/barretenberg/sol/src/honk/instance/BlakeHonk.sol index 6e617946e06..d5f739f0315 100644 --- a/barretenberg/sol/src/honk/instance/BlakeHonk.sol +++ b/barretenberg/sol/src/honk/instance/BlakeHonk.sol @@ -199,9 +199,9 @@ contract BlakeHonkVerifier is IVerifier { Fr shiftedScalar; // Scalar to be multiplied by [1]₁ Fr constantTermAccumulator; - // Accumulator for powers of rho - Fr batchingChallenge; // Linear combination of multilinear (sumcheck) evaluations and powers of rho + Fr batchingChallenge; + // Accumulator for powers of rho Fr batchedEvaluation; } diff --git a/barretenberg/sol/src/honk/instance/EcdsaHonk.sol b/barretenberg/sol/src/honk/instance/EcdsaHonk.sol index 6d8a4a39c02..5e3c064defc 100644 --- a/barretenberg/sol/src/honk/instance/EcdsaHonk.sol +++ b/barretenberg/sol/src/honk/instance/EcdsaHonk.sol @@ -198,9 +198,9 @@ contract EcdsaHonkVerifier is IVerifier { Fr shiftedScalar; // Scalar to be multiplied by [1]₁ Fr constantTermAccumulator; - // Accumulator for powers of rho - Fr batchingChallenge; // Linear combination of multilinear (sumcheck) evaluations and powers of rho + Fr batchingChallenge; + // Accumulator for powers of rho Fr batchedEvaluation; } diff --git a/yarn-project/cli/package.json b/yarn-project/cli/package.json index c70eeffc78b..b6a9891babd 100644 --- a/yarn-project/cli/package.json +++ b/yarn-project/cli/package.json @@ -78,7 +78,7 @@ "lodash.chunk": "^4.2.0", "lodash.groupby": "^4.6.0", "semver": "^7.5.4", - "solc": "^0.8.27", + "solc": "^0.8.26", "source-map-support": "^0.5.21", "tslib": "^2.4.0", "viem": "^2.7.15" diff --git a/yarn-project/end-to-end/package.json b/yarn-project/end-to-end/package.json index d2819649d11..bf630f4e047 100644 --- a/yarn-project/end-to-end/package.json +++ b/yarn-project/end-to-end/package.json @@ -81,7 +81,7 @@ "process": "^0.11.10", "puppeteer": "^22.2", "resolve-typescript-plugin": "^2.0.1", - "solc": "^0.8.27", + "solc": "^0.8.25", "stream-browserify": "^3.0.0", "string-argv": "^0.3.2", "ts-loader": "^9.4.4", diff --git a/yarn-project/ethereum/src/deploy_l1_contracts.ts b/yarn-project/ethereum/src/deploy_l1_contracts.ts index 3bc0f440a6b..7cb33ec0b13 100644 --- a/yarn-project/ethereum/src/deploy_l1_contracts.ts +++ b/yarn-project/ethereum/src/deploy_l1_contracts.ts @@ -567,7 +567,7 @@ export function compileContract( enabled: true, runs: 200, }, - evmVersion: 'cancun', + evmVersion: 'paris', outputSelection: { '*': { '*': ['evm.bytecode.object', 'abi'], diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index dd7db7e86c5..3a409f0ab9c 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -480,7 +480,7 @@ __metadata: lodash.chunk: ^4.2.0 lodash.groupby: ^4.6.0 semver: ^7.5.4 - solc: ^0.8.27 + solc: ^0.8.26 source-map-support: ^0.5.21 ts-jest: ^29.1.0 ts-node: ^10.9.1 @@ -571,7 +571,7 @@ __metadata: process: ^0.11.10 puppeteer: ^22.2 resolve-typescript-plugin: ^2.0.1 - solc: ^0.8.27 + solc: ^0.8.25 stream-browserify: ^3.0.0 string-argv: ^0.3.2 ts-loader: ^9.4.4 @@ -14498,9 +14498,9 @@ __metadata: languageName: node linkType: hard -"solc@npm:^0.8.27": - version: 0.8.27 - resolution: "solc@npm:0.8.27" +"solc@npm:^0.8.25": + version: 0.8.25 + resolution: "solc@npm:0.8.25" dependencies: command-exists: ^1.2.8 commander: ^8.1.0 @@ -14511,7 +14511,24 @@ __metadata: tmp: 0.0.33 bin: solcjs: solc.js - checksum: 33a81256445b7999672db8b03f94a7a767c28b9be88f60030fbc1b43b5ef9c3fa44591193abe8dda166df333c5fa4370bfcfb67acf01f8ca828ad7fb2f8ce54e + checksum: dce5d31c24be940c2486949dd1eb6ee12f3ebcaa11e60fb51a07b3bfaf74818d503f4eb1aef83e72302b3203a00edf42b91609364d5daa94c35dde20953326ae + languageName: node + linkType: hard + +"solc@npm:^0.8.26": + version: 0.8.26 + resolution: "solc@npm:0.8.26" + dependencies: + command-exists: ^1.2.8 + commander: ^8.1.0 + follow-redirects: ^1.12.1 + js-sha3: 0.8.0 + memorystream: ^0.3.1 + semver: ^5.5.0 + tmp: 0.0.33 + bin: + solcjs: solc.js + checksum: e3eaeac76e60676377b357af8f3919d4c8c6a74b74112b49279fe8c74a3dfa1de8afe4788689fc307453bde336edc8572988d2cf9e909f84d870420eb640400c languageName: node linkType: hard