diff --git a/barretenberg/acir_tests/flows/honk_sol.sh b/barretenberg/acir_tests/flows/honk_sol.sh index 1801d034e19..130380d76c3 100755 --- a/barretenberg/acir_tests/flows/honk_sol.sh +++ b/barretenberg/acir_tests/flows/honk_sol.sh @@ -19,7 +19,7 @@ export VERIFIER_PATH="$(pwd)/Verifier.sol" export TEST_PATH=$(realpath "../../sol-test/HonkTest.sol") export TESTING_HONK="true" -# Use solcjs to compile the generated key contract with the template verifier and test contract +# Use solcjs to compile the generated key contract with the template verifier and test contract # index.js will start an anvil, on a random port # Deploy the verifier then send a test transaction export TEST_NAME=$(basename $(pwd)) diff --git a/barretenberg/acir_tests/sol-test/src/index.js b/barretenberg/acir_tests/sol-test/src/index.js index 81833cd9e24..bcfed952892 100644 --- a/barretenberg/acir_tests/sol-test/src/index.js +++ b/barretenberg/acir_tests/sol-test/src/index.js @@ -3,9 +3,11 @@ const { readFileSync, promises: fsPromises } = fs; import { spawn } from "child_process"; import { ethers } from "ethers"; import solc from "solc"; +import linker from "solc/linker.js"; const NUMBER_OF_FIELDS_IN_PLONK_PROOF = 93; -const NUMBER_OF_FIELDS_IN_HONK_PROOF = 393; +// This excludes the public inputs which are sent separately to the Solidity verifier +const NUMBER_OF_FIELDS_IN_HONK_PROOF = 423; // 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 @@ -73,28 +75,26 @@ export const compilationInput = { // If testing honk is set, then we compile the honk test suite const testingHonk = getEnvVarCanBeUndefined("TESTING_HONK"); -const NUMBER_OF_FIELDS_IN_PROOF = testingHonk ? NUMBER_OF_FIELDS_IN_HONK_PROOF : NUMBER_OF_FIELDS_IN_PLONK_PROOF; +const NUMBER_OF_FIELDS_IN_PROOF = testingHonk + ? NUMBER_OF_FIELDS_IN_HONK_PROOF + : NUMBER_OF_FIELDS_IN_PLONK_PROOF; if (!testingHonk) { - - const keyPath = getEnvVar("KEY_PATH"); - const basePath = getEnvVar("BASE_PATH"); - const [key, base] = await Promise.all( - [ - fsPromises.readFile(keyPath, encoding), - fsPromises.readFile(basePath, encoding), - ] - ); - - compilationInput.sources["BaseUltraVerifier.sol"] = { - content: base, - }; - compilationInput.sources["Key.sol"] = { - content: key, - }; + const keyPath = getEnvVar("KEY_PATH"); + const basePath = getEnvVar("BASE_PATH"); + const [key, base] = await Promise.all([ + fsPromises.readFile(keyPath, encoding), + fsPromises.readFile(basePath, encoding), + ]); + + compilationInput.sources["BaseUltraVerifier.sol"] = { + content: base, + }; + compilationInput.sources["Key.sol"] = { + content: key, + }; } var output = JSON.parse(solc.compile(JSON.stringify(compilationInput))); - const contract = output.contracts["Test.sol"]["Test"]; const bytecode = contract.evm.bytecode.object; const abi = contract.abi; @@ -133,7 +133,7 @@ const launchAnvil = async (port) => { * Deploys the contract * @param {ethers.Signer} signer */ -const deploy = async (signer) => { +const deploy = async (signer, abi, bytecode) => { const factory = new ethers.ContractFactory(abi, bytecode, signer); const deployment = await factory.deploy(); const deployed = await deployment.waitForDeployment(); @@ -147,14 +147,14 @@ const deploy = async (signer) => { */ const readPublicInputs = (proofAsFields) => { const publicInputs = []; - // A proof with no public inputs is 93 fields long + // Compute the number of public inputs, not accounted for in the constant NUMBER_OF_FIELDS_IN_PROOF const numPublicInputs = proofAsFields.length - NUMBER_OF_FIELDS_IN_PROOF; let publicInputsOffset = 0; - + // Honk proofs contain 3 pieces of metadata before the public inputs, while plonk does not if (testingHonk) { publicInputsOffset = 3; - } + } for (let i = 0; i < numPublicInputs; i++) { publicInputs.push(proofAsFields[publicInputsOffset + i]); @@ -215,7 +215,7 @@ try { proofStr = proofStr.substring(8); // Get the part before and after the public inputs const proofStart = proofStr.slice(0, 64 * 3); - const proofEnd = proofStr.substring((64 * 3) + (64 * numPublicInputs)); + const proofEnd = proofStr.substring(64 * 3 + 64 * numPublicInputs); proofStr = proofStart + proofEnd; } else { proofStr = proofStr.substring(64 * numPublicInputs); @@ -228,8 +228,7 @@ try { const provider = await getProvider(randomPort); const signer = new ethers.Wallet(key, provider); - // deploy - const address = await deploy(signer); + const address = await deploy(signer, abi, bytecode); const contract = new ethers.Contract(address, abi, signer); const result = await contract.test(proofStr, publicInputs); diff --git a/barretenberg/cpp/src/barretenberg/bb/main.cpp b/barretenberg/cpp/src/barretenberg/bb/main.cpp index 9f69a7a8ac7..499fbf035d8 100644 --- a/barretenberg/cpp/src/barretenberg/bb/main.cpp +++ b/barretenberg/cpp/src/barretenberg/bb/main.cpp @@ -10,7 +10,7 @@ #include "barretenberg/serialize/cbind.hpp" #include "barretenberg/stdlib/client_ivc_verifier/client_ivc_recursive_verifier.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_flavor.hpp" -#include "barretenberg/stdlib_circuit_builders/ultra_keccak.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_keccak_flavor.hpp" #include #ifndef DISABLE_AZTEC_VM @@ -1085,7 +1085,6 @@ 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(); - 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 711ed414a81..b79539349f1 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp @@ -17,6 +17,8 @@ using { equal as == } for Fr global; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order +Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); + // Instantiation library FrLib { @@ -43,14 +45,14 @@ library FrLib // Call the modexp precompile to invert in the field assembly { - let free := mload(0x40) - mstore(free, 0x20) - mstore(add(free, 0x20), 0x20) + let free := mload(0x40) + mstore(free, 0x20) + mstore(add(free, 0x20), 0x20) mstore(add(free, 0x40), 0x20) mstore(add(free, 0x60), v) - mstore(add(free, 0x80), sub(MODULUS, 2)) + mstore(add(free, 0x80), sub(MODULUS, 2)) mstore(add(free, 0xa0), MODULUS) - let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) + let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) if iszero(success) { revert(0, 0) } @@ -68,17 +70,17 @@ library FrLib // Call the modexp precompile to invert in the field assembly { - let free := mload(0x40) - mstore(free, 0x20) - mstore(add(free, 0x20), 0x20) + let free := mload(0x40) + mstore(free, 0x20) + mstore(add(free, 0x20), 0x20) mstore(add(free, 0x40), 0x20) mstore(add(free, 0x60), b) - mstore(add(free, 0x80), v) - mstore(add(free, 0xa0), MODULUS) - let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) - if iszero(success) { - revert(0, 0) - } + mstore(add(free, 0x80), v) + mstore(add(free, 0xa0), MODULUS) + let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) + if iszero(success) { + revert(0, 0) + } result := mload(0x00) } @@ -87,7 +89,6 @@ library FrLib function div(Fr numerator, Fr denominator) internal view returns(Fr) { - Fr inversion = invert(denominator); return numerator * invert(denominator); } } @@ -131,14 +132,18 @@ function equal(Fr a, Fr b) pure returns(bool) uint256 constant CONST_PROOF_SIZE_LOG_N = 28; -uint256 constant NUMBER_OF_SUBRELATIONS = 18; -uint256 constant BATCHED_RELATION_PARTIAL_LENGTH = 7; -uint256 constant NUMBER_OF_ENTITIES = 42; -uint256 constant NUMBER_OF_ALPHAS = 17; +uint256 constant NUMBER_OF_SUBRELATIONS = 26; +uint256 constant BATCHED_RELATION_PARTIAL_LENGTH = 8; +uint256 constant NUMBER_OF_ENTITIES = 44; +uint256 constant NUMBER_UNSHIFTED = 35; +uint256 constant NUMBER_TO_BE_SHIFTED = 9; + +// Alphas are used as relation separators so there should be NUMBER_OF_SUBRELATIONS - 1 +uint256 constant NUMBER_OF_ALPHAS = 25; // Prime field order -uint256 constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; // EC group order -uint256 constant P = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order +uint256 constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; // EC group order. F_q +uint256 constant P = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order, F_r // ENUM FOR WIRES enum WIRE { @@ -153,6 +158,8 @@ enum WIRE { Q_ELLIPTIC, Q_AUX, Q_LOOKUP, + Q_POSEIDON2_EXTERNAL, + Q_POSEIDON2_INTERNAL, SIGMA_1, SIGMA_2, SIGMA_3, @@ -216,6 +223,8 @@ library Honk { G1Point qAux; // Auxillary G1Point qElliptic; // Auxillary G1Point qLookup; // Lookup + G1Point qPoseidon2External; + G1Point qPoseidon2Internal; // Copy cnstraints G1Point s1; G1Point s2; @@ -491,316 +500,28 @@ function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point mem return point; } -// Errors -error PublicInputsLengthWrong(); -error SumcheckFailed(); -error ZeromorphFailed(); -interface IVerifier { - function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool); -} - -/// Smart contract verifier of honk proofs -contract HonkVerifier is IVerifier -{ +library RelationsLib { 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 = loadProof(proof); - - if (publicInputs.length != vk.publicInputsSize) { - revert PublicInputsLengthWrong(); - } - - // Generate the fiat shamir challenges for the whole protocol - Transcript memory t = TranscriptLib.generateTranscript(p, vk, publicInputs); - - // 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(); - - // Zeromorph - bool zeromorphVerified = verifyZeroMorph(p, vk, t); - if (!zeromorphVerified) - revert ZeromorphFailed(); - - return sumcheckVerified && zeromorphVerified; // Boolean condition not required - nice for vanity :) - } - - function loadVerificationKey() internal view returns(Honk.VerificationKey memory) - { - return HonkVerificationKey.loadVerificationKey(); - } - - 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 - 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); - // Zero morph Commitments - for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { - // Explicitly stating the x0, x1, y0, y1 start and end boundaries to make the calldata slicing bearable - 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.zmCqs[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 * 0x80); - - p.zmCq = 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])) - }); - - p.zmPi = Honk.G1ProofPoint({ - x_0 : uint256(bytes32(proof [boundary + 0x80:boundary + 0xa0])), - x_1 : uint256(bytes32(proof [boundary + 0xa0:boundary + 0xc0])), - y_0 : uint256(bytes32(proof [boundary + 0xc0:boundary + 0xe0])), - y_1 : uint256(bytes32(proof [boundary + 0xe0:boundary + 0x100])) - }); - - return p; - } - - 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 < NUMBER_OF_PUBLIC_INPUTS; 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 = 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) - { - Fr[7] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000002d0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff89), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000030), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffdd), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000030), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff89), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000002d0) - ]; - - Fr[7] memory BARYCENTRIC_DOMAIN = - [ Fr.wrap(0x00), Fr.wrap(0x01), Fr.wrap(0x02), Fr.wrap(0x03), Fr.wrap(0x04), Fr.wrap(0x05), Fr.wrap(0x06) ]; - // To compute the next target sum, we evaluate the given univariate at a point u (challenge). - - // 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 - 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 view returns(Fr newEvaluation) - { - Fr univariateEval = Fr.wrap(1) + (roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); - newEvaluation = currentEvaluation * univariateEval; - } - - // Calculate the contributions of each relation to the expected value of the full honk relation - // - // For each relation, we use the purported values ( the ones provided by the prover ) of the multivariates to - // calculate a contribution to the purported value of the full Honk relation. - // These are stored in the evaluations part of the proof object. - // We add these together, with the appropiate scaling factor ( the alphas calculated in challenges ) - // This value is checked against the final value of the target total sum - et voila! function accumulateRelationEvaluations(Honk.Proof memory proof, Transcript memory tp, Fr powPartialEval) - internal view returns(Fr accumulator) + internal + view + returns (Fr accumulator) { Fr[NUMBER_OF_ENTITIES] memory purportedEvaluations = proof.sumcheckEvaluations; Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; - // Accumulate all 6 custom gates - each with varying number of subrelations + // Accumulate all relations in Ultra Honk - each with varying number of subrelations accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); accumulatePermutationRelation(purportedEvaluations, tp, evaluations, powPartialEval); accumulateLogDerivativeLookupRelation(purportedEvaluations, tp, evaluations, powPartialEval); accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); accumulateAuxillaryRelation(purportedEvaluations, tp, evaluations, powPartialEval); - - // Apply alpha challenges to challenge evaluations - // Returns grand honk realtion evaluation + 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); } @@ -821,8 +542,10 @@ contract HonkVerifier is IVerifier * */ function accumulateArithmeticRelation( - Fr[NUMBER_OF_ENTITIES] memory p, Fr[NUMBER_OF_SUBRELATIONS] memory evals, Fr domainSep) internal view - { + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal view { // Relation 0 Fr q_arith = wire(p, WIRE.Q_ARITH); { @@ -850,9 +573,11 @@ contract HonkVerifier is IVerifier } function accumulatePermutationRelation( - Fr[NUMBER_OF_ENTITIES] memory p, Transcript memory tp, Fr[NUMBER_OF_SUBRELATIONS] memory evals, Fr domainSep) - internal view - { + Fr[NUMBER_OF_ENTITIES] memory p, + Transcript memory tp, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { Fr grand_product_numerator; Fr grand_product_denominator; @@ -877,8 +602,11 @@ contract HonkVerifier is IVerifier { Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; - acc = acc - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * tp.publicInputsDelta)) * - grand_product_denominator); + acc = acc + - ( + (wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * tp.publicInputsDelta)) + * grand_product_denominator + ); acc = acc * domainSep; evals[2] = acc; } @@ -899,8 +627,8 @@ contract HonkVerifier is IVerifier // Calculate the write term (the table accumulation) { - write_term = wire(p, WIRE.TABLE_1) + tp.gamma + (wire(p, WIRE.TABLE_2) * tp.eta) + - (wire(p, WIRE.TABLE_3) * tp.etaTwo) + (wire(p, WIRE.TABLE_4) * tp.etaThree); + write_term = wire(p, WIRE.TABLE_1) + tp.gamma + (wire(p, WIRE.TABLE_2) * tp.eta) + + (wire(p, WIRE.TABLE_3) * tp.etaTwo) + (wire(p, WIRE.TABLE_4) * tp.etaThree); } // Calculate the write term @@ -909,15 +637,15 @@ contract HonkVerifier is IVerifier Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); - read_term = derived_entry_1 + (derived_entry_2 * tp.eta) + (derived_entry_3 * tp.etaTwo) + - (wire(p, WIRE.Q_O) * tp.etaThree); + read_term = derived_entry_1 + (derived_entry_2 * tp.eta) + (derived_entry_3 * tp.etaTwo) + + (wire(p, WIRE.Q_O) * tp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) + - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); // Inverse calculated correctly relation Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; @@ -931,8 +659,10 @@ contract HonkVerifier is IVerifier } function accumulateDeltaRangeRelation( - Fr[NUMBER_OF_ENTITIES] memory p, Fr[NUMBER_OF_SUBRELATIONS] memory evals, Fr domainSep) internal view - { + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) 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); @@ -1070,7 +800,6 @@ contract HonkVerifier is IVerifier // Constants for the auxiliary relation Fr constant LIMB_SIZE = Fr.wrap(uint256(1) << 68); Fr constant SUBLIMB_SHIFT = Fr.wrap(uint256(1) << 14); - Fr constant MINUS_ONE = Fr.wrap(P - 1); // Parameters used within the Auxiliary Relation // A struct is used to work around stack too deep. This relation has alot of variables @@ -1272,7 +1001,6 @@ contract HonkVerifier is IVerifier 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 - // 1 - ((w3' * eta + w2') * eta + w1') * eta // 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); @@ -1335,19 +1063,440 @@ contract HonkVerifier is IVerifier evals[12] = ap.auxiliary_identity; } - function scaleAndBatchSubrelations(Fr[NUMBER_OF_SUBRELATIONS] memory evaluations, - Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges) - internal view returns(Fr accumulator) - { + struct PoseidonExternalParams { + Fr s1; + Fr s2; + Fr s3; + Fr s4; + Fr u1; + Fr u2; + Fr u3; + Fr u4; + Fr t0; + Fr t1; + Fr t2; + Fr t3; + Fr v1; + Fr v2; + Fr v3; + Fr v4; + Fr q_pos_by_scaling; + } + + 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 { + PoseidonExternalParams memory ep; + + ep.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L); + ep.s2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_R); + ep.s3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_O); + ep.s4 = wire(p, WIRE.W_4) + wire(p, WIRE.Q_4); + + ep.u1 = ep.s1 * ep.s1 * ep.s1 * ep.s1 * ep.s1; + ep.u2 = ep.s2 * ep.s2 * ep.s2 * ep.s2 * ep.s2; + ep.u3 = ep.s3 * ep.s3 * ep.s3 * ep.s3 * ep.s3; + ep.u4 = ep.s4 * ep.s4 * ep.s4 * ep.s4 * ep.s4; + // matrix mul v = M_E * u with 14 additions + ep.t0 = ep.u1 + ep.u2; // u_1 + u_2 + ep.t1 = ep.u3 + ep.u4; // u_3 + u_4 + ep.t2 = ep.u2 + ep.u2 + ep.t1; // 2u_2 + // ep.t2 += ep.t1; // 2u_2 + u_3 + u_4 + ep.t3 = ep.u4 + ep.u4 + ep.t0; // 2u_4 + // ep.t3 += ep.t0; // u_1 + u_2 + 2u_4 + ep.v4 = ep.t1 + ep.t1; + ep.v4 = ep.v4 + ep.v4 + ep.t3; + // ep.v4 += ep.t3; // u_1 + u_2 + 4u_3 + 6u_4 + ep.v2 = ep.t0 + ep.t0; + ep.v2 = ep.v2 + ep.v2 + ep.t2; + // ep.v2 += ep.t2; // 4u_1 + 6u_2 + u_3 + u_4 + ep.v1 = ep.t3 + ep.v2; // 5u_1 + 7u_2 + u_3 + 3u_4 + ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 + + ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; + evals[18] = evals[18] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + + evals[19] = evals[19] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + + evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + + evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + } + + struct PoseidonInternalParams { + Fr u1; + Fr u2; + Fr u3; + Fr u4; + Fr u_sum; + Fr v1; + Fr v2; + Fr v3; + Fr v4; + Fr s1; + Fr q_pos_by_scaling; + } + + 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 // i guess this is the scaling factor? + ) internal pure { + PoseidonInternalParams memory ip; + Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ + FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), + FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), + FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), + FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) + ]; + + // add round constants + ip.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L); + + // apply s-box round + ip.u1 = ip.s1 * ip.s1 * ip.s1 * ip.s1 * ip.s1; + ip.u2 = wire(p, WIRE.W_R); + ip.u3 = wire(p, WIRE.W_O); + ip.u4 = wire(p, WIRE.W_4); + + // matrix mul with v = M_I * u 4 muls and 7 additions + ip.u_sum = ip.u1 + ip.u2 + ip.u3 + ip.u4; + + ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; + + ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; + evals[22] = evals[22] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + + ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; + evals[23] = evals[23] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + + ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; + evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + + ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; + evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + } + + function scaleAndBatchSubrelations( + Fr[NUMBER_OF_SUBRELATIONS] memory evaluations, + Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges + ) internal pure returns (Fr accumulator) { accumulator = accumulator + evaluations[0]; for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; } } +} + +// Errors +error PublicInputsLengthWrong(); +error SumcheckFailed(); +error ZeromorphFailed(); + +interface IVerifier { + function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool); +} + +// Smart contract verifier of honk proofs +contract HonkVerifier is IVerifier +{ + + function verify(bytes calldata proof, bytes32[] calldata publicInputs) public view override returns (bool) { + Honk.VerificationKey memory vk = loadVerificationKey(); + Honk.Proof memory p = loadProof(proof); + + if (publicInputs.length != vk.publicInputsSize) { + revert PublicInputsLengthWrong(); + } + + // Generate the fiat shamir challenges for the whole protocol + Transcript memory t = TranscriptLib.generateTranscript(p, vk, publicInputs); + + // 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(); + // Zeromorph + bool zeromorphVerified = verifyZeroMorph(p, vk, t); + if (!zeromorphVerified) revert ZeromorphFailed(); + + return sumcheckVerified && zeromorphVerified; // Boolean condition not required - nice for vanity :) + } + + 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); + // Zero morph Commitments + for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { + // Explicitly stating the x0, x1, y0, y1 start and end boundaries to make the calldata slicing bearable + 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.zmCqs[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 * 0x80); + + p.zmCq = 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])) + }); + + p.zmPi = Honk.G1ProofPoint({ + x_0: uint256(bytes32(proof[boundary + 0x80:boundary + 0xa0])), + x_1: uint256(bytes32(proof[boundary + 0xa0:boundary + 0xc0])), + y_0: uint256(bytes32(proof[boundary + 0xc0:boundary + 0xe0])), + y_1: uint256(bytes32(proof[boundary + 0xe0:boundary + 0x100])) + }); + + return p; + } + + 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 < NUMBER_OF_PUBLIC_INPUTS; 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; + } function verifyZeroMorph(Honk.Proof memory proof, Honk.VerificationKey memory vk, Transcript memory tp) - internal view returns(bool verified) + internal + view + returns (bool verified) { // Construct batched evaluation v = sum_{i=0}^{m-1}\rho^i*f_i(u) + sum_{i=0}^{l-1}\rho^{m+i}*h_i(u) Fr batchedEval = Fr.wrap(0); @@ -1370,8 +1519,7 @@ contract HonkVerifier is IVerifier } // Compute commitment to lifted degree quotient identity - function computeCZeta(Honk.Proof memory proof, Transcript memory tp) internal view returns(Honk.G1Point memory) - { + function computeCZeta(Honk.Proof memory proof, Transcript memory tp) internal view returns (Honk.G1Point memory) { Fr[LOG_N + 1] memory scalars; Honk.G1ProofPoint[LOG_N + 1] memory commitments; @@ -1379,6 +1527,7 @@ contract HonkVerifier is IVerifier commitments[0] = proof.zmCq; scalars[0] = Fr.wrap(1); + // TODO: optimize pow operations here ? batch mulable for (uint256 k = 0; k < LOG_N; ++k) { Fr degree = Fr.wrap((1 << k) - 1); Fr scalar = FrLib.pow(tp.zmY, k); @@ -1405,10 +1554,12 @@ contract HonkVerifier is IVerifier Fr x_pow_2kp1; } - function - computeCZetaX(Honk.Proof memory proof, Honk.VerificationKey memory vk, Transcript memory tp, Fr batchedEval) - internal view returns(Honk.G1Point memory) - { + function computeCZetaX( + Honk.Proof memory proof, + Honk.VerificationKey memory vk, + Transcript memory tp, + Fr batchedEval + ) internal view returns (Honk.G1Point memory) { Fr[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 1] memory scalars; Honk.G1Point[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 1] memory commitments; CZetaXParams memory cp; @@ -1420,19 +1571,19 @@ contract HonkVerifier is IVerifier // Add contribution: -v * x * \Phi_n(x) * [1]_1 // Add base scalars[0] = MINUS_ONE * batchedEval * tp.zmX * cp.phi_n_x; - commitments[0] = Honk.G1Point({ x : 1, y : 2 }); // One + commitments[0] = Honk.G1Point({x: 1, y: 2}); // One // f - Add all unshifted commitments // g - Add add to be shifted commitments // f commitments are accumulated at (zm_x * r) cp.rho_pow = Fr.wrap(1); - for (uint256 i = 1; i < 34; ++i) { + for (uint256 i = 1; i <= NUMBER_UNSHIFTED; ++i) { scalars[i] = tp.zmX * cp.rho_pow; cp.rho_pow = cp.rho_pow * tp.rho; } // g commitments are accumulated at r - for (uint256 i = 34; i < 43; ++i) { + for (uint256 i = NUMBER_UNSHIFTED + 1; i <= NUMBER_OF_ENTITIES; ++i) { scalars[i] = cp.rho_pow; cp.rho_pow = cp.rho_pow * tp.rho; } @@ -1448,41 +1599,43 @@ contract HonkVerifier is IVerifier commitments[9] = vk.qElliptic; commitments[10] = vk.qAux; commitments[11] = vk.qLookup; - commitments[12] = vk.s1; - commitments[13] = vk.s2; - commitments[14] = vk.s3; - commitments[15] = vk.s4; - commitments[16] = vk.id1; - commitments[17] = vk.id2; - commitments[18] = vk.id3; - commitments[19] = vk.id4; - commitments[20] = vk.t1; - commitments[21] = vk.t2; - commitments[22] = vk.t3; - commitments[23] = vk.t4; - commitments[24] = vk.lagrangeFirst; - commitments[25] = vk.lagrangeLast; + 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[26] = convertProofPoint(proof.w1); - commitments[27] = convertProofPoint(proof.w2); - commitments[28] = convertProofPoint(proof.w3); - commitments[29] = convertProofPoint(proof.w4); - commitments[30] = convertProofPoint(proof.zPerm); - commitments[31] = convertProofPoint(proof.lookupInverses); - commitments[32] = convertProofPoint(proof.lookupReadCounts); - commitments[33] = convertProofPoint(proof.lookupReadTags); + 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[34] = vk.t1; - commitments[35] = vk.t2; - commitments[36] = vk.t3; - commitments[37] = vk.t4; - commitments[38] = convertProofPoint(proof.w1); - commitments[39] = convertProofPoint(proof.w2); - commitments[40] = convertProofPoint(proof.w3); - commitments[41] = convertProofPoint(proof.w4); - commitments[42] = convertProofPoint(proof.zPerm); + 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); // Add scalar contributions // Add contributions: scalar * [q_k], k = 0,...,log_N, where @@ -1507,8 +1660,8 @@ contract HonkVerifier is IVerifier cp.x_pow_2kp1 = cp.x_pow_2kp1 * cp.x_pow_2kp1; } - scalars[43 + k] = scalar; - commitments[43 + k] = convertProofPoint(proof.zmCqs[k]); + scalars[NUMBER_OF_ENTITIES + 1 + k] = scalar; + commitments[NUMBER_OF_ENTITIES + 1 + k] = convertProofPoint(proof.zmCqs[k]); } return batchMul2(commitments, scalars); @@ -1516,11 +1669,12 @@ contract HonkVerifier is IVerifier // 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) + internal + view + returns (Honk.G1Point memory result) { uint256 limit = LOG_N + 1; - assembly - { + assembly { let success := 0x01 let free := mload(0x40) @@ -1534,9 +1688,10 @@ contract HonkVerifier is IVerifier 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 base_base := add(base, mul(count, 0x20)) let scalar_base := add(scalars, mul(count, 0x20)) mstore(add(free, 0x40), mload(mload(base_base))) @@ -1544,22 +1699,22 @@ contract HonkVerifier is IVerifier // 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(), 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))) + 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) - { + 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 - { + assembly { let success := 0x01 let free := mload(0x40) @@ -1573,9 +1728,9 @@ contract HonkVerifier is IVerifier success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, free, 0x40)) let count := 0x01 - for {} lt(count, limit){ count := add(count, 1) } { + for {} lt(count, limit) { count := add(count, 1) } { // Get loop offsets - let base_base := add(base, mul(count, 0x20)) + let base_base := add(base, mul(count, 0x20)) let scalar_base := add(scalars, mul(count, 0x20)) mstore(add(free, 0x40), mload(mload(base_base))) @@ -1584,21 +1739,24 @@ contract HonkVerifier is IVerifier 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 + // 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))) + mstore(result, mload(free)) + mstore(add(result, 0x20), mload(add(free, 0x20))) } } function zkgReduceVerify( - Honk.Proof memory proof, Transcript memory tp, Fr evaluation, Honk.G1Point memory commitment) - internal view returns(bool) - { + 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 ONE = Honk.G1Point({x: 1, y: 2}); Honk.G1Point memory P0 = commitment; P0 = ecAdd(P0, ecMul(quotient_commitment, tp.zmX)); @@ -1636,8 +1794,9 @@ contract HonkVerifier is IVerifier } // 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) +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/cpp/src/barretenberg/execution_trace/execution_trace.cpp b/barretenberg/cpp/src/barretenberg/execution_trace/execution_trace.cpp index 3b861421b47..12d64d4eda3 100644 --- a/barretenberg/cpp/src/barretenberg/execution_trace/execution_trace.cpp +++ b/barretenberg/cpp/src/barretenberg/execution_trace/execution_trace.cpp @@ -3,7 +3,7 @@ #include "barretenberg/plonk/proof_system/proving_key/proving_key.hpp" #include "barretenberg/stdlib_circuit_builders/mega_flavor.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_flavor.hpp" -#include "barretenberg/stdlib_circuit_builders/ultra_keccak.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_keccak_flavor.hpp" namespace bb { template @@ -64,17 +64,7 @@ typename ExecutionTrace_::TraceData ExecutionTrace_::construct_t uint32_t offset = Flavor::has_zero_row ? 1 : 0; // Offset at which to place each block in the trace polynomials // For each block in the trace, populate wire polys, copy cycles and selector polys - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1078): remove when Keccak flavor works with Poseidon - // gate - auto get_blocks = [&]() { - if constexpr (!HasKeccak) { - return builder.blocks.get(); - } else { - return builder.blocks.get_for_ultra_keccak(); - } - }; - - for (auto& block : get_blocks()) { + for (auto& block : builder.blocks.get()) { auto block_size = static_cast(block.size()); // Update wire polynomials and copy cycles @@ -96,7 +86,7 @@ typename ExecutionTrace_::TraceData ExecutionTrace_::construct_t // Insert the selector values for this block into the selector polynomials at the correct offset // TODO(https://github.com/AztecProtocol/barretenberg/issues/398): implicit arithmetization/flavor consistency - for (size_t selector_idx = 0; selector_idx < NUM_USED_SELECTORS; selector_idx++) { + for (size_t selector_idx = 0; selector_idx < NUM_SELECTORS; selector_idx++) { auto selector_poly = trace_data.selectors[selector_idx]; auto selector = block.selectors[selector_idx]; for (size_t row_idx = 0; row_idx < block_size; ++row_idx) { diff --git a/barretenberg/cpp/src/barretenberg/execution_trace/execution_trace.hpp b/barretenberg/cpp/src/barretenberg/execution_trace/execution_trace.hpp index 752ef633ebc..502ab6d2689 100644 --- a/barretenberg/cpp/src/barretenberg/execution_trace/execution_trace.hpp +++ b/barretenberg/cpp/src/barretenberg/execution_trace/execution_trace.hpp @@ -16,14 +16,11 @@ template class ExecutionTrace_ { public: static constexpr size_t NUM_WIRES = Builder::NUM_WIRES; - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1078): Since Keccak doesn't have knowledge of Poseidon2 - // gate yet, we ignore the two related selectors - static constexpr size_t NUM_USED_SELECTORS = - !HasKeccak ? Builder::Arithmetization::NUM_SELECTORS : Builder::Arithmetization::NUM_SELECTORS - 2; + static constexpr size_t NUM_SELECTORS = Builder::Arithmetization::NUM_SELECTORS; struct TraceData { std::array wires; - std::array selectors; + std::array selectors; // A vector of sets (vectors) of addresses into the wire polynomials whose values are copy constrained std::vector copy_cycles; uint32_t ram_rom_offset = 0; // offset of the RAM/ROM block in the execution trace @@ -48,10 +45,13 @@ template class ExecutionTrace_ { std::string wire_tag = "w_" + std::to_string(idx + 1) + "_lagrange"; proving_key.polynomial_store.put(wire_tag, wires[idx].share()); } - for (size_t idx = 0; idx < Builder::Arithmetization::NUM_SELECTORS; ++idx) { - selectors[idx] = Polynomial(proving_key.circuit_size); - std::string selector_tag = builder.selector_names[idx] + "_lagrange"; - proving_key.polynomial_store.put(selector_tag, selectors[idx].share()); + { + ZoneScopedN("selector initialization"); + for (size_t idx = 0; idx < Builder::Arithmetization::NUM_SELECTORS; ++idx) { + selectors[idx] = Polynomial(proving_key.circuit_size); + std::string selector_tag = builder.selector_names[idx] + "_lagrange"; + proving_key.polynomial_store.put(selector_tag, selectors[idx].share()); + } } } { diff --git a/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp index 98be3e69610..9a61d1b7a88 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp @@ -375,9 +375,6 @@ concept IsHonkFlavor = IsAnyOf concept IsUltraFlavor = IsAnyOf; -template -concept HasKeccak = IsAnyOf; - template concept IsGoblinFlavor = IsAnyOf, diff --git a/barretenberg/cpp/src/barretenberg/honk/utils/honk_key_gen.hpp b/barretenberg/cpp/src/barretenberg/honk/utils/honk_key_gen.hpp index be1e37b4218..8f6b6a0a26c 100644 --- a/barretenberg/cpp/src/barretenberg/honk/utils/honk_key_gen.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/utils/honk_key_gen.hpp @@ -79,6 +79,8 @@ inline void output_vk_sol_ultra_honk(std::ostream& os, print_g1(key->q_elliptic, "qElliptic"); print_g1(key->q_aux, "qAux"); print_g1(key->q_lookup, "qLookup"); + print_g1(key->q_poseidon2_external, "qPoseidon2External"); + print_g1(key->q_poseidon2_internal, "qPoseidon2Internal"); print_g1(key->sigma_1, "s1"); print_g1(key->sigma_2, "s2"); print_g1(key->sigma_3, "s3"); diff --git a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/ultra_arithmetization.hpp b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/ultra_arithmetization.hpp index be4ca5b5fcb..52f5ff80a2f 100644 --- a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/ultra_arithmetization.hpp +++ b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/ultra_arithmetization.hpp @@ -30,8 +30,6 @@ template class UltraArith { aux, lookup, poseidon_external, poseidon_internal }; } - auto get_for_ultra_keccak() { return RefArray{ pub_inputs, arithmetic, delta_range, elliptic, aux, lookup }; } - bool operator==(const UltraTraceBlocks& other) const = default; }; @@ -125,12 +123,6 @@ template class UltraArith { this->aux, this->lookup, this->poseidon_external, this->poseidon_internal }; } - auto get_for_ultra_keccak() - { - return RefArray{ this->pub_inputs, this->arithmetic, this->delta_range, - this->elliptic, this->aux, this->lookup }; - } - void summarize() const { info("Gate blocks summary:"); diff --git a/barretenberg/cpp/src/barretenberg/srs/factories/file_crs_factory.cpp b/barretenberg/cpp/src/barretenberg/srs/factories/file_crs_factory.cpp index 16af14fec8b..eb37760417b 100644 --- a/barretenberg/cpp/src/barretenberg/srs/factories/file_crs_factory.cpp +++ b/barretenberg/cpp/src/barretenberg/srs/factories/file_crs_factory.cpp @@ -59,7 +59,7 @@ std::shared_ptr> FileCrsFactory::get if (prover_degree_ < degree || !prover_crs_) { prover_crs_ = std::make_shared>(degree, path_); prover_degree_ = degree; - info("Initializing ", Curve::name, " prover CRS from file of size ", degree); + vinfo("Initializing ", Curve::name, " prover CRS from file of size ", degree); } return prover_crs_; } @@ -70,7 +70,7 @@ std::shared_ptr> FileCrsFactory::g if (verifier_degree_ < degree || !verifier_crs_) { verifier_crs_ = std::make_shared>(path_, degree); verifier_degree_ = degree; - info("Initializing ", Curve::name, " verifier CRS from file of size ", degree); + vinfo("Initializing ", Curve::name, " verifier CRS from file of size ", degree); } return verifier_crs_; } diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_keccak.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_keccak_flavor.hpp similarity index 90% rename from barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_keccak.hpp rename to barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_keccak_flavor.hpp index 4e9e8fe67af..5ffa02d78fa 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_keccak.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_keccak_flavor.hpp @@ -14,6 +14,8 @@ #include "barretenberg/relations/elliptic_relation.hpp" #include "barretenberg/relations/logderiv_lookup_relation.hpp" #include "barretenberg/relations/permutation_relation.hpp" +#include "barretenberg/relations/poseidon2_external_relation.hpp" +#include "barretenberg/relations/poseidon2_internal_relation.hpp" #include "barretenberg/relations/relation_parameters.hpp" #include "barretenberg/relations/ultra_arithmetic_relation.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" @@ -21,7 +23,6 @@ namespace bb { -// TODO(https://github.com/AztecProtocol/barretenberg/issues/1078): Update flavor to support Poseidon relation. class UltraKeccakFlavor { public: using CircuitBuilder = UltraCircuitBuilder; @@ -39,10 +40,10 @@ class UltraKeccakFlavor { static constexpr size_t NUM_WIRES = CircuitBuilder::NUM_WIRES; // The number of multivariate polynomials on which a sumcheck prover sumcheck operates (including shifts). We often // need containers of this size to hold related data, so we choose a name more agnostic than `NUM_POLYNOMIALS`. - static constexpr size_t NUM_ALL_ENTITIES = 42; + static constexpr size_t NUM_ALL_ENTITIES = 44; // The number of polynomials precomputed to describe a circuit and to aid a prover in constructing a satisfying // assignment of witnesses. We again choose a neutral name. - static constexpr size_t NUM_PRECOMPUTED_ENTITIES = 25; + static constexpr size_t NUM_PRECOMPUTED_ENTITIES = 27; // The total number of witness entities not including shifts. static constexpr size_t NUM_WITNESS_ENTITIES = 8; // The total number of witnesses including shifts and derived entities. @@ -59,11 +60,14 @@ class UltraKeccakFlavor { bb::LogDerivLookupRelation, bb::DeltaRangeConstraintRelation, bb::EllipticRelation, - bb::AuxiliaryRelation>; + bb::AuxiliaryRelation, + bb::Poseidon2ExternalRelation, + bb::Poseidon2InternalRelation>; + using Relations = Relations_; static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); - static_assert(MAX_PARTIAL_RELATION_LENGTH == 6); + static_assert(MAX_PARTIAL_RELATION_LENGTH == 7); static constexpr size_t MAX_TOTAL_RELATION_LENGTH = compute_max_total_relation_length(); static_assert(MAX_TOTAL_RELATION_LENGTH == 11); static constexpr size_t NUM_SUBRELATIONS = compute_number_of_subrelations(); @@ -102,37 +106,51 @@ class UltraKeccakFlavor { public: using DataType = DataType_; DEFINE_FLAVOR_MEMBERS(DataType, - q_m, // column 0 - q_c, // column 1 - q_l, // column 2 - q_r, // column 3 - q_o, // column 4 - q_4, // column 5 - q_arith, // column 6 - q_delta_range, // column 7 - q_elliptic, // column 8 - q_aux, // column 9 - q_lookup, // column 10 - sigma_1, // column 11 - sigma_2, // column 12 - sigma_3, // column 13 - sigma_4, // column 14 - id_1, // column 15 - id_2, // column 16 - id_3, // column 17 - id_4, // column 18 - table_1, // column 19 - table_2, // column 20 - table_3, // column 21 - table_4, // column 22 - lagrange_first, // column 23 - lagrange_last) // column 24 + q_m, // column 0 + q_c, // column 1 + q_l, // column 2 + q_r, // column 3 + q_o, // column 4 + q_4, // column 5 + q_arith, // column 6 + q_delta_range, // column 7 + q_elliptic, // column 8 + q_aux, // column 9 + q_lookup, // column 10 + q_poseidon2_external, // column 11 + q_poseidon2_internal, // column 12 + sigma_1, // column 13 + sigma_2, // column 14 + sigma_3, // column 15 + sigma_4, // column 16 + id_1, // column 17 + id_2, // column 18 + id_3, // column 19 + id_4, // column 20 + table_1, // column 21 + table_2, // column 22 + table_3, // column 23 + table_4, // column 24 + lagrange_first, // column 25 + lagrange_last) // column 26 static constexpr CircuitType CIRCUIT_TYPE = CircuitBuilder::CIRCUIT_TYPE; auto get_selectors() { - return RefArray{ q_m, q_c, q_l, q_r, q_o, q_4, q_arith, q_delta_range, q_elliptic, q_aux, q_lookup }; + return RefArray{ q_m, + q_c, + q_l, + q_r, + q_o, + q_4, + q_arith, + q_delta_range, + q_elliptic, + q_aux, + q_lookup, + q_poseidon2_external, + q_poseidon2_internal }; }; auto get_sigma_polynomials() { return RefArray{ sigma_1, sigma_2, sigma_3, sigma_4 }; }; auto get_id_polynomials() { return RefArray{ id_1, id_2, id_3, id_4 }; }; @@ -397,6 +415,8 @@ class UltraKeccakFlavor { const Commitment& q_elliptic, const Commitment& q_aux, const Commitment& q_lookup, + const Commitment& q_poseidon2_external, + const Commitment& q_poseidon2_internal, const Commitment& sigma_1, const Commitment& sigma_2, const Commitment& sigma_3, @@ -427,6 +447,8 @@ class UltraKeccakFlavor { this->q_elliptic = q_elliptic; this->q_aux = q_aux; this->q_lookup = q_lookup; + this->q_poseidon2_external = q_poseidon2_external; + this->q_poseidon2_internal = q_poseidon2_internal; this->sigma_1 = sigma_1; this->sigma_2 = sigma_2; this->sigma_3 = sigma_3; @@ -459,6 +481,8 @@ class UltraKeccakFlavor { q_elliptic, q_aux, q_lookup, + q_poseidon2_external, + q_poseidon2_internal, sigma_1, sigma_2, sigma_3, @@ -544,6 +568,8 @@ class UltraKeccakFlavor { q_elliptic = "Q_ELLIPTIC"; q_aux = "Q_AUX"; q_lookup = "Q_LOOKUP"; + q_poseidon2_external = "Q_POSEIDON2_EXTERNAL"; + q_poseidon2_internal = "Q_POSEIDON2_INTERNAL"; sigma_1 = "SIGMA_1"; sigma_2 = "SIGMA_2"; sigma_3 = "SIGMA_3"; @@ -583,6 +609,8 @@ class UltraKeccakFlavor { this->q_elliptic = verification_key->q_elliptic; this->q_aux = verification_key->q_aux; this->q_lookup = verification_key->q_lookup; + this->q_poseidon2_external = verification_key->q_poseidon2_external; + this->q_poseidon2_internal = verification_key->q_poseidon2_internal; this->sigma_1 = verification_key->sigma_1; this->sigma_2 = verification_key->sigma_2; this->sigma_3 = verification_key->sigma_3; diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.hpp b/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.hpp index 7ef81472de5..70daf2cf459 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.hpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.hpp @@ -8,7 +8,7 @@ #include "barretenberg/relations/relation_parameters.hpp" #include "barretenberg/stdlib_circuit_builders/mega_flavor.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_flavor.hpp" -#include "barretenberg/stdlib_circuit_builders/ultra_keccak.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_keccak_flavor.hpp" namespace bb { /** @@ -49,7 +49,6 @@ template class DeciderProvingKey_ { BB_OP_COUNT_TIME_NAME("DeciderProvingKey(Circuit&)"); circuit.add_gates_to_ensure_all_polys_are_non_zero(); circuit.finalize_circuit(); - info("finalized gate count: ", circuit.num_gates); // Set flag indicating whether the polynomials will be constructed with fixed block sizes for each gate type const bool is_structured = (trace_structure != TraceStructure::NONE); diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.hpp index e056f671084..f51ae3476c6 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.hpp @@ -21,7 +21,7 @@ #include "barretenberg/stdlib_circuit_builders/mega_flavor.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_flavor.hpp" -#include "barretenberg/stdlib_circuit_builders/ultra_keccak.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_keccak_flavor.hpp" #include "barretenberg/sumcheck/instance/prover_instance.hpp" #include "barretenberg/transcript/transcript.hpp" diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_verifier.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_verifier.hpp index 5920d7b0b40..d4d2925cff3 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_verifier.hpp @@ -5,7 +5,7 @@ #include "barretenberg/relations/relation_parameters.hpp" #include "barretenberg/stdlib_circuit_builders/mega_flavor.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_flavor.hpp" -#include "barretenberg/stdlib_circuit_builders/ultra_keccak.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_keccak_flavor.hpp" #include "barretenberg/sumcheck/instance/verifier_instance.hpp" namespace bb { diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_honk.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_honk.test.cpp index be4d5979709..59f8a6e1936 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_honk.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_honk.test.cpp @@ -16,45 +16,40 @@ using namespace bb; -using DeciderProvingKey = DeciderProvingKey_; -using VerificationKey = UltraFlavor::VerificationKey; - -std::vector add_variables(auto& circuit_builder, std::vector variables) -{ - std::vector res; - for (size_t i = 0; i < variables.size(); i++) { - res.emplace_back(circuit_builder.add_variable(variables[i])); +template class UltraHonkTests : public ::testing::Test { + public: + using DeciderProvingKey = DeciderProvingKey_; + using VerificationKey = typename Flavor::VerificationKey; + using Prover = UltraProver_; + using Verifier = UltraVerifier_; + + std::vector add_variables(auto& circuit_builder, std::vector variables) + { + std::vector res; + for (auto& variable : variables) { + res.emplace_back(circuit_builder.add_variable(variable)); + } + return res; } - return res; -} -void prove_and_verify(auto& circuit_builder, bool expected_result) -{ - auto proving_key = std::make_shared(circuit_builder); - UltraProver prover(proving_key); - auto verification_key = std::make_shared(proving_key->proving_key); - UltraVerifier verifier(verification_key); - auto proof = prover.construct_proof(); - bool verified = verifier.verify_proof(proof); - EXPECT_EQ(verified, expected_result); -}; - -void ensure_non_zero(auto& polynomial) -{ - bool has_non_zero_coefficient = false; - for (auto& coeff : polynomial) { - has_non_zero_coefficient |= !coeff.is_zero(); - } - ASSERT_TRUE(has_non_zero_coefficient); -} + void prove_and_verify(auto& circuit_builder, bool expected_result) + { + auto proving_key = std::make_shared(circuit_builder); + Prover prover(proving_key); + auto verification_key = std::make_shared(proving_key->proving_key); + Verifier verifier(verification_key); + auto proof = prover.construct_proof(); + bool verified = verifier.verify_proof(proof); + EXPECT_EQ(verified, expected_result); + }; -// TODO(https://github.com/AztecProtocol/barretenberg/issues/1075): Make the tests run on UltraKeccakFlavor as well so -// we have a means of checking issues without having to run the Solidity verifier contract. -class UltraHonkTests : public ::testing::Test { protected: static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); } }; +using FlavorTypes = testing::Types; +TYPED_TEST_SUITE(UltraHonkTests, FlavorTypes); + /** * @brief A quick test to ensure that none of our polynomials are identically zero * @@ -62,15 +57,23 @@ class UltraHonkTests : public ::testing::Test { * to achieve non-zero polynomials * */ -TEST_F(UltraHonkTests, ANonZeroPolynomialIsAGoodPolynomial) +TYPED_TEST(UltraHonkTests, ANonZeroPolynomialIsAGoodPolynomial) { auto circuit_builder = UltraCircuitBuilder(); - auto proving_key = std::make_shared(circuit_builder); - UltraProver prover(proving_key); + auto proving_key = std::make_shared(circuit_builder); + typename TestFixture::Prover prover(proving_key); auto proof = prover.construct_proof(); auto& polynomials = proving_key->proving_key.polynomials; + auto ensure_non_zero = [](auto& polynomial) { + bool has_non_zero_coefficient = false; + for (auto& coeff : polynomial) { + has_non_zero_coefficient |= !coeff.is_zero(); + } + ASSERT_TRUE(has_non_zero_coefficient); + }; + for (auto& poly : polynomials.get_selectors()) { ensure_non_zero(poly); } @@ -88,7 +91,7 @@ TEST_F(UltraHonkTests, ANonZeroPolynomialIsAGoodPolynomial) * @brief Test proof construction/verification for a structured execution trace * */ -TEST_F(UltraHonkTests, StructuredTrace) +TYPED_TEST(UltraHonkTests, StructuredTrace) { auto builder = UltraCircuitBuilder(); size_t num_gates = 3; @@ -98,10 +101,10 @@ TEST_F(UltraHonkTests, StructuredTrace) // Construct an proving_key with a structured execution trace TraceStructure trace_structure = TraceStructure::SMALL_TEST; - auto proving_key = std::make_shared(builder, trace_structure); - UltraProver prover(proving_key); - auto verification_key = std::make_shared(proving_key->proving_key); - UltraVerifier verifier(verification_key); + auto proving_key = std::make_shared(builder, trace_structure); + typename TestFixture::Prover prover(proving_key); + auto verification_key = std::make_shared(proving_key->proving_key); + typename TestFixture::Verifier verifier(verification_key); auto proof = prover.construct_proof(); EXPECT_TRUE(verifier.verify_proof(proof)); } @@ -110,7 +113,7 @@ TEST_F(UltraHonkTests, StructuredTrace) * @brief Test simple circuit with public inputs * */ -TEST_F(UltraHonkTests, PublicInputs) +TYPED_TEST(UltraHonkTests, PublicInputs) { auto builder = UltraCircuitBuilder(); size_t num_gates = 10; @@ -118,10 +121,10 @@ TEST_F(UltraHonkTests, PublicInputs) // Add some arbitrary arithmetic gates that utilize public inputs MockCircuits::add_arithmetic_gates_with_public_inputs(builder, num_gates); - prove_and_verify(builder, /*expected_result=*/true); + TestFixture::prove_and_verify(builder, /*expected_result=*/true); } -TEST_F(UltraHonkTests, XorConstraint) +TYPED_TEST(UltraHonkTests, XorConstraint) { auto circuit_builder = UltraCircuitBuilder(); @@ -145,10 +148,10 @@ TEST_F(UltraHonkTests, XorConstraint) circuit_builder.create_gates_from_plookup_accumulators( plookup::MultiTableId::UINT32_XOR, lookup_accumulators, left_witness_index, right_witness_index); - prove_and_verify(circuit_builder, /*expected_result=*/true); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkTests, create_gates_from_plookup_accumulators) +TYPED_TEST(UltraHonkTests, create_gates_from_plookup_accumulators) { auto circuit_builder = UltraCircuitBuilder(); @@ -205,15 +208,17 @@ TEST_F(UltraHonkTests, create_gates_from_plookup_accumulators) } } - prove_and_verify(circuit_builder, /*expected_result=*/true); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true); } /** * @brief Test various failure modes for the lookup relation via bad input polynomials * */ -TEST_F(UltraHonkTests, LookupFailure) +TYPED_TEST(UltraHonkTests, LookupFailure) { + using DeciderProvingKey = typename TestFixture::DeciderProvingKey; + using VerificationKey = typename TestFixture::VerificationKey; // Construct a circuit with lookup and arithmetic gates auto construct_circuit_with_lookups = []() { UltraCircuitBuilder builder; @@ -225,9 +230,9 @@ TEST_F(UltraHonkTests, LookupFailure) }; auto prove_and_verify = [](auto& proving_key) { - UltraProver prover(proving_key); + typename TestFixture::Prover prover(proving_key); auto verification_key = std::make_shared(proving_key->proving_key); - UltraVerifier verifier(verification_key); + typename TestFixture::Verifier verifier(verification_key); auto proof = prover.construct_proof(); return verifier.verify_proof(proof); }; @@ -238,7 +243,7 @@ TEST_F(UltraHonkTests, LookupFailure) auto proving_key = std::make_shared(builder); - EXPECT_TRUE(prove_and_verify(proving_key)); + prove_and_verify(proving_key); } // Failure mode 1: bad read counts/tags @@ -250,9 +255,10 @@ TEST_F(UltraHonkTests, LookupFailure) // Erroneously update the read counts/tags at an arbitrary index // Note: updating only one or the other may not cause failure due to the design of the relation algebra. For - // example, the inverse is only computed if read tags is non-zero, otherwise the inverse at the row in question - // will be zero. So if read counts is incremented at some arbitrary index but read tags is not, the inverse will - // be 0 and the erroneous read_counts value will get multiplied by 0 in the relation. This is expected behavior. + // example, the inverse is only computed if read tags is non-zero, otherwise the inverse at the row in + // question will be zero. So if read counts is incremented at some arbitrary index but read tags is not, the + // inverse will be 0 and the erroneous read_counts value will get multiplied by 0 in the relation. This is + // expected behavior. polynomials.lookup_read_counts[25] = 1; polynomials.lookup_read_tags[25] = 1; @@ -292,7 +298,7 @@ TEST_F(UltraHonkTests, LookupFailure) } } -TEST_F(UltraHonkTests, test_no_lookup_proof) +TYPED_TEST(UltraHonkTests, test_no_lookup_proof) { auto circuit_builder = UltraCircuitBuilder(); @@ -311,10 +317,10 @@ TEST_F(UltraHonkTests, test_no_lookup_proof) } } - prove_and_verify(circuit_builder, /*expected_result=*/true); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkTests, test_elliptic_gate) +TYPED_TEST(UltraHonkTests, test_elliptic_gate) { typedef grumpkin::g1::affine_element affine_element; typedef grumpkin::g1::element element; @@ -344,10 +350,10 @@ TEST_F(UltraHonkTests, test_elliptic_gate) y3 = circuit_builder.add_variable(p3.y); circuit_builder.create_ecc_add_gate({ x1, y1, x2, y2, x3, y3, -1 }); - prove_and_verify(circuit_builder, /*expected_result=*/true); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkTests, non_trivial_tag_permutation) +TYPED_TEST(UltraHonkTests, non_trivial_tag_permutation) { auto circuit_builder = UltraCircuitBuilder(); fr a = fr::random_element(); @@ -371,10 +377,10 @@ TEST_F(UltraHonkTests, non_trivial_tag_permutation) circuit_builder.assign_tag(c_idx, 2); circuit_builder.assign_tag(d_idx, 2); - prove_and_verify(circuit_builder, /*expected_result=*/true); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkTests, non_trivial_tag_permutation_and_cycles) +TYPED_TEST(UltraHonkTests, non_trivial_tag_permutation_and_cycles) { auto circuit_builder = UltraCircuitBuilder(); fr a = fr::random_element(); @@ -408,10 +414,10 @@ TEST_F(UltraHonkTests, non_trivial_tag_permutation_and_cycles) circuit_builder.create_add_gate( { e_idx, f_idx, circuit_builder.zero_idx, fr::one(), -fr::one(), fr::zero(), fr::zero() }); - prove_and_verify(circuit_builder, /*expected_result=*/true); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkTests, bad_tag_permutation) +TYPED_TEST(UltraHonkTests, bad_tag_permutation) { { auto circuit_builder = UltraCircuitBuilder(); @@ -434,7 +440,7 @@ TEST_F(UltraHonkTests, bad_tag_permutation) circuit_builder.assign_tag(c_idx, 2); circuit_builder.assign_tag(d_idx, 2); - prove_and_verify(circuit_builder, /*expected_result=*/false); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false); } // Same as above but without tag creation to check reason of failure is really tag mismatch { @@ -450,11 +456,11 @@ TEST_F(UltraHonkTests, bad_tag_permutation) circuit_builder.create_add_gate({ a_idx, b_idx, circuit_builder.zero_idx, 1, 1, 0, 0 }); circuit_builder.create_add_gate({ c_idx, d_idx, circuit_builder.zero_idx, 1, 1, 0, -1 }); - prove_and_verify(circuit_builder, /*expected_result=*/true); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true); } } -TEST_F(UltraHonkTests, sort_widget) +TYPED_TEST(UltraHonkTests, sort_widget) { auto circuit_builder = UltraCircuitBuilder(); fr a = fr::one(); @@ -468,10 +474,10 @@ TEST_F(UltraHonkTests, sort_widget) auto d_idx = circuit_builder.add_variable(d); circuit_builder.create_sort_constraint({ a_idx, b_idx, c_idx, d_idx }); - prove_and_verify(circuit_builder, /*expected_result=*/true); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkTests, sort_with_edges_gate) +TYPED_TEST(UltraHonkTests, sort_with_edges_gate) { fr a = fr::one(); fr b = fr(2); @@ -495,7 +501,7 @@ TEST_F(UltraHonkTests, sort_with_edges_gate) circuit_builder.create_sort_constraint_with_edges( { a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, a, h); - prove_and_verify(circuit_builder, /*expected_result=*/true); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true); } { @@ -511,7 +517,7 @@ TEST_F(UltraHonkTests, sort_with_edges_gate) circuit_builder.create_sort_constraint_with_edges( { a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, a, g); - prove_and_verify(circuit_builder, /*expected_result=*/false); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false); } { auto circuit_builder = UltraCircuitBuilder(); @@ -526,7 +532,7 @@ TEST_F(UltraHonkTests, sort_with_edges_gate) circuit_builder.create_sort_constraint_with_edges( { a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, b, h); - prove_and_verify(circuit_builder, /*expected_result=*/false); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false); } { auto circuit_builder = UltraCircuitBuilder(); @@ -541,99 +547,101 @@ TEST_F(UltraHonkTests, sort_with_edges_gate) circuit_builder.create_sort_constraint_with_edges( { a_idx, b2_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, b, h); - prove_and_verify(circuit_builder, /*expected_result=*/false); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false); } { auto circuit_builder = UltraCircuitBuilder(); - auto idx = add_variables(circuit_builder, { 1, 2, 5, 6, 7, 10, 11, 13, 16, 17, 20, 22, 22, 25, - 26, 29, 29, 32, 32, 33, 35, 38, 39, 39, 42, 42, 43, 45 }); + auto idx = + TestFixture::add_variables(circuit_builder, { 1, 2, 5, 6, 7, 10, 11, 13, 16, 17, 20, 22, 22, 25, + 26, 29, 29, 32, 32, 33, 35, 38, 39, 39, 42, 42, 43, 45 }); circuit_builder.create_sort_constraint_with_edges(idx, 1, 45); - prove_and_verify(circuit_builder, /*expected_result=*/true); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true); } { auto circuit_builder = UltraCircuitBuilder(); - auto idx = add_variables(circuit_builder, { 1, 2, 5, 6, 7, 10, 11, 13, 16, 17, 20, 22, 22, 25, - 26, 29, 29, 32, 32, 33, 35, 38, 39, 39, 42, 42, 43, 45 }); + auto idx = + TestFixture::add_variables(circuit_builder, { 1, 2, 5, 6, 7, 10, 11, 13, 16, 17, 20, 22, 22, 25, + 26, 29, 29, 32, 32, 33, 35, 38, 39, 39, 42, 42, 43, 45 }); circuit_builder.create_sort_constraint_with_edges(idx, 1, 29); - prove_and_verify(circuit_builder, /*expected_result=*/false); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false); } } -TEST_F(UltraHonkTests, range_constraint) +TYPED_TEST(UltraHonkTests, range_constraint) { { auto circuit_builder = UltraCircuitBuilder(); - auto indices = add_variables(circuit_builder, { 1, 2, 3, 4, 5, 6, 7, 8 }); + auto indices = TestFixture::add_variables(circuit_builder, { 1, 2, 3, 4, 5, 6, 7, 8 }); for (size_t i = 0; i < indices.size(); i++) { circuit_builder.create_new_range_constraint(indices[i], 8); } // auto ind = {a_idx,b_idx,c_idx,d_idx,e_idx,f_idx,g_idx,h_idx}; circuit_builder.create_sort_constraint(indices); - prove_and_verify(circuit_builder, /*expected_result=*/true); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true); } { auto circuit_builder = UltraCircuitBuilder(); - auto indices = add_variables(circuit_builder, { 3 }); + auto indices = TestFixture::add_variables(circuit_builder, { 3 }); for (size_t i = 0; i < indices.size(); i++) { circuit_builder.create_new_range_constraint(indices[i], 3); } // auto ind = {a_idx,b_idx,c_idx,d_idx,e_idx,f_idx,g_idx,h_idx}; circuit_builder.create_dummy_constraints(indices); - prove_and_verify(circuit_builder, /*expected_result=*/true); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true); } { auto circuit_builder = UltraCircuitBuilder(); - auto indices = add_variables(circuit_builder, { 1, 2, 3, 4, 5, 6, 8, 25 }); + auto indices = TestFixture::add_variables(circuit_builder, { 1, 2, 3, 4, 5, 6, 8, 25 }); for (size_t i = 0; i < indices.size(); i++) { circuit_builder.create_new_range_constraint(indices[i], 8); } circuit_builder.create_sort_constraint(indices); - prove_and_verify(circuit_builder, /*expected_result=*/false); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false); } { auto circuit_builder = UltraCircuitBuilder(); - auto indices = - add_variables(circuit_builder, { 1, 2, 3, 4, 5, 6, 10, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 19, 51 }); + auto indices = TestFixture::add_variables( + circuit_builder, { 1, 2, 3, 4, 5, 6, 10, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 19, 51 }); for (size_t i = 0; i < indices.size(); i++) { circuit_builder.create_new_range_constraint(indices[i], 128); } circuit_builder.create_dummy_constraints(indices); - prove_and_verify(circuit_builder, /*expected_result=*/true); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true); } { auto circuit_builder = UltraCircuitBuilder(); - auto indices = - add_variables(circuit_builder, { 1, 2, 3, 80, 5, 6, 29, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 13, 14 }); + auto indices = TestFixture::add_variables( + circuit_builder, { 1, 2, 3, 80, 5, 6, 29, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 13, 14 }); for (size_t i = 0; i < indices.size(); i++) { circuit_builder.create_new_range_constraint(indices[i], 79); } circuit_builder.create_dummy_constraints(indices); - prove_and_verify(circuit_builder, /*expected_result=*/false); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false); } { auto circuit_builder = UltraCircuitBuilder(); - auto indices = - add_variables(circuit_builder, { 1, 0, 3, 80, 5, 6, 29, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 13, 14 }); + auto indices = TestFixture::add_variables( + circuit_builder, { 1, 0, 3, 80, 5, 6, 29, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 13, 14 }); for (size_t i = 0; i < indices.size(); i++) { circuit_builder.create_new_range_constraint(indices[i], 79); } circuit_builder.create_dummy_constraints(indices); - prove_and_verify(circuit_builder, /*expected_result=*/false); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false); } } -TEST_F(UltraHonkTests, range_with_gates) +TYPED_TEST(UltraHonkTests, range_with_gates) { auto circuit_builder = UltraCircuitBuilder(); - auto idx = add_variables(circuit_builder, { 1, 2, 3, 4, 5, 6, 7, 8 }); + auto idx = TestFixture::add_variables(circuit_builder, { 1, 2, 3, 4, 5, 6, 7, 8 }); for (size_t i = 0; i < idx.size(); i++) { circuit_builder.create_new_range_constraint(idx[i], 8); } @@ -645,13 +653,13 @@ TEST_F(UltraHonkTests, range_with_gates) circuit_builder.create_add_gate( { idx[6], idx[7], circuit_builder.zero_idx, fr::one(), fr::one(), fr::zero(), -15 }); - prove_and_verify(circuit_builder, /*expected_result=*/true); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkTests, range_with_gates_where_range_is_not_a_power_of_two) +TYPED_TEST(UltraHonkTests, range_with_gates_where_range_is_not_a_power_of_two) { auto circuit_builder = UltraCircuitBuilder(); - auto idx = add_variables(circuit_builder, { 1, 2, 3, 4, 5, 6, 7, 8 }); + auto idx = TestFixture::add_variables(circuit_builder, { 1, 2, 3, 4, 5, 6, 7, 8 }); for (size_t i = 0; i < idx.size(); i++) { circuit_builder.create_new_range_constraint(idx[i], 12); } @@ -663,10 +671,10 @@ TEST_F(UltraHonkTests, range_with_gates_where_range_is_not_a_power_of_two) circuit_builder.create_add_gate( { idx[6], idx[7], circuit_builder.zero_idx, fr::one(), fr::one(), fr::zero(), -15 }); - prove_and_verify(circuit_builder, /*expected_result=*/true); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkTests, sort_widget_complex) +TYPED_TEST(UltraHonkTests, sort_widget_complex) { { @@ -677,7 +685,7 @@ TEST_F(UltraHonkTests, sort_widget_complex) ind.emplace_back(circuit_builder.add_variable(a[i])); circuit_builder.create_sort_constraint(ind); - prove_and_verify(circuit_builder, /*expected_result=*/true); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true); } { @@ -688,11 +696,11 @@ TEST_F(UltraHonkTests, sort_widget_complex) ind.emplace_back(circuit_builder.add_variable(a[i])); circuit_builder.create_sort_constraint(ind); - prove_and_verify(circuit_builder, /*expected_result=*/false); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false); } } -TEST_F(UltraHonkTests, sort_widget_neg) +TYPED_TEST(UltraHonkTests, sort_widget_neg) { auto circuit_builder = UltraCircuitBuilder(); fr a = fr::one(); @@ -706,10 +714,10 @@ TEST_F(UltraHonkTests, sort_widget_neg) auto d_idx = circuit_builder.add_variable(d); circuit_builder.create_sort_constraint({ a_idx, b_idx, c_idx, d_idx }); - prove_and_verify(circuit_builder, /*expected_result=*/false); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false); } -TEST_F(UltraHonkTests, composed_range_constraint) +TYPED_TEST(UltraHonkTests, composed_range_constraint) { auto circuit_builder = UltraCircuitBuilder(); auto c = fr::random_element(); @@ -719,10 +727,10 @@ TEST_F(UltraHonkTests, composed_range_constraint) circuit_builder.create_add_gate({ a_idx, circuit_builder.zero_idx, circuit_builder.zero_idx, 1, 0, 0, -fr(e) }); circuit_builder.decompose_into_default_range(a_idx, 134); - prove_and_verify(circuit_builder, /*expected_result=*/true); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkTests, non_native_field_multiplication) +TYPED_TEST(UltraHonkTests, non_native_field_multiplication) { using fq = fq; auto circuit_builder = UltraCircuitBuilder(); @@ -775,10 +783,10 @@ TEST_F(UltraHonkTests, non_native_field_multiplication) const auto [lo_1_idx, hi_1_idx] = circuit_builder.evaluate_non_native_field_multiplication(inputs); circuit_builder.range_constrain_two_limbs(lo_1_idx, hi_1_idx, 70, 70); - prove_and_verify(circuit_builder, /*expected_result=*/true); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkTests, rom) +TYPED_TEST(UltraHonkTests, rom) { auto circuit_builder = UltraCircuitBuilder(); @@ -816,10 +824,10 @@ TEST_F(UltraHonkTests, rom) 0, }); - prove_and_verify(circuit_builder, /*expected_result=*/true); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkTests, ram) +TYPED_TEST(UltraHonkTests, ram) { auto circuit_builder = UltraCircuitBuilder(); @@ -879,10 +887,10 @@ TEST_F(UltraHonkTests, ram) }, false); - prove_and_verify(circuit_builder, /*expected_result=*/true); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkTests, range_checks_on_duplicates) +TYPED_TEST(UltraHonkTests, range_checks_on_duplicates) { auto circuit_builder = UltraCircuitBuilder(); @@ -914,14 +922,14 @@ TEST_F(UltraHonkTests, range_checks_on_duplicates) }, false); - prove_and_verify(circuit_builder, /*expected_result=*/true); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true); } // Ensure copy constraints added on variables smaller than 2^14, which have been previously // range constrained, do not break the set equivalence checks because of indices mismatch. // 2^14 is DEFAULT_PLOOKUP_RANGE_BITNUM i.e. the maximum size before a variable gets sliced // before range constraints are applied to it. -TEST_F(UltraHonkTests, range_constraint_small_variable) +TYPED_TEST(UltraHonkTests, range_constraint_small_variable) { auto circuit_builder = UltraCircuitBuilder(); @@ -937,5 +945,5 @@ TEST_F(UltraHonkTests, range_constraint_small_variable) circuit_builder.create_range_constraint(c_idx, 8, "bad range"); circuit_builder.assert_equal(a_idx, c_idx); - prove_and_verify(circuit_builder, /*expected_result=*/true); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true); } \ No newline at end of file diff --git a/barretenberg/sol/src/honk/Fr.sol b/barretenberg/sol/src/honk/Fr.sol index 26fada6693a..aeeebb0f2ad 100644 --- a/barretenberg/sol/src/honk/Fr.sol +++ b/barretenberg/sol/src/honk/Fr.sol @@ -13,8 +13,9 @@ using {notEqual as !=} for Fr global; using {equal as ==} for Fr global; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order - +Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); // Instantiation + library FrLib { function from(uint256 value) internal pure returns (Fr) { return Fr.wrap(value % MODULUS); @@ -79,7 +80,6 @@ library FrLib { // TODO: Montgomery's batch inversion trick function div(Fr numerator, Fr denominator) internal view returns (Fr) { - Fr inversion = invert(denominator); return numerator * invert(denominator); } } diff --git a/barretenberg/sol/src/honk/HonkTypes.sol b/barretenberg/sol/src/honk/HonkTypes.sol index 275c26ab7f2..593d4bf2c2a 100644 --- a/barretenberg/sol/src/honk/HonkTypes.sol +++ b/barretenberg/sol/src/honk/HonkTypes.sol @@ -8,14 +8,14 @@ import {Fr} from "./Fr.sol"; uint256 constant CONST_PROOF_SIZE_LOG_N = 28; -uint256 constant NUMBER_OF_SUBRELATIONS = 18; -uint256 constant BATCHED_RELATION_PARTIAL_LENGTH = 7; -uint256 constant NUMBER_OF_ENTITIES = 42; -uint256 constant NUMBER_OF_ALPHAS = 17; +uint256 constant NUMBER_OF_SUBRELATIONS = 26; +uint256 constant BATCHED_RELATION_PARTIAL_LENGTH = 8; +uint256 constant NUMBER_OF_ENTITIES = 44; +uint256 constant NUMBER_UNSHIFTED = 35; +uint256 constant NUMBER_TO_BE_SHIFTED = 9; -// Prime field order -uint256 constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; // EC group order -uint256 constant P = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order +// Alphas are used as relation separators so there should be NUMBER_OF_SUBRELATIONS - 1 +uint256 constant NUMBER_OF_ALPHAS = 25; // ENUM FOR WIRES enum WIRE { @@ -30,6 +30,8 @@ enum WIRE { Q_ELLIPTIC, Q_AUX, Q_LOOKUP, + Q_POSEIDON2_EXTERNAL, + Q_POSEIDON2_INTERNAL, SIGMA_1, SIGMA_2, SIGMA_3, @@ -94,6 +96,8 @@ library Honk { G1Point qAux; // Auxillary G1Point qElliptic; // Auxillary G1Point qLookup; // Lookup + G1Point qPoseidon2External; + G1Point qPoseidon2Internal; // Copy cnstraints G1Point s1; G1Point s2; diff --git a/barretenberg/sol/src/honk/HonkVerifier.sol b/barretenberg/sol/src/honk/HonkVerifier.sol index 942adc6814f..540398bc0c6 100644 --- a/barretenberg/sol/src/honk/HonkVerifier.sol +++ b/barretenberg/sol/src/honk/HonkVerifier.sol @@ -11,19 +11,20 @@ import { NUMBER_OF_ENTITIES, NUMBER_OF_SUBRELATIONS, NUMBER_OF_ALPHAS, + NUMBER_UNSHIFTED, BATCHED_RELATION_PARTIAL_LENGTH, - CONST_PROOF_SIZE_LOG_N, - P, - Q + 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 {Fr, FrLib} from "./Fr.sol"; +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(); error ZeromorphFailed(); @@ -34,14 +35,14 @@ abstract contract BaseHonkVerifier is IVerifier { function verify(bytes calldata proof, bytes32[] calldata publicInputs) public view override returns (bool) { Honk.VerificationKey memory vk = loadVerificationKey(); - Honk.Proof memory p = loadProof(proof); + 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, vk, publicInputs); + Transcript memory t = TranscriptLib.generateTranscript(p, publicInputs, vk.publicInputsSize); // Compute the public input delta t.publicInputsDelta = @@ -62,138 +63,6 @@ abstract contract BaseHonkVerifier is IVerifier { return VK.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); - // Zero morph Commitments - for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { - // Explicitly stating the x0, x1, y0, y1 start and end boundaries to make the calldata slicing bearable - 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.zmCqs[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 * 0x80); - - p.zmCq = 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])) - }); - - p.zmPi = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[boundary + 0x80:boundary + 0xa0])), - x_1: uint256(bytes32(proof[boundary + 0xa0:boundary + 0xc0])), - y_0: uint256(bytes32(proof[boundary + 0xc0:boundary + 0xe0])), - y_1: uint256(bytes32(proof[boundary + 0xe0:boundary + 0x100])) - }); - - return p; - } - function computePublicInputDelta( bytes32[] memory publicInputs, Fr beta, @@ -243,7 +112,7 @@ abstract contract BaseHonkVerifier is IVerifier { } // Last round - Fr grandHonkRelationSum = accumulateRelationEvaluations(proof, tp, powPartialEvaluation); + Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations(proof, tp, powPartialEvaluation); verified = (grandHonkRelationSum == roundTarget); } @@ -263,18 +132,27 @@ abstract contract BaseHonkVerifier is IVerifier { returns (Fr targetSum) { // TODO: inline - Fr[7] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000002d0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff89), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000030), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffdd), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000030), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff89), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000002d0) + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff11), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000090), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff71), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000000f0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000013b0) ]; - Fr[7] 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[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 @@ -307,590 +185,13 @@ abstract contract BaseHonkVerifier is IVerifier { // 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 - view + pure returns (Fr newEvaluation) { Fr univariateEval = Fr.wrap(1) + (roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); newEvaluation = currentEvaluation * univariateEval; } - // Calculate the contributions of each relation to the expected value of the full honk relation - // - // For each relation, we use the purported values ( the ones provided by the prover ) of the multivariates to - // calculate a contribution to the purported value of the full Honk relation. - // These are stored in the evaluations part of the proof object. - // We add these together, with the appropiate scaling factor ( the alphas calculated in challenges ) - // This value is checked against the final value of the target total sum - et voila! - function accumulateRelationEvaluations(Honk.Proof memory proof, Transcript memory tp, Fr powPartialEval) - internal - view - returns (Fr accumulator) - { - Fr[NUMBER_OF_ENTITIES] memory purportedEvaluations = proof.sumcheckEvaluations; - Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; - - // Accumulate all 6 custom gates - each with varying number of subrelations - accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePermutationRelation(purportedEvaluations, tp, evaluations, powPartialEval); - accumulateLogDerivativeLookupRelation(purportedEvaluations, tp, evaluations, powPartialEval); - accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateAuxillaryRelation(purportedEvaluations, tp, evaluations, powPartialEval); - - // Apply alpha challenges to challenge evaluations - // Returns grand honk realtion evaluation - accumulator = scaleAndBatchSubrelations(evaluations, tp.alphas); - } - - /** - * 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) { - return p[uint256(_wire)]; - } - - /** - * Ultra Arithmetic Relation - * - */ - function accumulateArithmeticRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal view { - // Relation 0 - Fr q_arith = wire(p, WIRE.Q_ARITH); - { - 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 + (q_arith - Fr.wrap(1)) * wire(p, WIRE.W_4_SHIFT); - accum = accum * q_arith; - accum = accum * domainSep; - evals[0] = accum; - } - - // Relation 1 - { - Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); - accum = accum * (q_arith - Fr.wrap(2)); - accum = accum * (q_arith - Fr.wrap(1)); - accum = accum * q_arith; - accum = accum * domainSep; - evals[1] = accum; - } - } - - function accumulatePermutationRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Transcript memory tp, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal view { - Fr grand_product_numerator; - Fr grand_product_denominator; - - { - Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * tp.beta + tp.gamma; - num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * tp.beta + tp.gamma); - num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * tp.beta + tp.gamma); - num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * tp.beta + tp.gamma); - - grand_product_numerator = num; - } - { - Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * tp.beta + tp.gamma; - den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * tp.beta + tp.gamma); - den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * tp.beta + tp.gamma); - den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * tp.beta + tp.gamma); - - grand_product_denominator = den; - } - - // Contribution 2 - { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; - - acc = acc - - ( - (wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * tp.publicInputsDelta)) - * grand_product_denominator - ); - acc = acc * domainSep; - evals[2] = acc; - } - - // Contribution 3 - { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; - evals[3] = acc; - } - } - - function accumulateLogDerivativeLookupRelation( - 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; - - // Calculate the write term (the table accumulation) - { - write_term = wire(p, WIRE.TABLE_1) + tp.gamma + (wire(p, WIRE.TABLE_2) * tp.eta) - + (wire(p, WIRE.TABLE_3) * tp.etaTwo) + (wire(p, WIRE.TABLE_4) * tp.etaThree); - } - - // Calculate the write term - { - Fr derived_entry_1 = wire(p, WIRE.W_L) + tp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); - - read_term = derived_entry_1 + (derived_entry_2 * tp.eta) + (derived_entry_3 * tp.etaTwo) - + (wire(p, WIRE.Q_O) * tp.etaThree); - } - - Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; - Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - - Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); - - // Inverse calculated correctly relation - Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; - accumulatorNone = accumulatorNone * domainSep; - - // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; - - evals[4] = accumulatorNone; - evals[5] = accumulatorOne; - } - - function accumulateDeltaRangeRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) 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); - - // Compute wire differences - Fr delta_1 = wire(p, WIRE.W_R) - wire(p, WIRE.W_L); - Fr delta_2 = wire(p, WIRE.W_O) - wire(p, WIRE.W_R); - Fr delta_3 = wire(p, WIRE.W_4) - wire(p, WIRE.W_O); - Fr delta_4 = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_4); - - // Contribution 6 - { - Fr acc = delta_1; - acc = acc * (delta_1 + minus_one); - acc = acc * (delta_1 + minus_two); - acc = acc * (delta_1 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[6] = acc; - } - - // Contribution 7 - { - Fr acc = delta_2; - acc = acc * (delta_2 + minus_one); - acc = acc * (delta_2 + minus_two); - acc = acc * (delta_2 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[7] = acc; - } - - // Contribution 8 - { - Fr acc = delta_3; - acc = acc * (delta_3 + minus_one); - acc = acc * (delta_3 + minus_two); - acc = acc * (delta_3 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[8] = acc; - } - - // Contribution 9 - { - Fr acc = delta_4; - acc = acc * (delta_4 + minus_one); - acc = acc * (delta_4 + minus_two); - acc = acc * (delta_4 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[9] = acc; - } - } - - struct EllipticParams { - // Points - Fr x_1; - Fr y_1; - Fr x_2; - Fr y_2; - Fr y_3; - Fr x_3; - // push accumulators into memory - Fr x_double_identity; - } - - 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); - - ep.x_2 = wire(p, WIRE.W_L_SHIFT); - ep.y_2 = wire(p, WIRE.W_4_SHIFT); - ep.y_3 = wire(p, WIRE.W_O_SHIFT); - ep.x_3 = wire(p, WIRE.W_R_SHIFT); - - Fr q_sign = wire(p, WIRE.Q_L); - Fr q_is_double = wire(p, WIRE.Q_M); - - // Contribution 10 point addition, x-coordinate check - // q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0 - Fr x_diff = (ep.x_2 - ep.x_1); - Fr y1_sqr = (ep.y_1 * ep.y_1); - { - // Move to top - Fr partialEval = domainSep; - - Fr y2_sqr = (ep.y_2 * ep.y_2); - Fr y1y2 = ep.y_1 * ep.y_2 * q_sign; - Fr x_add_identity = (ep.x_3 + ep.x_2 + ep.x_1); - x_add_identity = x_add_identity * x_diff * x_diff; - x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - - evals[10] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (Fr.wrap(1) - q_is_double); - } - - // Contribution 11 point addition, x-coordinate check - // q_elliptic * (q_sign * y1 + y3)(x2 - x1) + (x3 - x1)(y2 - q_sign * y1) = 0 - { - Fr y1_plus_y3 = ep.y_1 + ep.y_3; - Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; - evals[11] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (Fr.wrap(1) - q_is_double); - } - - // Contribution 10 point doubling, x-coordinate check - // (x3 + x1 + x1) (4y1*y1) - 9 * x1 * x1 * x1 * x1 = 0 - // N.B. we're using the equivalence x1*x1*x1 === y1*y1 - curve_b to reduce degree by 1 - { - Fr x_pow_4 = (y1_sqr + GRUMPKIN_CURVE_B_PARAMETER_NEGATED) * ep.x_1; - Fr y1_sqr_mul_4 = y1_sqr + y1_sqr; - y1_sqr_mul_4 = y1_sqr_mul_4 + y1_sqr_mul_4; - Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); - - // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; - evals[10] = evals[10] + acc; - } - - // Contribution 11 point doubling, y-coordinate check - // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 - { - Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); - evals[11] = evals[11] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; - } - } - - // Constants for the auxiliary relation - Fr constant LIMB_SIZE = Fr.wrap(uint256(1) << 68); - Fr constant SUBLIMB_SHIFT = Fr.wrap(uint256(1) << 14); - Fr constant MINUS_ONE = Fr.wrap(P - 1); - - // Parameters used within the Auxiliary Relation - // A struct is used to work around stack too deep. This relation has alot of variables - struct AuxParams { - Fr limb_subproduct; - Fr non_native_field_gate_1; - Fr non_native_field_gate_2; - Fr non_native_field_gate_3; - Fr limb_accumulator_1; - Fr limb_accumulator_2; - Fr memory_record_check; - Fr partial_record_check; - Fr next_gate_access_type; - Fr record_delta; - Fr index_delta; - Fr adjacent_values_match_if_adjacent_indices_match; - Fr adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation; - Fr access_check; - Fr next_gate_access_type_is_boolean; - Fr ROM_consistency_check_identity; - Fr RAM_consistency_check_identity; - Fr timestamp_delta; - Fr RAM_timestamp_check_identity; - Fr memory_identity; - Fr index_is_monotonically_increasing; - Fr auxiliary_identity; - } - - function accumulateAuxillaryRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Transcript memory tp, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { - AuxParams memory ap; - - /** - * Contribution 12 - * Non native field arithmetic gate 2 - * deg 4 - * - * _ _ - * / _ _ _ 14 \ - * q_2 . q_4 | (w_1 . w_2) + (w_1 . w_2) + (w_1 . w_4 + w_2 . w_3 - w_3) . 2 - w_3 - w_4 | - * \_ _/ - * - * - */ - ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = - (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); - - ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); - ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); - - ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); - - Fr non_native_field_identity = - ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; - non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); - - // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm - // deg 2 - ap.limb_accumulator_1 = wire(p, WIRE.W_R_SHIFT) * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L_SHIFT); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_O); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_R); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L); - ap.limb_accumulator_1 = ap.limb_accumulator_1 - wire(p, WIRE.W_4); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * wire(p, WIRE.Q_4); - - // ((((w3' * 2^14 + w2') * 2^14 + w1') * 2^14 + w4) * 2^14 + w3 - w4') * qm - // deg 2 - ap.limb_accumulator_2 = wire(p, WIRE.W_O_SHIFT) * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_R_SHIFT); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_L_SHIFT); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_4); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_O); - ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - - Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; - limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 - - /** - * MEMORY - * - * A RAM memory record contains a tuple of the following fields: - * * i: `index` of memory cell being accessed - * * t: `timestamp` of memory cell being accessed (used for RAM, set to 0 for ROM) - * * v: `value` of memory cell being accessed - * * a: `access` type of record. read: 0 = read, 1 = write - * * r: `record` of memory cell. record = access + index * eta + timestamp * eta_two + value * eta_three - * - * A ROM memory record contains a tuple of the following fields: - * * i: `index` of memory cell being accessed - * * v: `value1` of memory cell being accessed (ROM tables can store up to 2 values per index) - * * v2:`value2` of memory cell being accessed (ROM tables can store up to 2 values per index) - * * r: `record` of memory cell. record = index * eta + value2 * eta_two + value1 * eta_three - * - * When performing a read/write access, the values of i, t, v, v2, a, r are stored in the following wires + - * selectors, depending on whether the gate is a RAM read/write or a ROM read - * - * | gate type | i | v2/t | v | a | r | - * | --------- | -- | ----- | -- | -- | -- | - * | ROM | w1 | w2 | w3 | -- | w4 | - * | RAM | w1 | w2 | w3 | qc | w4 | - * - * (for accesses where `index` is a circuit constant, it is assumed the circuit will apply a copy constraint on - * `w2` to fix its value) - * - * - */ - - /** - * Memory Record Check - * Partial degree: 1 - * Total degree: 4 - * - * A ROM/ROM access gate can be evaluated with the identity: - * - * qc + w1 \eta + w2 \eta_two + w3 \eta_three - w4 = 0 - * - * For ROM gates, qc = 0 - */ - ap.memory_record_check = wire(p, WIRE.W_O) * tp.etaThree; - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * tp.etaTwo); - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * tp.eta); - ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); - ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 - ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); - - /** - * Contribution 13 & 14 - * ROM Consistency Check - * Partial degree: 1 - * Total degree: 4 - * - * For every ROM read, a set equivalence check is applied between the record witnesses, and a second set of - * records that are sorted. - * - * We apply the following checks for the sorted records: - * - * 1. w1, w2, w3 correctly map to 'index', 'v1, 'v2' for a given record value at w4 - * 2. index values for adjacent records are monotonically increasing - * 3. if, at gate i, index_i == index_{i + 1}, then value1_i == value1_{i + 1} and value2_i == value2_{i + 1} - * - */ - ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); - ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - - 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 - - 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 - - /** - * Contributions 15,16,17 - * RAM Consistency Check - * - * The 'access' type of the record is extracted with the expression `w_4 - ap.partial_record_check` - * (i.e. for an honest Prover `w1 * eta + w2 * eta^2 + w3 * eta^3 - w4 = access`. - * This is validated by requiring `access` to be boolean - * - * For two adjacent entries in the sorted list if _both_ - * A) index values match - * B) adjacent access value is 0 (i.e. next gate is a READ) - * then - * C) both values must match. - * The gate boolean check is - * (A && B) => C === !(A && B) || C === !A || !B || C - * - * N.B. it is the responsibility of the circuit writer to ensure that every RAM cell is initialized - * 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 - - // TODO(https://github.com/AztecProtocol/barretenberg/issues/757): If we sorted in - // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta - // 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 - - // 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 - // do with an arithmetic gate because of the `eta` factors. We need to check that the *next* gate's access - // type is correct, to cover this edge case - // deg 2 or 4 - ap.next_gate_access_type_is_boolean = - 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 - - ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_ARITH)); // deg 3 or 9 - - /** - * RAM Timestamp Consistency Check - * - * | w1 | w2 | w3 | w4 | - * | index | timestamp | timestamp_check | -- | - * - * Let delta_index = index_{i + 1} - index_{i} - * - * Iff delta_index == 0, timestamp_check = timestamp_{i + 1} - timestamp_i - * Else timestamp_check = 0 - */ - ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = - (ap.index_delta * MINUS_ONE + Fr.wrap(1)) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 - - /** - * Complete Contribution 12 - * The complete RAM/ROM memory identity - * Partial degree: - */ - 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 - - // (deg 3 or 9) + (deg 4) + (deg 3) - ap.auxiliary_identity = ap.memory_identity + non_native_field_identity + limb_accumulator_identity; - ap.auxiliary_identity = ap.auxiliary_identity * (wire(p, WIRE.Q_AUX) * domainSep); // deg 4 or 10 - evals[12] = ap.auxiliary_identity; - } - - function scaleAndBatchSubrelations( - Fr[NUMBER_OF_SUBRELATIONS] memory evaluations, - Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges - ) internal view returns (Fr accumulator) { - accumulator = accumulator + evaluations[0]; - - for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; - } - } - function verifyZeroMorph(Honk.Proof memory proof, Honk.VerificationKey memory vk, Transcript memory tp) internal view @@ -977,12 +278,12 @@ abstract contract BaseHonkVerifier is IVerifier { // f commitments are accumulated at (zm_x * r) cp.rho_pow = Fr.wrap(1); - for (uint256 i = 1; i < 34; ++i) { + for (uint256 i = 1; i <= NUMBER_UNSHIFTED; ++i) { scalars[i] = tp.zmX * cp.rho_pow; cp.rho_pow = cp.rho_pow * tp.rho; } // g commitments are accumulated at r - for (uint256 i = 34; i < 43; ++i) { + for (uint256 i = NUMBER_UNSHIFTED + 1; i <= NUMBER_OF_ENTITIES; ++i) { scalars[i] = cp.rho_pow; cp.rho_pow = cp.rho_pow * tp.rho; } @@ -999,41 +300,43 @@ abstract contract BaseHonkVerifier is IVerifier { commitments[9] = vk.qElliptic; commitments[10] = vk.qAux; commitments[11] = vk.qLookup; - commitments[12] = vk.s1; - commitments[13] = vk.s2; - commitments[14] = vk.s3; - commitments[15] = vk.s4; - commitments[16] = vk.id1; - commitments[17] = vk.id2; - commitments[18] = vk.id3; - commitments[19] = vk.id4; - commitments[20] = vk.t1; - commitments[21] = vk.t2; - commitments[22] = vk.t3; - commitments[23] = vk.t4; - commitments[24] = vk.lagrangeFirst; - commitments[25] = vk.lagrangeLast; + 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[26] = convertProofPoint(proof.w1); - commitments[27] = convertProofPoint(proof.w2); - commitments[28] = convertProofPoint(proof.w3); - commitments[29] = convertProofPoint(proof.w4); - commitments[30] = convertProofPoint(proof.zPerm); - commitments[31] = convertProofPoint(proof.lookupInverses); - commitments[32] = convertProofPoint(proof.lookupReadCounts); - commitments[33] = convertProofPoint(proof.lookupReadTags); + 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[34] = vk.t1; - commitments[35] = vk.t2; - commitments[36] = vk.t3; - commitments[37] = vk.t4; - commitments[38] = convertProofPoint(proof.w1); - commitments[39] = convertProofPoint(proof.w2); - commitments[40] = convertProofPoint(proof.w3); - commitments[41] = convertProofPoint(proof.w4); - commitments[42] = convertProofPoint(proof.zPerm); + 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); // Add scalar contributions // Add contributions: scalar * [q_k], k = 0,...,log_N, where @@ -1058,8 +361,8 @@ abstract contract BaseHonkVerifier is IVerifier { cp.x_pow_2kp1 = cp.x_pow_2kp1 * cp.x_pow_2kp1; } - scalars[43 + k] = scalar; - commitments[43 + k] = convertProofPoint(proof.zmCqs[k]); + scalars[NUMBER_OF_ENTITIES + 1 + k] = scalar; + commitments[NUMBER_OF_ENTITIES + 1 + k] = convertProofPoint(proof.zmCqs[k]); } return batchMul2(commitments, scalars); diff --git a/barretenberg/sol/src/honk/Relations.sol b/barretenberg/sol/src/honk/Relations.sol new file mode 100644 index 00000000000..685797de47f --- /dev/null +++ b/barretenberg/sol/src/honk/Relations.sol @@ -0,0 +1,710 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Aztec Labs +pragma solidity >=0.8.21; + +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 +import {MINUS_ONE, MODULUS as P, Fr, FrLib} from "./Fr.sol"; +import {Transcript, TranscriptLib} from "./Transcript.sol"; + +library RelationsLib { + Fr internal constant GRUMPKIN_CURVE_B_PARAMETER_NEGATED = Fr.wrap(17); // -(-17) + + function accumulateRelationEvaluations(Honk.Proof memory proof, Transcript memory tp, Fr powPartialEval) + internal + view + returns (Fr accumulator) + { + Fr[NUMBER_OF_ENTITIES] memory purportedEvaluations = proof.sumcheckEvaluations; + Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; + + // Accumulate all relations in Ultra Honk - each with varying number of subrelations + accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePermutationRelation(purportedEvaluations, tp, evaluations, powPartialEval); + accumulateLogDerivativeLookupRelation(purportedEvaluations, tp, evaluations, powPartialEval); + accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateAuxillaryRelation(purportedEvaluations, tp, 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); + } + + /** + * 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) { + return p[uint256(_wire)]; + } + + /** + * Ultra Arithmetic Relation + * + */ + function accumulateArithmeticRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal view { + // Relation 0 + Fr q_arith = wire(p, WIRE.Q_ARITH); + { + 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 + (q_arith - Fr.wrap(1)) * wire(p, WIRE.W_4_SHIFT); + accum = accum * q_arith; + accum = accum * domainSep; + evals[0] = accum; + } + + // Relation 1 + { + Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); + accum = accum * (q_arith - Fr.wrap(2)); + accum = accum * (q_arith - Fr.wrap(1)); + accum = accum * q_arith; + accum = accum * domainSep; + evals[1] = accum; + } + } + + function accumulatePermutationRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Transcript memory tp, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + Fr grand_product_numerator; + Fr grand_product_denominator; + + { + Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * tp.beta + tp.gamma; + num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * tp.beta + tp.gamma); + num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * tp.beta + tp.gamma); + num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * tp.beta + tp.gamma); + + grand_product_numerator = num; + } + { + Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * tp.beta + tp.gamma; + den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * tp.beta + tp.gamma); + den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * tp.beta + tp.gamma); + den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * tp.beta + tp.gamma); + + grand_product_denominator = den; + } + + // Contribution 2 + { + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; + + acc = acc + - ( + (wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * tp.publicInputsDelta)) + * grand_product_denominator + ); + acc = acc * domainSep; + evals[2] = acc; + } + + // Contribution 3 + { + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + evals[3] = acc; + } + } + + function accumulateLogDerivativeLookupRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Transcript memory tp, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + Fr write_term; + Fr read_term; + + // Calculate the write term (the table accumulation) + { + write_term = wire(p, WIRE.TABLE_1) + tp.gamma + (wire(p, WIRE.TABLE_2) * tp.eta) + + (wire(p, WIRE.TABLE_3) * tp.etaTwo) + (wire(p, WIRE.TABLE_4) * tp.etaThree); + } + + // Calculate the write term + { + Fr derived_entry_1 = wire(p, WIRE.W_L) + tp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); + + read_term = derived_entry_1 + (derived_entry_2 * tp.eta) + (derived_entry_3 * tp.etaTwo) + + (wire(p, WIRE.Q_O) * tp.etaThree); + } + + Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; + Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; + + Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) + - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + + // Inverse calculated correctly relation + Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; + accumulatorNone = accumulatorNone * domainSep; + + // Inverse + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; + + evals[4] = accumulatorNone; + evals[5] = accumulatorOne; + } + + function accumulateDeltaRangeRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) 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); + + // Compute wire differences + Fr delta_1 = wire(p, WIRE.W_R) - wire(p, WIRE.W_L); + Fr delta_2 = wire(p, WIRE.W_O) - wire(p, WIRE.W_R); + Fr delta_3 = wire(p, WIRE.W_4) - wire(p, WIRE.W_O); + Fr delta_4 = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_4); + + // Contribution 6 + { + Fr acc = delta_1; + acc = acc * (delta_1 + minus_one); + acc = acc * (delta_1 + minus_two); + acc = acc * (delta_1 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[6] = acc; + } + + // Contribution 7 + { + Fr acc = delta_2; + acc = acc * (delta_2 + minus_one); + acc = acc * (delta_2 + minus_two); + acc = acc * (delta_2 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[7] = acc; + } + + // Contribution 8 + { + Fr acc = delta_3; + acc = acc * (delta_3 + minus_one); + acc = acc * (delta_3 + minus_two); + acc = acc * (delta_3 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[8] = acc; + } + + // Contribution 9 + { + Fr acc = delta_4; + acc = acc * (delta_4 + minus_one); + acc = acc * (delta_4 + minus_two); + acc = acc * (delta_4 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[9] = acc; + } + } + + struct EllipticParams { + // Points + Fr x_1; + Fr y_1; + Fr x_2; + Fr y_2; + Fr y_3; + Fr x_3; + // push accumulators into memory + Fr x_double_identity; + } + + function accumulateEllipticRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + EllipticParams memory ep; + ep.x_1 = wire(p, WIRE.W_R); + ep.y_1 = wire(p, WIRE.W_O); + + ep.x_2 = wire(p, WIRE.W_L_SHIFT); + ep.y_2 = wire(p, WIRE.W_4_SHIFT); + ep.y_3 = wire(p, WIRE.W_O_SHIFT); + ep.x_3 = wire(p, WIRE.W_R_SHIFT); + + Fr q_sign = wire(p, WIRE.Q_L); + Fr q_is_double = wire(p, WIRE.Q_M); + + // Contribution 10 point addition, x-coordinate check + // q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0 + Fr x_diff = (ep.x_2 - ep.x_1); + Fr y1_sqr = (ep.y_1 * ep.y_1); + { + // Move to top + Fr partialEval = domainSep; + + Fr y2_sqr = (ep.y_2 * ep.y_2); + Fr y1y2 = ep.y_1 * ep.y_2 * q_sign; + Fr x_add_identity = (ep.x_3 + ep.x_2 + ep.x_1); + x_add_identity = x_add_identity * x_diff * x_diff; + x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; + + evals[10] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (Fr.wrap(1) - q_is_double); + } + + // Contribution 11 point addition, x-coordinate check + // q_elliptic * (q_sign * y1 + y3)(x2 - x1) + (x3 - x1)(y2 - q_sign * y1) = 0 + { + Fr y1_plus_y3 = ep.y_1 + ep.y_3; + Fr y_diff = ep.y_2 * q_sign - ep.y_1; + Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; + evals[11] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (Fr.wrap(1) - q_is_double); + } + + // Contribution 10 point doubling, x-coordinate check + // (x3 + x1 + x1) (4y1*y1) - 9 * x1 * x1 * x1 * x1 = 0 + // N.B. we're using the equivalence x1*x1*x1 === y1*y1 - curve_b to reduce degree by 1 + { + Fr x_pow_4 = (y1_sqr + GRUMPKIN_CURVE_B_PARAMETER_NEGATED) * ep.x_1; + Fr y1_sqr_mul_4 = y1_sqr + y1_sqr; + y1_sqr_mul_4 = y1_sqr_mul_4 + y1_sqr_mul_4; + Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); + + // NOTE: pushed into memory (stack >:'( ) + ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + evals[10] = evals[10] + acc; + } + + // Contribution 11 point doubling, y-coordinate check + // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 + { + Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; + Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); + evals[11] = evals[11] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + } + } + + // Constants for the auxiliary relation + Fr constant LIMB_SIZE = Fr.wrap(uint256(1) << 68); + Fr constant SUBLIMB_SHIFT = Fr.wrap(uint256(1) << 14); + + // Parameters used within the Auxiliary Relation + // A struct is used to work around stack too deep. This relation has alot of variables + struct AuxParams { + Fr limb_subproduct; + Fr non_native_field_gate_1; + Fr non_native_field_gate_2; + Fr non_native_field_gate_3; + Fr limb_accumulator_1; + Fr limb_accumulator_2; + Fr memory_record_check; + Fr partial_record_check; + Fr next_gate_access_type; + Fr record_delta; + Fr index_delta; + Fr adjacent_values_match_if_adjacent_indices_match; + Fr adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation; + Fr access_check; + Fr next_gate_access_type_is_boolean; + Fr ROM_consistency_check_identity; + Fr RAM_consistency_check_identity; + Fr timestamp_delta; + Fr RAM_timestamp_check_identity; + Fr memory_identity; + Fr index_is_monotonically_increasing; + Fr auxiliary_identity; + } + + function accumulateAuxillaryRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Transcript memory tp, // sooo we take the relation parameters, if needed, from tramscript + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep // i guess this is the scaling factor? + ) internal pure { + AuxParams memory ap; + + /** + * Contribution 12 + * Non native field arithmetic gate 2 + * deg 4 + * + * _ _ + * / _ _ _ 14 \ + * q_2 . q_4 | (w_1 . w_2) + (w_1 . w_2) + (w_1 . w_4 + w_2 . w_3 - w_3) . 2 - w_3 - w_4 | + * \_ _/ + * + * + */ + ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); + ap.non_native_field_gate_2 = + (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); + + ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; + ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.non_native_field_gate_1 = ap.limb_subproduct; + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); + + ap.non_native_field_gate_3 = ap.limb_subproduct; + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); + + Fr non_native_field_identity = + ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; + non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); + + // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm + // deg 2 + ap.limb_accumulator_1 = wire(p, WIRE.W_R_SHIFT) * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L_SHIFT); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_O); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_R); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L); + ap.limb_accumulator_1 = ap.limb_accumulator_1 - wire(p, WIRE.W_4); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * wire(p, WIRE.Q_4); + + // ((((w3' * 2^14 + w2') * 2^14 + w1') * 2^14 + w4) * 2^14 + w3 - w4') * qm + // deg 2 + ap.limb_accumulator_2 = wire(p, WIRE.W_O_SHIFT) * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_R_SHIFT); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_L_SHIFT); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_4); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_O); + ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); + + Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; + limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 + + /** + * MEMORY + * + * A RAM memory record contains a tuple of the following fields: + * * i: `index` of memory cell being accessed + * * t: `timestamp` of memory cell being accessed (used for RAM, set to 0 for ROM) + * * v: `value` of memory cell being accessed + * * a: `access` type of record. read: 0 = read, 1 = write + * * r: `record` of memory cell. record = access + index * eta + timestamp * eta_two + value * eta_three + * + * A ROM memory record contains a tuple of the following fields: + * * i: `index` of memory cell being accessed + * * v: `value1` of memory cell being accessed (ROM tables can store up to 2 values per index) + * * v2:`value2` of memory cell being accessed (ROM tables can store up to 2 values per index) + * * r: `record` of memory cell. record = index * eta + value2 * eta_two + value1 * eta_three + * + * When performing a read/write access, the values of i, t, v, v2, a, r are stored in the following wires + + * selectors, depending on whether the gate is a RAM read/write or a ROM read + * + * | gate type | i | v2/t | v | a | r | + * | --------- | -- | ----- | -- | -- | -- | + * | ROM | w1 | w2 | w3 | -- | w4 | + * | RAM | w1 | w2 | w3 | qc | w4 | + * + * (for accesses where `index` is a circuit constant, it is assumed the circuit will apply a copy constraint on + * `w2` to fix its value) + * + * + */ + + /** + * Memory Record Check + * Partial degree: 1 + * Total degree: 4 + * + * A ROM/ROM access gate can be evaluated with the identity: + * + * qc + w1 \eta + w2 \eta_two + w3 \eta_three - w4 = 0 + * + * For ROM gates, qc = 0 + */ + ap.memory_record_check = wire(p, WIRE.W_O) * tp.etaThree; + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * tp.etaTwo); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * tp.eta); + ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); + ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 + ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); + + /** + * Contribution 13 & 14 + * ROM Consistency Check + * Partial degree: 1 + * Total degree: 4 + * + * For every ROM read, a set equivalence check is applied between the record witnesses, and a second set of + * records that are sorted. + * + * We apply the following checks for the sorted records: + * + * 1. w1, w2, w3 correctly map to 'index', 'v1, 'v2' for a given record value at w4 + * 2. index values for adjacent records are monotonically increasing + * 3. if, at gate i, index_i == index_{i + 1}, then value1_i == value1_{i + 1} and value2_i == value2_{i + 1} + * + */ + ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); + ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); + + 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 + + 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 + + /** + * Contributions 15,16,17 + * RAM Consistency Check + * + * The 'access' type of the record is extracted with the expression `w_4 - ap.partial_record_check` + * (i.e. for an honest Prover `w1 * eta + w2 * eta^2 + w3 * eta^3 - w4 = access`. + * This is validated by requiring `access` to be boolean + * + * For two adjacent entries in the sorted list if _both_ + * A) index values match + * B) adjacent access value is 0 (i.e. next gate is a READ) + * then + * C) both values must match. + * The gate boolean check is + * (A && B) => C === !(A && B) || C === !A || !B || C + * + * N.B. it is the responsibility of the circuit writer to ensure that every RAM cell is initialized + * 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 + + // TODO(https://github.com/AztecProtocol/barretenberg/issues/757): If we sorted in + // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta + // 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 + + // 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 + // do with an arithmetic gate because of the `eta` factors. We need to check that the *next* gate's access + // type is correct, to cover this edge case + // deg 2 or 4 + ap.next_gate_access_type_is_boolean = + 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 + + ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_ARITH)); // deg 3 or 9 + + /** + * RAM Timestamp Consistency Check + * + * | w1 | w2 | w3 | w4 | + * | index | timestamp | timestamp_check | -- | + * + * Let delta_index = index_{i + 1} - index_{i} + * + * Iff delta_index == 0, timestamp_check = timestamp_{i + 1} - timestamp_i + * Else timestamp_check = 0 + */ + ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); + ap.RAM_timestamp_check_identity = + (ap.index_delta * MINUS_ONE + Fr.wrap(1)) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 + + /** + * Complete Contribution 12 + * The complete RAM/ROM memory identity + * Partial degree: + */ + 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 + + // (deg 3 or 9) + (deg 4) + (deg 3) + ap.auxiliary_identity = ap.memory_identity + non_native_field_identity + limb_accumulator_identity; + ap.auxiliary_identity = ap.auxiliary_identity * (wire(p, WIRE.Q_AUX) * domainSep); // deg 4 or 10 + evals[12] = ap.auxiliary_identity; + } + + // Big todo for poseidon params, reduce them + struct PoseidonExternalParams { + Fr s1; + Fr s2; + Fr s3; + Fr s4; + Fr u1; + Fr u2; + Fr u3; + Fr u4; + Fr t0; + Fr t1; + Fr t2; + Fr t3; + Fr v1; + Fr v2; + Fr v3; + Fr v4; + Fr q_pos_by_scaling; + } + + 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 { + PoseidonExternalParams memory ep; + + ep.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L); + ep.s2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_R); + ep.s3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_O); + ep.s4 = wire(p, WIRE.W_4) + wire(p, WIRE.Q_4); + + ep.u1 = ep.s1 * ep.s1 * ep.s1 * ep.s1 * ep.s1; + ep.u2 = ep.s2 * ep.s2 * ep.s2 * ep.s2 * ep.s2; + ep.u3 = ep.s3 * ep.s3 * ep.s3 * ep.s3 * ep.s3; + ep.u4 = ep.s4 * ep.s4 * ep.s4 * ep.s4 * ep.s4; + // matrix mul v = M_E * u with 14 additions + ep.t0 = ep.u1 + ep.u2; // u_1 + u_2 + ep.t1 = ep.u3 + ep.u4; // u_3 + u_4 + ep.t2 = ep.u2 + ep.u2 + ep.t1; // 2u_2 + // ep.t2 += ep.t1; // 2u_2 + u_3 + u_4 + ep.t3 = ep.u4 + ep.u4 + ep.t0; // 2u_4 + // ep.t3 += ep.t0; // u_1 + u_2 + 2u_4 + ep.v4 = ep.t1 + ep.t1; + ep.v4 = ep.v4 + ep.v4 + ep.t3; + // ep.v4 += ep.t3; // u_1 + u_2 + 4u_3 + 6u_4 + ep.v2 = ep.t0 + ep.t0; + ep.v2 = ep.v2 + ep.v2 + ep.t2; + // ep.v2 += ep.t2; // 4u_1 + 6u_2 + u_3 + u_4 + ep.v1 = ep.t3 + ep.v2; // 5u_1 + 7u_2 + u_3 + 3u_4 + ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 + + ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; + evals[18] = evals[18] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + + evals[19] = evals[19] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + + evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + + evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + } + + struct PoseidonInternalParams { + Fr u1; + Fr u2; + Fr u3; + Fr u4; + Fr u_sum; + Fr v1; + Fr v2; + Fr v3; + Fr v4; + Fr s1; + Fr q_pos_by_scaling; + } + + function accumulatePoseidonInternalRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + PoseidonInternalParams memory ip; + + Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ + FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), + FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), + FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), + FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) + ]; + + // add round constants + ip.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L); + + // apply s-box round + ip.u1 = ip.s1 * ip.s1 * ip.s1 * ip.s1 * ip.s1; + ip.u2 = wire(p, WIRE.W_R); + ip.u3 = wire(p, WIRE.W_O); + ip.u4 = wire(p, WIRE.W_4); + + // matrix mul with v = M_I * u 4 muls and 7 additions + ip.u_sum = ip.u1 + ip.u2 + ip.u3 + ip.u4; + + ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; + + ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; + evals[22] = evals[22] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + + ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; + evals[23] = evals[23] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + + ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; + evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + + ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; + evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + } + + function scaleAndBatchSubrelations( + Fr[NUMBER_OF_SUBRELATIONS] memory evaluations, + Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges + ) internal pure returns (Fr accumulator) { + accumulator = accumulator + evaluations[0]; + + for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { + accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; + } + } +} diff --git a/barretenberg/sol/src/honk/Transcript.sol b/barretenberg/sol/src/honk/Transcript.sol index 2c209cf6305..0404d7c7e2a 100644 --- a/barretenberg/sol/src/honk/Transcript.sol +++ b/barretenberg/sol/src/honk/Transcript.sol @@ -1,3 +1,5 @@ +pragma solidity >=0.8.21; + import { Honk, NUMBER_OF_ALPHAS, @@ -6,7 +8,6 @@ import { CONST_PROOF_SIZE_LOG_N } from "./HonkTypes.sol"; import {Fr, FrLib} from "./Fr.sol"; -import {LOG_N, NUMBER_OF_PUBLIC_INPUTS} from "./keys/Add2HonkVerificationKey.sol"; // Transcript library to generate fiat shamir challenges struct Transcript { @@ -16,7 +17,7 @@ struct Transcript { Fr beta; Fr gamma; Fr[NUMBER_OF_ALPHAS] alphas; - Fr[LOG_N] gateChallenges; + Fr[CONST_PROOF_SIZE_LOG_N] gateChallenges; Fr[CONST_PROOF_SIZE_LOG_N] sumCheckUChallenges; Fr rho; // Zero morph @@ -29,12 +30,12 @@ struct Transcript { } library TranscriptLib { - function generateTranscript( - Honk.Proof memory proof, - Honk.VerificationKey memory vk, - bytes32[] calldata publicInputs - ) internal view returns (Transcript memory t) { - (t.eta, t.etaTwo, t.etaThree) = generateEtaChallenge(proof, publicInputs); + function generateTranscript(Honk.Proof memory proof, bytes32[] calldata publicInputs, uint256 publicInputsSize) + internal + view + returns (Transcript memory t) + { + (t.eta, t.etaTwo, t.etaThree) = generateEtaChallenge(proof, publicInputs, publicInputsSize); (t.beta, t.gamma) = generateBetaAndGammaChallenges(t.etaThree, proof); @@ -42,7 +43,7 @@ library TranscriptLib { t.gateChallenges = generateGateChallenges(t.alphas[NUMBER_OF_ALPHAS - 1]); - t.sumCheckUChallenges = generateSumcheckChallenges(proof, t.gateChallenges[LOG_N - 1]); + t.sumCheckUChallenges = generateSumcheckChallenges(proof, t.gateChallenges[CONST_PROOF_SIZE_LOG_N - 1]); t.rho = generateRhoChallenge(proof, t.sumCheckUChallenges[CONST_PROOF_SIZE_LOG_N - 1]); t.zmY = generateZMYChallenge(t.rho, proof); @@ -52,33 +53,33 @@ library TranscriptLib { return t; } - function generateEtaChallenge(Honk.Proof memory proof, bytes32[] calldata publicInputs) + function generateEtaChallenge(Honk.Proof memory proof, bytes32[] calldata publicInputs, uint256 publicInputsSize) internal view returns (Fr eta, Fr etaTwo, Fr etaThree) { - bytes32[3 + NUMBER_OF_PUBLIC_INPUTS + 12] memory round0; + bytes32[] memory round0 = new bytes32[](3 + publicInputsSize + 12); round0[0] = bytes32(proof.circuitSize); round0[1] = bytes32(proof.publicInputsSize); round0[2] = bytes32(proof.publicInputsOffset); - for (uint256 i = 0; i < NUMBER_OF_PUBLIC_INPUTS; i++) { + for (uint256 i = 0; i < publicInputsSize; i++) { round0[3 + i] = bytes32(publicInputs[i]); } // Create the first challenge // Note: w4 is added to the challenge later on - 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); + 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); eta = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); etaTwo = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(eta)))); @@ -136,8 +137,12 @@ library TranscriptLib { } } - function generateGateChallenges(Fr previousChallenge) internal view returns (Fr[LOG_N] memory gateChallenges) { - for (uint256 i = 0; i < LOG_N; i++) { + function generateGateChallenges(Fr previousChallenge) + internal + view + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges) + { + for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); gateChallenges[i] = previousChallenge; } @@ -194,7 +199,7 @@ library TranscriptLib { function generateZMXZChallenges(Fr previousChallenge, Honk.Proof memory proof) internal - view + pure returns (Fr zeromorphX, Fr zeromorphZ) { uint256[4 + 1] memory buf; @@ -208,4 +213,137 @@ library TranscriptLib { zeromorphX = FrLib.fromBytes32(keccak256(abi.encodePacked(buf))); zeromorphZ = FrLib.fromBytes32(keccak256(abi.encodePacked(zeromorphX))); } + + // TODO: mod q proof points + // TODO: Preprocess all of the memory locations + // TODO: Adjust proof point serde away from poseidon forced field elements + // TODO: move this back to probably each instance to avoid dynamic init of arrays in the Transcript Lib + 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); + // Zero morph Commitments + for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { + // Explicitly stating the x0, x1, y0, y1 start and end boundaries to make the calldata slicing bearable + 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.zmCqs[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 * 0x80); + + p.zmCq = 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])) + }); + + p.zmPi = Honk.G1ProofPoint({ + x_0: uint256(bytes32(proof[boundary + 0x80:boundary + 0xa0])), + x_1: uint256(bytes32(proof[boundary + 0xa0:boundary + 0xc0])), + y_0: uint256(bytes32(proof[boundary + 0xc0:boundary + 0xe0])), + y_1: uint256(bytes32(proof[boundary + 0xe0:boundary + 0x100])) + }); + + return p; + } } diff --git a/barretenberg/sol/src/honk/instance/Add2Honk.sol b/barretenberg/sol/src/honk/instance/Add2Honk.sol index 06be6aa1a25..9f554d10b54 100644 --- a/barretenberg/sol/src/honk/instance/Add2Honk.sol +++ b/barretenberg/sol/src/honk/instance/Add2Honk.sol @@ -1,7 +1,4 @@ // SPDX-License-Identifier: Apache-2.0 - -// NOTE: to work around solidity not allowing templates, we need a transcript class and proof class for each - // Copyright 2024 Aztec Labs pragma solidity >=0.8.21; @@ -14,222 +11,19 @@ import { NUMBER_OF_ENTITIES, NUMBER_OF_SUBRELATIONS, NUMBER_OF_ALPHAS, + NUMBER_UNSHIFTED, BATCHED_RELATION_PARTIAL_LENGTH, - CONST_PROOF_SIZE_LOG_N, - P, - Q + CONST_PROOF_SIZE_LOG_N } from "../HonkTypes.sol"; import {ecMul, ecAdd, ecSub, negateInplace, convertProofPoint} from "../utils.sol"; -// Field arithmetic libraries -import {Fr, FrLib} from "../Fr.sol"; - -// Transcript library to generate fiat shamir challenges -struct Transcript { - Fr eta; - Fr etaTwo; - Fr etaThree; - Fr beta; - Fr gamma; - Fr[NUMBER_OF_ALPHAS] alphas; - Fr[CONST_PROOF_SIZE_LOG_N] gateChallenges; - Fr[CONST_PROOF_SIZE_LOG_N] sumCheckUChallenges; - Fr rho; - // Zero morph - Fr zmX; - Fr zmY; - Fr zmZ; - Fr zmQuotient; - // Derived - Fr publicInputsDelta; -} - -library TranscriptLib { - function generateTranscript( - Honk.Proof memory proof, - Honk.VerificationKey memory vk, - bytes32[] calldata publicInputs - ) internal view returns (Transcript memory t) { - (t.eta, t.etaTwo, t.etaThree) = generateEtaChallenge(proof, publicInputs); - - (t.beta, t.gamma) = generateBetaAndGammaChallenges(t.etaThree, proof); - - t.alphas = generateAlphaChallenges(t.gamma, proof); - - t.gateChallenges = generateGateChallenges(t.alphas[NUMBER_OF_ALPHAS - 1]); - - t.sumCheckUChallenges = generateSumcheckChallenges(proof, t.gateChallenges[CONST_PROOF_SIZE_LOG_N - 1]); - t.rho = generateRhoChallenge(proof, t.sumCheckUChallenges[CONST_PROOF_SIZE_LOG_N - 1]); - - t.zmY = generateZMYChallenge(t.rho, proof); - - (t.zmX, t.zmZ) = generateZMXZChallenges(t.zmY, proof); - - return t; - } - - function generateEtaChallenge(Honk.Proof memory proof, bytes32[] calldata publicInputs) - internal - view - returns (Fr eta, Fr etaTwo, Fr etaThree) - { - bytes32[3 + NUMBER_OF_PUBLIC_INPUTS + 12] memory round0; - round0[0] = bytes32(proof.circuitSize); - round0[1] = bytes32(proof.publicInputsSize); - round0[2] = bytes32(proof.publicInputsOffset); - 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 + 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); - - eta = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); - etaTwo = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(eta)))); - etaThree = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(etaTwo)))); - } - - function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.Proof memory proof) - internal - view - returns (Fr beta, Fr gamma) - { - bytes32[13] memory round1; - round1[0] = FrLib.toBytes32(previousChallenge); - round1[1] = bytes32(proof.lookupReadCounts.x_0); - round1[2] = bytes32(proof.lookupReadCounts.x_1); - round1[3] = bytes32(proof.lookupReadCounts.y_0); - round1[4] = bytes32(proof.lookupReadCounts.y_1); - round1[5] = bytes32(proof.lookupReadTags.x_0); - round1[6] = bytes32(proof.lookupReadTags.x_1); - round1[7] = bytes32(proof.lookupReadTags.y_0); - round1[8] = bytes32(proof.lookupReadTags.y_1); - round1[9] = bytes32(proof.w4.x_0); - round1[10] = bytes32(proof.w4.x_1); - round1[11] = bytes32(proof.w4.y_0); - round1[12] = bytes32(proof.w4.y_1); - - beta = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); - gamma = FrLib.fromBytes32(keccak256(abi.encodePacked(beta))); - } - - // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges(Fr previousChallenge, Honk.Proof memory proof) - internal - view - returns (Fr[NUMBER_OF_ALPHAS] memory alphas) - { - // Generate the original sumcheck alpha 0 by hashing zPerm and zLookup - uint256[9] memory alpha0; - alpha0[0] = Fr.unwrap(previousChallenge); - alpha0[1] = proof.lookupInverses.x_0; - alpha0[2] = proof.lookupInverses.x_1; - alpha0[3] = proof.lookupInverses.y_0; - alpha0[4] = proof.lookupInverses.y_1; - alpha0[5] = proof.zPerm.x_0; - alpha0[6] = proof.zPerm.x_1; - alpha0[7] = proof.zPerm.y_0; - alpha0[8] = proof.zPerm.y_1; - - alphas[0] = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); - - Fr prevChallenge = alphas[0]; - for (uint256 i = 1; i < NUMBER_OF_ALPHAS; i++) { - prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(prevChallenge)))); - alphas[i] = prevChallenge; - } - } - - function generateGateChallenges(Fr previousChallenge) - internal - view - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges) - { - for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - gateChallenges[i] = previousChallenge; - } - } - - function generateSumcheckChallenges(Honk.Proof memory proof, Fr prevChallenge) - internal - view - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges) - { - for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { - Fr[BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; - univariateChal[0] = prevChallenge; - - // TODO(opt): memcpy - for (uint256 j = 0; j < BATCHED_RELATION_PARTIAL_LENGTH; j++) { - univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; - } - - sumcheckChallenges[i] = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); - prevChallenge = sumcheckChallenges[i]; - } - } - - function generateRhoChallenge(Honk.Proof memory proof, Fr prevChallenge) internal view returns (Fr rho) { - Fr[NUMBER_OF_ENTITIES + 1] memory rhoChallengeElements; - rhoChallengeElements[0] = prevChallenge; - - // TODO: memcpy - for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - rhoChallengeElements[i + 1] = proof.sumcheckEvaluations[i]; - } - - rho = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); - } - - function generateZMYChallenge(Fr previousChallenge, Honk.Proof memory proof) - internal - view - returns (Fr zeromorphY) - { - uint256[CONST_PROOF_SIZE_LOG_N * 4 + 1] memory zmY; - zmY[0] = Fr.unwrap(previousChallenge); - - for (uint256 i; i < CONST_PROOF_SIZE_LOG_N; ++i) { - zmY[1 + i * 4] = proof.zmCqs[i].x_0; - zmY[2 + i * 4] = proof.zmCqs[i].x_1; - zmY[3 + i * 4] = proof.zmCqs[i].y_0; - zmY[4 + i * 4] = proof.zmCqs[i].y_1; - } +// Field arithmetic libraries - prevent littering the code with modmul / addmul +import {MODULUS as P, MINUS_ONE, Fr, FrLib} from "../Fr.sol"; - zeromorphY = FrLib.fromBytes32(keccak256(abi.encodePacked(zmY))); - } +import {Transcript, TranscriptLib} from "../Transcript.sol"; - function generateZMXZChallenges(Fr previousChallenge, Honk.Proof memory proof) - internal - view - returns (Fr zeromorphX, Fr zeromorphZ) - { - uint256[4 + 1] memory buf; - buf[0] = Fr.unwrap(previousChallenge); - - buf[1] = proof.zmCq.x_0; - buf[2] = proof.zmCq.x_1; - buf[3] = proof.zmCq.y_0; - buf[4] = proof.zmCq.y_1; - - zeromorphX = FrLib.fromBytes32(keccak256(abi.encodePacked(buf))); - zeromorphZ = FrLib.fromBytes32(keccak256(abi.encodePacked(zeromorphX))); - } -} +import {RelationsLib} from "../Relations.sol"; // Errors error PublicInputsLengthWrong(); @@ -238,18 +32,16 @@ error ZeromorphFailed(); /// Smart contract verifier of honk proofs contract Add2HonkVerifier 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 = loadProof(proof); + 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, vk, publicInputs); + Transcript memory t = TranscriptLib.generateTranscript(p, publicInputs, vk.publicInputsSize); // Compute the public input delta t.publicInputsDelta = @@ -270,139 +62,6 @@ contract Add2HonkVerifier is IVerifier { return VK.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 - // TODO(md): update the log deriv prover commitment rounds - 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); - // Zero morph Commitments - for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { - // Explicitly stating the x0, x1, y0, y1 start and end boundaries to make the calldata slicing bearable - 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.zmCqs[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 * 0x80); - - p.zmCq = 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])) - }); - - p.zmPi = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[boundary + 0x80:boundary + 0xa0])), - x_1: uint256(bytes32(proof[boundary + 0xa0:boundary + 0xc0])), - y_0: uint256(bytes32(proof[boundary + 0xc0:boundary + 0xe0])), - y_1: uint256(bytes32(proof[boundary + 0xe0:boundary + 0x100])) - }); - - return p; - } - function computePublicInputDelta( bytes32[] memory publicInputs, Fr beta, @@ -452,7 +111,7 @@ contract Add2HonkVerifier is IVerifier { } // Last round - Fr grandHonkRelationSum = accumulateRelationEvaluations(proof, tp, powPartialEvaluation); + Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations(proof, tp, powPartialEvaluation); verified = (grandHonkRelationSum == roundTarget); } @@ -472,18 +131,27 @@ contract Add2HonkVerifier is IVerifier { returns (Fr targetSum) { // TODO: inline - Fr[7] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000002d0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff89), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000030), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffdd), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000030), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff89), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000002d0) + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff11), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000090), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff71), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000000f0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000013b0) ]; - Fr[7] 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[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 @@ -516,590 +184,13 @@ contract Add2HonkVerifier is IVerifier { // 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 - view + pure returns (Fr newEvaluation) { Fr univariateEval = Fr.wrap(1) + (roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); newEvaluation = currentEvaluation * univariateEval; } - // Calculate the contributions of each relation to the expected value of the full honk relation - // - // For each relation, we use the purported values ( the ones provided by the prover ) of the multivariates to - // calculate a contribution to the purported value of the full Honk relation. - // These are stored in the evaluations part of the proof object. - // We add these together, with the appropiate scaling factor ( the alphas calculated in challenges ) - // This value is checked against the final value of the target total sum - et voila! - function accumulateRelationEvaluations(Honk.Proof memory proof, Transcript memory tp, Fr powPartialEval) - internal - view - returns (Fr accumulator) - { - Fr[NUMBER_OF_ENTITIES] memory purportedEvaluations = proof.sumcheckEvaluations; - Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; - - // Accumulate all 6 custom gates - each with varying number of subrelations - accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePermutationRelation(purportedEvaluations, tp, evaluations, powPartialEval); - accumulateLogDerivativeLookupRelation(purportedEvaluations, tp, evaluations, powPartialEval); - accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateAuxillaryRelation(purportedEvaluations, tp, evaluations, powPartialEval); - - // Apply alpha challenges to challenge evaluations - // Returns grand honk realtion evaluation - accumulator = scaleAndBatchSubrelations(evaluations, tp.alphas); - } - - /** - * 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) { - return p[uint256(_wire)]; - } - - /** - * Ultra Arithmetic Relation - * - */ - function accumulateArithmeticRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal view { - // Relation 0 - Fr q_arith = wire(p, WIRE.Q_ARITH); - { - 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 + (q_arith - Fr.wrap(1)) * wire(p, WIRE.W_4_SHIFT); - accum = accum * q_arith; - accum = accum * domainSep; - evals[0] = accum; - } - - // Relation 1 - { - Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); - accum = accum * (q_arith - Fr.wrap(2)); - accum = accum * (q_arith - Fr.wrap(1)); - accum = accum * q_arith; - accum = accum * domainSep; - evals[1] = accum; - } - } - - function accumulatePermutationRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Transcript memory tp, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal view { - Fr grand_product_numerator; - Fr grand_product_denominator; - - { - Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * tp.beta + tp.gamma; - num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * tp.beta + tp.gamma); - num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * tp.beta + tp.gamma); - num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * tp.beta + tp.gamma); - - grand_product_numerator = num; - } - { - Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * tp.beta + tp.gamma; - den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * tp.beta + tp.gamma); - den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * tp.beta + tp.gamma); - den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * tp.beta + tp.gamma); - - grand_product_denominator = den; - } - - // Contribution 2 - { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; - - acc = acc - - ( - (wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * tp.publicInputsDelta)) - * grand_product_denominator - ); - acc = acc * domainSep; - evals[2] = acc; - } - - // Contribution 3 - { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; - evals[3] = acc; - } - } - - function accumulateLogDerivativeLookupRelation( - 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; - - // Calculate the write term (the table accumulation) - { - write_term = wire(p, WIRE.TABLE_1) + tp.gamma + (wire(p, WIRE.TABLE_2) * tp.eta) - + (wire(p, WIRE.TABLE_3) * tp.etaTwo) + (wire(p, WIRE.TABLE_4) * tp.etaThree); - } - - // Calculate the write term - { - Fr derived_entry_1 = wire(p, WIRE.W_L) + tp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); - - read_term = derived_entry_1 + (derived_entry_2 * tp.eta) + (derived_entry_3 * tp.etaTwo) - + (wire(p, WIRE.Q_O) * tp.etaThree); - } - - Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; - Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - - Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); - - // Inverse calculated correctly relation - Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; - accumulatorNone = accumulatorNone * domainSep; - - // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; - - evals[4] = accumulatorNone; - evals[5] = accumulatorOne; - } - - function accumulateDeltaRangeRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) 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); - - // Compute wire differences - Fr delta_1 = wire(p, WIRE.W_R) - wire(p, WIRE.W_L); - Fr delta_2 = wire(p, WIRE.W_O) - wire(p, WIRE.W_R); - Fr delta_3 = wire(p, WIRE.W_4) - wire(p, WIRE.W_O); - Fr delta_4 = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_4); - - // Contribution 6 - { - Fr acc = delta_1; - acc = acc * (delta_1 + minus_one); - acc = acc * (delta_1 + minus_two); - acc = acc * (delta_1 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[6] = acc; - } - - // Contribution 7 - { - Fr acc = delta_2; - acc = acc * (delta_2 + minus_one); - acc = acc * (delta_2 + minus_two); - acc = acc * (delta_2 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[7] = acc; - } - - // Contribution 8 - { - Fr acc = delta_3; - acc = acc * (delta_3 + minus_one); - acc = acc * (delta_3 + minus_two); - acc = acc * (delta_3 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[8] = acc; - } - - // Contribution 9 - { - Fr acc = delta_4; - acc = acc * (delta_4 + minus_one); - acc = acc * (delta_4 + minus_two); - acc = acc * (delta_4 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[9] = acc; - } - } - - struct EllipticParams { - // Points - Fr x_1; - Fr y_1; - Fr x_2; - Fr y_2; - Fr y_3; - Fr x_3; - // push accumulators into memory - Fr x_double_identity; - } - - 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); - - ep.x_2 = wire(p, WIRE.W_L_SHIFT); - ep.y_2 = wire(p, WIRE.W_4_SHIFT); - ep.y_3 = wire(p, WIRE.W_O_SHIFT); - ep.x_3 = wire(p, WIRE.W_R_SHIFT); - - Fr q_sign = wire(p, WIRE.Q_L); - Fr q_is_double = wire(p, WIRE.Q_M); - - // Contribution 10 point addition, x-coordinate check - // q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0 - Fr x_diff = (ep.x_2 - ep.x_1); - Fr y1_sqr = (ep.y_1 * ep.y_1); - { - // Move to top - Fr partialEval = domainSep; - - Fr y2_sqr = (ep.y_2 * ep.y_2); - Fr y1y2 = ep.y_1 * ep.y_2 * q_sign; - Fr x_add_identity = (ep.x_3 + ep.x_2 + ep.x_1); - x_add_identity = x_add_identity * x_diff * x_diff; - x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - - evals[10] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (Fr.wrap(1) - q_is_double); - } - - // Contribution 11 point addition, x-coordinate check - // q_elliptic * (q_sign * y1 + y3)(x2 - x1) + (x3 - x1)(y2 - q_sign * y1) = 0 - { - Fr y1_plus_y3 = ep.y_1 + ep.y_3; - Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; - evals[11] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (Fr.wrap(1) - q_is_double); - } - - // Contribution 10 point doubling, x-coordinate check - // (x3 + x1 + x1) (4y1*y1) - 9 * x1 * x1 * x1 * x1 = 0 - // N.B. we're using the equivalence x1*x1*x1 === y1*y1 - curve_b to reduce degree by 1 - { - Fr x_pow_4 = (y1_sqr + GRUMPKIN_CURVE_B_PARAMETER_NEGATED) * ep.x_1; - Fr y1_sqr_mul_4 = y1_sqr + y1_sqr; - y1_sqr_mul_4 = y1_sqr_mul_4 + y1_sqr_mul_4; - Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); - - // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; - evals[10] = evals[10] + acc; - } - - // Contribution 11 point doubling, y-coordinate check - // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 - { - Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); - evals[11] = evals[11] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; - } - } - - // Constants for the auxiliary relation - Fr constant LIMB_SIZE = Fr.wrap(uint256(1) << 68); - Fr constant SUBLIMB_SHIFT = Fr.wrap(uint256(1) << 14); - Fr constant MINUS_ONE = Fr.wrap(P - 1); - - // Parameters used within the Auxiliary Relation - // A struct is used to work around stack too deep. This relation has alot of variables - struct AuxParams { - Fr limb_subproduct; - Fr non_native_field_gate_1; - Fr non_native_field_gate_2; - Fr non_native_field_gate_3; - Fr limb_accumulator_1; - Fr limb_accumulator_2; - Fr memory_record_check; - Fr partial_record_check; - Fr next_gate_access_type; - Fr record_delta; - Fr index_delta; - Fr adjacent_values_match_if_adjacent_indices_match; - Fr adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation; - Fr access_check; - Fr next_gate_access_type_is_boolean; - Fr ROM_consistency_check_identity; - Fr RAM_consistency_check_identity; - Fr timestamp_delta; - Fr RAM_timestamp_check_identity; - Fr memory_identity; - Fr index_is_monotonically_increasing; - Fr auxiliary_identity; - } - - function accumulateAuxillaryRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Transcript memory tp, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { - AuxParams memory ap; - - /** - * Contribution 12 - * Non native field arithmetic gate 2 - * deg 4 - * - * _ _ - * / _ _ _ 14 \ - * q_2 . q_4 | (w_1 . w_2) + (w_1 . w_2) + (w_1 . w_4 + w_2 . w_3 - w_3) . 2 - w_3 - w_4 | - * \_ _/ - * - * - */ - ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = - (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); - - ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); - ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); - - ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); - - Fr non_native_field_identity = - ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; - non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); - - // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm - // deg 2 - ap.limb_accumulator_1 = wire(p, WIRE.W_R_SHIFT) * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L_SHIFT); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_O); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_R); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L); - ap.limb_accumulator_1 = ap.limb_accumulator_1 - wire(p, WIRE.W_4); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * wire(p, WIRE.Q_4); - - // ((((w3' * 2^14 + w2') * 2^14 + w1') * 2^14 + w4) * 2^14 + w3 - w4') * qm - // deg 2 - ap.limb_accumulator_2 = wire(p, WIRE.W_O_SHIFT) * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_R_SHIFT); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_L_SHIFT); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_4); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_O); - ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - - Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; - limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 - - /** - * MEMORY - * - * A RAM memory record contains a tuple of the following fields: - * * i: `index` of memory cell being accessed - * * t: `timestamp` of memory cell being accessed (used for RAM, set to 0 for ROM) - * * v: `value` of memory cell being accessed - * * a: `access` type of record. read: 0 = read, 1 = write - * * r: `record` of memory cell. record = access + index * eta + timestamp * eta_two + value * eta_three - * - * A ROM memory record contains a tuple of the following fields: - * * i: `index` of memory cell being accessed - * * v: `value1` of memory cell being accessed (ROM tables can store up to 2 values per index) - * * v2:`value2` of memory cell being accessed (ROM tables can store up to 2 values per index) - * * r: `record` of memory cell. record = index * eta + value2 * eta_two + value1 * eta_three - * - * When performing a read/write access, the values of i, t, v, v2, a, r are stored in the following wires + - * selectors, depending on whether the gate is a RAM read/write or a ROM read - * - * | gate type | i | v2/t | v | a | r | - * | --------- | -- | ----- | -- | -- | -- | - * | ROM | w1 | w2 | w3 | -- | w4 | - * | RAM | w1 | w2 | w3 | qc | w4 | - * - * (for accesses where `index` is a circuit constant, it is assumed the circuit will apply a copy constraint on - * `w2` to fix its value) - * - * - */ - - /** - * Memory Record Check - * Partial degree: 1 - * Total degree: 4 - * - * A ROM/ROM access gate can be evaluated with the identity: - * - * qc + w1 \eta + w2 \eta_two + w3 \eta_three - w4 = 0 - * - * For ROM gates, qc = 0 - */ - ap.memory_record_check = wire(p, WIRE.W_O) * tp.etaThree; - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * tp.etaTwo); - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * tp.eta); - ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); - ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 - ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); - - /** - * Contribution 13 & 14 - * ROM Consistency Check - * Partial degree: 1 - * Total degree: 4 - * - * For every ROM read, a set equivalence check is applied between the record witnesses, and a second set of - * records that are sorted. - * - * We apply the following checks for the sorted records: - * - * 1. w1, w2, w3 correctly map to 'index', 'v1, 'v2' for a given record value at w4 - * 2. index values for adjacent records are monotonically increasing - * 3. if, at gate i, index_i == index_{i + 1}, then value1_i == value1_{i + 1} and value2_i == value2_{i + 1} - * - */ - ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); - ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - - 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 - - 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 - - /** - * Contributions 15,16,17 - * RAM Consistency Check - * - * The 'access' type of the record is extracted with the expression `w_4 - ap.partial_record_check` - * (i.e. for an honest Prover `w1 * eta + w2 * eta^2 + w3 * eta^3 - w4 = access`. - * This is validated by requiring `access` to be boolean - * - * For two adjacent entries in the sorted list if _both_ - * A) index values match - * B) adjacent access value is 0 (i.e. next gate is a READ) - * then - * C) both values must match. - * The gate boolean check is - * (A && B) => C === !(A && B) || C === !A || !B || C - * - * N.B. it is the responsibility of the circuit writer to ensure that every RAM cell is initialized - * 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 - - // TODO(https://github.com/AztecProtocol/barretenberg/issues/757): If we sorted in - // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta - // 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 - - // 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 - // do with an arithmetic gate because of the `eta` factors. We need to check that the *next* gate's access - // type is correct, to cover this edge case - // deg 2 or 4 - ap.next_gate_access_type_is_boolean = - 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 - - ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_ARITH)); // deg 3 or 9 - - /** - * RAM Timestamp Consistency Check - * - * | w1 | w2 | w3 | w4 | - * | index | timestamp | timestamp_check | -- | - * - * Let delta_index = index_{i + 1} - index_{i} - * - * Iff delta_index == 0, timestamp_check = timestamp_{i + 1} - timestamp_i - * Else timestamp_check = 0 - */ - ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = - (ap.index_delta * MINUS_ONE + Fr.wrap(1)) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 - - /** - * Complete Contribution 12 - * The complete RAM/ROM memory identity - * Partial degree: - */ - 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 - - // (deg 3 or 9) + (deg 4) + (deg 3) - ap.auxiliary_identity = ap.memory_identity + non_native_field_identity + limb_accumulator_identity; - ap.auxiliary_identity = ap.auxiliary_identity * (wire(p, WIRE.Q_AUX) * domainSep); // deg 4 or 10 - evals[12] = ap.auxiliary_identity; - } - - function scaleAndBatchSubrelations( - Fr[NUMBER_OF_SUBRELATIONS] memory evaluations, - Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges - ) internal view returns (Fr accumulator) { - accumulator = accumulator + evaluations[0]; - - for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; - } - } - function verifyZeroMorph(Honk.Proof memory proof, Honk.VerificationKey memory vk, Transcript memory tp) internal view @@ -1185,12 +276,12 @@ contract Add2HonkVerifier is IVerifier { // f commitments are accumulated at (zm_x * r) cp.rho_pow = Fr.wrap(1); - for (uint256 i = 1; i < 34; ++i) { + for (uint256 i = 1; i <= NUMBER_UNSHIFTED; ++i) { scalars[i] = tp.zmX * cp.rho_pow; cp.rho_pow = cp.rho_pow * tp.rho; } // g commitments are accumulated at r - for (uint256 i = 34; i < 43; ++i) { + for (uint256 i = NUMBER_UNSHIFTED + 1; i <= NUMBER_OF_ENTITIES; ++i) { scalars[i] = cp.rho_pow; cp.rho_pow = cp.rho_pow * tp.rho; } @@ -1207,41 +298,43 @@ contract Add2HonkVerifier is IVerifier { commitments[9] = vk.qElliptic; commitments[10] = vk.qAux; commitments[11] = vk.qLookup; - commitments[12] = vk.s1; - commitments[13] = vk.s2; - commitments[14] = vk.s3; - commitments[15] = vk.s4; - commitments[16] = vk.id1; - commitments[17] = vk.id2; - commitments[18] = vk.id3; - commitments[19] = vk.id4; - commitments[20] = vk.t1; - commitments[21] = vk.t2; - commitments[22] = vk.t3; - commitments[23] = vk.t4; - commitments[24] = vk.lagrangeFirst; - commitments[25] = vk.lagrangeLast; + 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[26] = convertProofPoint(proof.w1); - commitments[27] = convertProofPoint(proof.w2); - commitments[28] = convertProofPoint(proof.w3); - commitments[29] = convertProofPoint(proof.w4); - commitments[30] = convertProofPoint(proof.zPerm); - commitments[31] = convertProofPoint(proof.lookupInverses); - commitments[32] = convertProofPoint(proof.lookupReadCounts); - commitments[33] = convertProofPoint(proof.lookupReadTags); + 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[34] = vk.t1; - commitments[35] = vk.t2; - commitments[36] = vk.t3; - commitments[37] = vk.t4; - commitments[38] = convertProofPoint(proof.w1); - commitments[39] = convertProofPoint(proof.w2); - commitments[40] = convertProofPoint(proof.w3); - commitments[41] = convertProofPoint(proof.w4); - commitments[42] = convertProofPoint(proof.zPerm); + 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); // Add scalar contributions // Add contributions: scalar * [q_k], k = 0,...,log_N, where @@ -1266,8 +359,8 @@ contract Add2HonkVerifier is IVerifier { cp.x_pow_2kp1 = cp.x_pow_2kp1 * cp.x_pow_2kp1; } - scalars[43 + k] = scalar; - commitments[43 + k] = convertProofPoint(proof.zmCqs[k]); + scalars[NUMBER_OF_ENTITIES + 1 + k] = scalar; + commitments[NUMBER_OF_ENTITIES + 1 + k] = convertProofPoint(proof.zmCqs[k]); } return batchMul2(commitments, scalars); @@ -1286,7 +379,7 @@ contract Add2HonkVerifier is IVerifier { let free := mload(0x40) // Write the original into the accumulator - // Load into memory for ecMUL, leave offset for eccAdd result + // Load into memory forecMUL, leave offset foreccAdd 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)))) diff --git a/barretenberg/sol/src/honk/instance/BlakeHonk.sol b/barretenberg/sol/src/honk/instance/BlakeHonk.sol index 3bc154de40b..f3b36f359b2 100644 --- a/barretenberg/sol/src/honk/instance/BlakeHonk.sol +++ b/barretenberg/sol/src/honk/instance/BlakeHonk.sol @@ -11,222 +11,19 @@ import { NUMBER_OF_ENTITIES, NUMBER_OF_SUBRELATIONS, NUMBER_OF_ALPHAS, + NUMBER_UNSHIFTED, BATCHED_RELATION_PARTIAL_LENGTH, - CONST_PROOF_SIZE_LOG_N, - P, - Q + 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 {Fr, FrLib} from "../Fr.sol"; - -// Transcript library to generate fiat shamir challenges -struct Transcript { - Fr eta; - Fr etaTwo; - Fr etaThree; - Fr beta; - Fr gamma; - Fr[NUMBER_OF_ALPHAS] alphas; - Fr[CONST_PROOF_SIZE_LOG_N] gateChallenges; - Fr[CONST_PROOF_SIZE_LOG_N] sumCheckUChallenges; - Fr rho; - // Zero morph - Fr zmX; - Fr zmY; - Fr zmZ; - Fr zmQuotient; - // Derived - Fr publicInputsDelta; -} - -library TranscriptLib { - function generateTranscript( - Honk.Proof memory proof, - Honk.VerificationKey memory vk, - bytes32[] calldata publicInputs - ) internal view returns (Transcript memory t) { - (t.eta, t.etaTwo, t.etaThree) = generateEtaChallenge(proof, publicInputs); - - (t.beta, t.gamma) = generateBetaAndGammaChallenges(t.etaThree, proof); - - t.alphas = generateAlphaChallenges(t.gamma, proof); - - t.gateChallenges = generateGateChallenges(t.alphas[NUMBER_OF_ALPHAS - 1]); - - t.sumCheckUChallenges = generateSumcheckChallenges(proof, t.gateChallenges[CONST_PROOF_SIZE_LOG_N - 1]); - t.rho = generateRhoChallenge(proof, t.sumCheckUChallenges[CONST_PROOF_SIZE_LOG_N - 1]); - - t.zmY = generateZMYChallenge(t.rho, proof); - - (t.zmX, t.zmZ) = generateZMXZChallenges(t.zmY, proof); - - return t; - } - - function generateEtaChallenge(Honk.Proof memory proof, bytes32[] calldata publicInputs) - internal - view - returns (Fr eta, Fr etaTwo, Fr etaThree) - { - bytes32[3 + NUMBER_OF_PUBLIC_INPUTS + 12] memory round0; - round0[0] = bytes32(proof.circuitSize); - round0[1] = bytes32(proof.publicInputsSize); - round0[2] = bytes32(proof.publicInputsOffset); - 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 + 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); - - eta = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); - etaTwo = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(eta)))); - etaThree = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(etaTwo)))); - } - - function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.Proof memory proof) - internal - view - returns (Fr beta, Fr gamma) - { - bytes32[13] memory round1; - round1[0] = FrLib.toBytes32(previousChallenge); - round1[1] = bytes32(proof.lookupReadCounts.x_0); - round1[2] = bytes32(proof.lookupReadCounts.x_1); - round1[3] = bytes32(proof.lookupReadCounts.y_0); - round1[4] = bytes32(proof.lookupReadCounts.y_1); - round1[5] = bytes32(proof.lookupReadTags.x_0); - round1[6] = bytes32(proof.lookupReadTags.x_1); - round1[7] = bytes32(proof.lookupReadTags.y_0); - round1[8] = bytes32(proof.lookupReadTags.y_1); - round1[9] = bytes32(proof.w4.x_0); - round1[10] = bytes32(proof.w4.x_1); - round1[11] = bytes32(proof.w4.y_0); - round1[12] = bytes32(proof.w4.y_1); - - beta = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); - gamma = FrLib.fromBytes32(keccak256(abi.encodePacked(beta))); - } - - // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges(Fr previousChallenge, Honk.Proof memory proof) - internal - view - returns (Fr[NUMBER_OF_ALPHAS] memory alphas) - { - // Generate the original sumcheck alpha 0 by hashing zPerm and zLookup - uint256[9] memory alpha0; - alpha0[0] = Fr.unwrap(previousChallenge); - alpha0[1] = proof.lookupInverses.x_0; - alpha0[2] = proof.lookupInverses.x_1; - alpha0[3] = proof.lookupInverses.y_0; - alpha0[4] = proof.lookupInverses.y_1; - alpha0[5] = proof.zPerm.x_0; - alpha0[6] = proof.zPerm.x_1; - alpha0[7] = proof.zPerm.y_0; - alpha0[8] = proof.zPerm.y_1; - - alphas[0] = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); - - Fr prevChallenge = alphas[0]; - for (uint256 i = 1; i < NUMBER_OF_ALPHAS; i++) { - prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(prevChallenge)))); - alphas[i] = prevChallenge; - } - } - - function generateGateChallenges(Fr previousChallenge) - internal - view - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges) - { - for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - gateChallenges[i] = previousChallenge; - } - } - - function generateSumcheckChallenges(Honk.Proof memory proof, Fr prevChallenge) - internal - view - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges) - { - for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { - Fr[BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; - univariateChal[0] = prevChallenge; - - // TODO(opt): memcpy - for (uint256 j = 0; j < BATCHED_RELATION_PARTIAL_LENGTH; j++) { - univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; - } +import {MODULUS as P, MINUS_ONE, Fr, FrLib} from "../Fr.sol"; - sumcheckChallenges[i] = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); - prevChallenge = sumcheckChallenges[i]; - } - } - - function generateRhoChallenge(Honk.Proof memory proof, Fr prevChallenge) internal view returns (Fr rho) { - Fr[NUMBER_OF_ENTITIES + 1] memory rhoChallengeElements; - rhoChallengeElements[0] = prevChallenge; +import {Transcript, TranscriptLib} from "../Transcript.sol"; - // TODO: memcpy - for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - rhoChallengeElements[i + 1] = proof.sumcheckEvaluations[i]; - } - - rho = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); - } - - function generateZMYChallenge(Fr previousChallenge, Honk.Proof memory proof) - internal - view - returns (Fr zeromorphY) - { - uint256[CONST_PROOF_SIZE_LOG_N * 4 + 1] memory zmY; - zmY[0] = Fr.unwrap(previousChallenge); - - for (uint256 i; i < CONST_PROOF_SIZE_LOG_N; ++i) { - zmY[1 + i * 4] = proof.zmCqs[i].x_0; - zmY[2 + i * 4] = proof.zmCqs[i].x_1; - zmY[3 + i * 4] = proof.zmCqs[i].y_0; - zmY[4 + i * 4] = proof.zmCqs[i].y_1; - } - - zeromorphY = FrLib.fromBytes32(keccak256(abi.encodePacked(zmY))); - } - - function generateZMXZChallenges(Fr previousChallenge, Honk.Proof memory proof) - internal - view - returns (Fr zeromorphX, Fr zeromorphZ) - { - uint256[4 + 1] memory buf; - buf[0] = Fr.unwrap(previousChallenge); - - buf[1] = proof.zmCq.x_0; - buf[2] = proof.zmCq.x_1; - buf[3] = proof.zmCq.y_0; - buf[4] = proof.zmCq.y_1; - - zeromorphX = FrLib.fromBytes32(keccak256(abi.encodePacked(buf))); - zeromorphZ = FrLib.fromBytes32(keccak256(abi.encodePacked(zeromorphX))); - } -} +import {RelationsLib} from "../Relations.sol"; // Errors error PublicInputsLengthWrong(); @@ -235,18 +32,16 @@ error ZeromorphFailed(); /// Smart contract verifier of honk proofs contract BlakeHonkVerifier 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 = loadProof(proof); + 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, vk, publicInputs); + Transcript memory t = TranscriptLib.generateTranscript(p, publicInputs, vk.publicInputsSize); // Compute the public input delta t.publicInputsDelta = @@ -267,138 +62,6 @@ contract BlakeHonkVerifier is IVerifier { return VK.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); - // Zero morph Commitments - for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { - // Explicitly stating the x0, x1, y0, y1 start and end boundaries to make the calldata slicing bearable - 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.zmCqs[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 * 0x80); - - p.zmCq = 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])) - }); - - p.zmPi = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[boundary + 0x80:boundary + 0xa0])), - x_1: uint256(bytes32(proof[boundary + 0xa0:boundary + 0xc0])), - y_0: uint256(bytes32(proof[boundary + 0xc0:boundary + 0xe0])), - y_1: uint256(bytes32(proof[boundary + 0xe0:boundary + 0x100])) - }); - - return p; - } - function computePublicInputDelta( bytes32[] memory publicInputs, Fr beta, @@ -448,7 +111,7 @@ contract BlakeHonkVerifier is IVerifier { } // Last round - Fr grandHonkRelationSum = accumulateRelationEvaluations(proof, tp, powPartialEvaluation); + Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations(proof, tp, powPartialEvaluation); verified = (grandHonkRelationSum == roundTarget); } @@ -468,18 +131,27 @@ contract BlakeHonkVerifier is IVerifier { returns (Fr targetSum) { // TODO: inline - Fr[7] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000002d0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff89), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000030), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffdd), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000030), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff89), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000002d0) + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff11), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000090), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff71), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000000f0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000013b0) ]; - Fr[7] 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[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 @@ -512,590 +184,13 @@ contract BlakeHonkVerifier is IVerifier { // 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 - view + pure returns (Fr newEvaluation) { Fr univariateEval = Fr.wrap(1) + (roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); newEvaluation = currentEvaluation * univariateEval; } - // Calculate the contributions of each relation to the expected value of the full honk relation - // - // For each relation, we use the purported values ( the ones provided by the prover ) of the multivariates to - // calculate a contribution to the purported value of the full Honk relation. - // These are stored in the evaluations part of the proof object. - // We add these together, with the appropiate scaling factor ( the alphas calculated in challenges ) - // This value is checked against the final value of the target total sum - et voila! - function accumulateRelationEvaluations(Honk.Proof memory proof, Transcript memory tp, Fr powPartialEval) - internal - view - returns (Fr accumulator) - { - Fr[NUMBER_OF_ENTITIES] memory purportedEvaluations = proof.sumcheckEvaluations; - Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; - - // Accumulate all 6 custom gates - each with varying number of subrelations - accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePermutationRelation(purportedEvaluations, tp, evaluations, powPartialEval); - accumulateLogDerivativeLookupRelation(purportedEvaluations, tp, evaluations, powPartialEval); - accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateAuxillaryRelation(purportedEvaluations, tp, evaluations, powPartialEval); - - // Apply alpha challenges to challenge evaluations - // Returns grand honk realtion evaluation - accumulator = scaleAndBatchSubrelations(evaluations, tp.alphas); - } - - /** - * 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) { - return p[uint256(_wire)]; - } - - /** - * Ultra Arithmetic Relation - * - */ - function accumulateArithmeticRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal view { - // Relation 0 - Fr q_arith = wire(p, WIRE.Q_ARITH); - { - 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 + (q_arith - Fr.wrap(1)) * wire(p, WIRE.W_4_SHIFT); - accum = accum * q_arith; - accum = accum * domainSep; - evals[0] = accum; - } - - // Relation 1 - { - Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); - accum = accum * (q_arith - Fr.wrap(2)); - accum = accum * (q_arith - Fr.wrap(1)); - accum = accum * q_arith; - accum = accum * domainSep; - evals[1] = accum; - } - } - - function accumulatePermutationRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Transcript memory tp, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal view { - Fr grand_product_numerator; - Fr grand_product_denominator; - - { - Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * tp.beta + tp.gamma; - num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * tp.beta + tp.gamma); - num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * tp.beta + tp.gamma); - num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * tp.beta + tp.gamma); - - grand_product_numerator = num; - } - { - Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * tp.beta + tp.gamma; - den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * tp.beta + tp.gamma); - den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * tp.beta + tp.gamma); - den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * tp.beta + tp.gamma); - - grand_product_denominator = den; - } - - // Contribution 2 - { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; - - acc = acc - - ( - (wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * tp.publicInputsDelta)) - * grand_product_denominator - ); - acc = acc * domainSep; - evals[2] = acc; - } - - // Contribution 3 - { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; - evals[3] = acc; - } - } - - function accumulateLogDerivativeLookupRelation( - 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; - - // Calculate the write term (the table accumulation) - { - write_term = wire(p, WIRE.TABLE_1) + tp.gamma + (wire(p, WIRE.TABLE_2) * tp.eta) - + (wire(p, WIRE.TABLE_3) * tp.etaTwo) + (wire(p, WIRE.TABLE_4) * tp.etaThree); - } - - // Calculate the write term - { - Fr derived_entry_1 = wire(p, WIRE.W_L) + tp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); - - read_term = derived_entry_1 + (derived_entry_2 * tp.eta) + (derived_entry_3 * tp.etaTwo) - + (wire(p, WIRE.Q_O) * tp.etaThree); - } - - Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; - Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - - Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); - - // Inverse calculated correctly relation - Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; - accumulatorNone = accumulatorNone * domainSep; - - // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; - - evals[4] = accumulatorNone; - evals[5] = accumulatorOne; - } - - function accumulateDeltaRangeRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) 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); - - // Compute wire differences - Fr delta_1 = wire(p, WIRE.W_R) - wire(p, WIRE.W_L); - Fr delta_2 = wire(p, WIRE.W_O) - wire(p, WIRE.W_R); - Fr delta_3 = wire(p, WIRE.W_4) - wire(p, WIRE.W_O); - Fr delta_4 = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_4); - - // Contribution 6 - { - Fr acc = delta_1; - acc = acc * (delta_1 + minus_one); - acc = acc * (delta_1 + minus_two); - acc = acc * (delta_1 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[6] = acc; - } - - // Contribution 7 - { - Fr acc = delta_2; - acc = acc * (delta_2 + minus_one); - acc = acc * (delta_2 + minus_two); - acc = acc * (delta_2 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[7] = acc; - } - - // Contribution 8 - { - Fr acc = delta_3; - acc = acc * (delta_3 + minus_one); - acc = acc * (delta_3 + minus_two); - acc = acc * (delta_3 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[8] = acc; - } - - // Contribution 9 - { - Fr acc = delta_4; - acc = acc * (delta_4 + minus_one); - acc = acc * (delta_4 + minus_two); - acc = acc * (delta_4 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[9] = acc; - } - } - - struct EllipticParams { - // Points - Fr x_1; - Fr y_1; - Fr x_2; - Fr y_2; - Fr y_3; - Fr x_3; - // push accumulators into memory - Fr x_double_identity; - } - - 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); - - ep.x_2 = wire(p, WIRE.W_L_SHIFT); - ep.y_2 = wire(p, WIRE.W_4_SHIFT); - ep.y_3 = wire(p, WIRE.W_O_SHIFT); - ep.x_3 = wire(p, WIRE.W_R_SHIFT); - - Fr q_sign = wire(p, WIRE.Q_L); - Fr q_is_double = wire(p, WIRE.Q_M); - - // Contribution 10 point addition, x-coordinate check - // q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0 - Fr x_diff = (ep.x_2 - ep.x_1); - Fr y1_sqr = (ep.y_1 * ep.y_1); - { - // Move to top - Fr partialEval = domainSep; - - Fr y2_sqr = (ep.y_2 * ep.y_2); - Fr y1y2 = ep.y_1 * ep.y_2 * q_sign; - Fr x_add_identity = (ep.x_3 + ep.x_2 + ep.x_1); - x_add_identity = x_add_identity * x_diff * x_diff; - x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - - evals[10] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (Fr.wrap(1) - q_is_double); - } - - // Contribution 11 point addition, x-coordinate check - // q_elliptic * (q_sign * y1 + y3)(x2 - x1) + (x3 - x1)(y2 - q_sign * y1) = 0 - { - Fr y1_plus_y3 = ep.y_1 + ep.y_3; - Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; - evals[11] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (Fr.wrap(1) - q_is_double); - } - - // Contribution 10 point doubling, x-coordinate check - // (x3 + x1 + x1) (4y1*y1) - 9 * x1 * x1 * x1 * x1 = 0 - // N.B. we're using the equivalence x1*x1*x1 === y1*y1 - curve_b to reduce degree by 1 - { - Fr x_pow_4 = (y1_sqr + GRUMPKIN_CURVE_B_PARAMETER_NEGATED) * ep.x_1; - Fr y1_sqr_mul_4 = y1_sqr + y1_sqr; - y1_sqr_mul_4 = y1_sqr_mul_4 + y1_sqr_mul_4; - Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); - - // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; - evals[10] = evals[10] + acc; - } - - // Contribution 11 point doubling, y-coordinate check - // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 - { - Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); - evals[11] = evals[11] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; - } - } - - // Constants for the auxiliary relation - Fr constant LIMB_SIZE = Fr.wrap(uint256(1) << 68); - Fr constant SUBLIMB_SHIFT = Fr.wrap(uint256(1) << 14); - Fr constant MINUS_ONE = Fr.wrap(P - 1); - - // Parameters used within the Auxiliary Relation - // A struct is used to work around stack too deep. This relation has alot of variables - struct AuxParams { - Fr limb_subproduct; - Fr non_native_field_gate_1; - Fr non_native_field_gate_2; - Fr non_native_field_gate_3; - Fr limb_accumulator_1; - Fr limb_accumulator_2; - Fr memory_record_check; - Fr partial_record_check; - Fr next_gate_access_type; - Fr record_delta; - Fr index_delta; - Fr adjacent_values_match_if_adjacent_indices_match; - Fr adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation; - Fr access_check; - Fr next_gate_access_type_is_boolean; - Fr ROM_consistency_check_identity; - Fr RAM_consistency_check_identity; - Fr timestamp_delta; - Fr RAM_timestamp_check_identity; - Fr memory_identity; - Fr index_is_monotonically_increasing; - Fr auxiliary_identity; - } - - function accumulateAuxillaryRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Transcript memory tp, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { - AuxParams memory ap; - - /** - * Contribution 12 - * Non native field arithmetic gate 2 - * deg 4 - * - * _ _ - * / _ _ _ 14 \ - * q_2 . q_4 | (w_1 . w_2) + (w_1 . w_2) + (w_1 . w_4 + w_2 . w_3 - w_3) . 2 - w_3 - w_4 | - * \_ _/ - * - * - */ - ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = - (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); - - ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); - ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); - - ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); - - Fr non_native_field_identity = - ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; - non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); - - // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm - // deg 2 - ap.limb_accumulator_1 = wire(p, WIRE.W_R_SHIFT) * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L_SHIFT); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_O); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_R); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L); - ap.limb_accumulator_1 = ap.limb_accumulator_1 - wire(p, WIRE.W_4); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * wire(p, WIRE.Q_4); - - // ((((w3' * 2^14 + w2') * 2^14 + w1') * 2^14 + w4) * 2^14 + w3 - w4') * qm - // deg 2 - ap.limb_accumulator_2 = wire(p, WIRE.W_O_SHIFT) * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_R_SHIFT); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_L_SHIFT); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_4); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_O); - ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - - Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; - limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 - - /** - * MEMORY - * - * A RAM memory record contains a tuple of the following fields: - * * i: `index` of memory cell being accessed - * * t: `timestamp` of memory cell being accessed (used for RAM, set to 0 for ROM) - * * v: `value` of memory cell being accessed - * * a: `access` type of record. read: 0 = read, 1 = write - * * r: `record` of memory cell. record = access + index * eta + timestamp * eta_two + value * eta_three - * - * A ROM memory record contains a tuple of the following fields: - * * i: `index` of memory cell being accessed - * * v: `value1` of memory cell being accessed (ROM tables can store up to 2 values per index) - * * v2:`value2` of memory cell being accessed (ROM tables can store up to 2 values per index) - * * r: `record` of memory cell. record = index * eta + value2 * eta_two + value1 * eta_three - * - * When performing a read/write access, the values of i, t, v, v2, a, r are stored in the following wires + - * selectors, depending on whether the gate is a RAM read/write or a ROM read - * - * | gate type | i | v2/t | v | a | r | - * | --------- | -- | ----- | -- | -- | -- | - * | ROM | w1 | w2 | w3 | -- | w4 | - * | RAM | w1 | w2 | w3 | qc | w4 | - * - * (for accesses where `index` is a circuit constant, it is assumed the circuit will apply a copy constraint on - * `w2` to fix its value) - * - * - */ - - /** - * Memory Record Check - * Partial degree: 1 - * Total degree: 4 - * - * A ROM/ROM access gate can be evaluated with the identity: - * - * qc + w1 \eta + w2 \eta_two + w3 \eta_three - w4 = 0 - * - * For ROM gates, qc = 0 - */ - ap.memory_record_check = wire(p, WIRE.W_O) * tp.etaThree; - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * tp.etaTwo); - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * tp.eta); - ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); - ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 - ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); - - /** - * Contribution 13 & 14 - * ROM Consistency Check - * Partial degree: 1 - * Total degree: 4 - * - * For every ROM read, a set equivalence check is applied between the record witnesses, and a second set of - * records that are sorted. - * - * We apply the following checks for the sorted records: - * - * 1. w1, w2, w3 correctly map to 'index', 'v1, 'v2' for a given record value at w4 - * 2. index values for adjacent records are monotonically increasing - * 3. if, at gate i, index_i == index_{i + 1}, then value1_i == value1_{i + 1} and value2_i == value2_{i + 1} - * - */ - ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); - ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - - 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 - - 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 - - /** - * Contributions 15,16,17 - * RAM Consistency Check - * - * The 'access' type of the record is extracted with the expression `w_4 - ap.partial_record_check` - * (i.e. for an honest Prover `w1 * eta + w2 * eta^2 + w3 * eta^3 - w4 = access`. - * This is validated by requiring `access` to be boolean - * - * For two adjacent entries in the sorted list if _both_ - * A) index values match - * B) adjacent access value is 0 (i.e. next gate is a READ) - * then - * C) both values must match. - * The gate boolean check is - * (A && B) => C === !(A && B) || C === !A || !B || C - * - * N.B. it is the responsibility of the circuit writer to ensure that every RAM cell is initialized - * 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 - - // TODO(https://github.com/AztecProtocol/barretenberg/issues/757): If we sorted in - // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta - // 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 - - // 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 - // do with an arithmetic gate because of the `eta` factors. We need to check that the *next* gate's access - // type is correct, to cover this edge case - // deg 2 or 4 - ap.next_gate_access_type_is_boolean = - 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 - - ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_ARITH)); // deg 3 or 9 - - /** - * RAM Timestamp Consistency Check - * - * | w1 | w2 | w3 | w4 | - * | index | timestamp | timestamp_check | -- | - * - * Let delta_index = index_{i + 1} - index_{i} - * - * Iff delta_index == 0, timestamp_check = timestamp_{i + 1} - timestamp_i - * Else timestamp_check = 0 - */ - ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = - (ap.index_delta * MINUS_ONE + Fr.wrap(1)) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 - - /** - * Complete Contribution 12 - * The complete RAM/ROM memory identity - * Partial degree: - */ - 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 - - // (deg 3 or 9) + (deg 4) + (deg 3) - ap.auxiliary_identity = ap.memory_identity + non_native_field_identity + limb_accumulator_identity; - ap.auxiliary_identity = ap.auxiliary_identity * (wire(p, WIRE.Q_AUX) * domainSep); // deg 4 or 10 - evals[12] = ap.auxiliary_identity; - } - - function scaleAndBatchSubrelations( - Fr[NUMBER_OF_SUBRELATIONS] memory evaluations, - Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges - ) internal view returns (Fr accumulator) { - accumulator = accumulator + evaluations[0]; - - for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; - } - } - function verifyZeroMorph(Honk.Proof memory proof, Honk.VerificationKey memory vk, Transcript memory tp) internal view @@ -1117,6 +212,7 @@ contract BlakeHonkVerifier is IVerifier { Honk.G1Point memory c_zeta_Z = ecAdd(c_zeta, ecMul(c_zeta_x, tp.zmZ)); // KZG pairing accumulator + // WORKTODO: concerned that this is zero - it is multiplied by a point later on Fr evaluation = Fr.wrap(0); verified = zkgReduceVerify(proof, tp, evaluation, c_zeta_Z); } @@ -1181,12 +277,12 @@ contract BlakeHonkVerifier is IVerifier { // f commitments are accumulated at (zm_x * r) cp.rho_pow = Fr.wrap(1); - for (uint256 i = 1; i < 34; ++i) { + for (uint256 i = 1; i <= NUMBER_UNSHIFTED; ++i) { scalars[i] = tp.zmX * cp.rho_pow; cp.rho_pow = cp.rho_pow * tp.rho; } // g commitments are accumulated at r - for (uint256 i = 34; i < 43; ++i) { + for (uint256 i = NUMBER_UNSHIFTED + 1; i <= NUMBER_OF_ENTITIES; ++i) { scalars[i] = cp.rho_pow; cp.rho_pow = cp.rho_pow * tp.rho; } @@ -1203,41 +299,43 @@ contract BlakeHonkVerifier is IVerifier { commitments[9] = vk.qElliptic; commitments[10] = vk.qAux; commitments[11] = vk.qLookup; - commitments[12] = vk.s1; - commitments[13] = vk.s2; - commitments[14] = vk.s3; - commitments[15] = vk.s4; - commitments[16] = vk.id1; - commitments[17] = vk.id2; - commitments[18] = vk.id3; - commitments[19] = vk.id4; - commitments[20] = vk.t1; - commitments[21] = vk.t2; - commitments[22] = vk.t3; - commitments[23] = vk.t4; - commitments[24] = vk.lagrangeFirst; - commitments[25] = vk.lagrangeLast; + 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[26] = convertProofPoint(proof.w1); - commitments[27] = convertProofPoint(proof.w2); - commitments[28] = convertProofPoint(proof.w3); - commitments[29] = convertProofPoint(proof.w4); - commitments[30] = convertProofPoint(proof.zPerm); - commitments[31] = convertProofPoint(proof.lookupInverses); - commitments[32] = convertProofPoint(proof.lookupReadCounts); - commitments[33] = convertProofPoint(proof.lookupReadTags); + 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[34] = vk.t1; - commitments[35] = vk.t2; - commitments[36] = vk.t3; - commitments[37] = vk.t4; - commitments[38] = convertProofPoint(proof.w1); - commitments[39] = convertProofPoint(proof.w2); - commitments[40] = convertProofPoint(proof.w3); - commitments[41] = convertProofPoint(proof.w4); - commitments[42] = convertProofPoint(proof.zPerm); + 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); // Add scalar contributions // Add contributions: scalar * [q_k], k = 0,...,log_N, where @@ -1262,8 +360,8 @@ contract BlakeHonkVerifier is IVerifier { cp.x_pow_2kp1 = cp.x_pow_2kp1 * cp.x_pow_2kp1; } - scalars[43 + k] = scalar; - commitments[43 + k] = convertProofPoint(proof.zmCqs[k]); + scalars[NUMBER_OF_ENTITIES + 1 + k] = scalar; + commitments[NUMBER_OF_ENTITIES + 1 + k] = convertProofPoint(proof.zmCqs[k]); } return batchMul2(commitments, scalars); @@ -1282,7 +380,7 @@ contract BlakeHonkVerifier is IVerifier { let free := mload(0x40) // Write the original into the accumulator - // Load into memory for ecMUL, leave offset for eccAdd result + // Load into memory forecMUL, leave offset foreccAdd 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)))) @@ -1322,7 +420,7 @@ contract BlakeHonkVerifier is IVerifier { let free := mload(0x40) // Write the original into the accumulator - // Load into memory for ecMUL, leave offset for eccAdd result + // Load into memory forecMUL, leave offset foreccAdd 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)))) diff --git a/barretenberg/sol/src/honk/instance/EcdsaHonk.sol b/barretenberg/sol/src/honk/instance/EcdsaHonk.sol index 92dd87aa01a..1dc716151cd 100644 --- a/barretenberg/sol/src/honk/instance/EcdsaHonk.sol +++ b/barretenberg/sol/src/honk/instance/EcdsaHonk.sol @@ -11,223 +11,19 @@ import { NUMBER_OF_ENTITIES, NUMBER_OF_SUBRELATIONS, NUMBER_OF_ALPHAS, + NUMBER_UNSHIFTED, BATCHED_RELATION_PARTIAL_LENGTH, - CONST_PROOF_SIZE_LOG_N, - P, - Q + 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 {Fr, FrLib} from "../Fr.sol"; - -// Transcript library to generate fiat shamir challenges -struct Transcript { - Fr eta; - Fr etaTwo; - Fr etaThree; - Fr beta; - Fr gamma; - Fr[NUMBER_OF_ALPHAS] alphas; - Fr[CONST_PROOF_SIZE_LOG_N] gateChallenges; - Fr[CONST_PROOF_SIZE_LOG_N] sumCheckUChallenges; - Fr rho; - // Zero morph - Fr zmX; - Fr zmY; - Fr zmZ; - Fr zmQuotient; - // Derived - Fr publicInputsDelta; - Fr lookupGrandProductDelta; -} - -library TranscriptLib { - function generateTranscript( - Honk.Proof memory proof, - Honk.VerificationKey memory vk, - bytes32[] calldata publicInputs - ) internal view returns (Transcript memory t) { - (t.eta, t.etaTwo, t.etaThree) = generateEtaChallenge(proof, publicInputs); - - (t.beta, t.gamma) = generateBetaAndGammaChallenges(t.etaThree, proof); - - t.alphas = generateAlphaChallenges(t.gamma, proof); - - t.gateChallenges = generateGateChallenges(t.alphas[NUMBER_OF_ALPHAS - 1]); - - t.sumCheckUChallenges = generateSumcheckChallenges(proof, t.gateChallenges[CONST_PROOF_SIZE_LOG_N - 1]); - t.rho = generateRhoChallenge(proof, t.sumCheckUChallenges[CONST_PROOF_SIZE_LOG_N - 1]); - - t.zmY = generateZMYChallenge(t.rho, proof); - - (t.zmX, t.zmZ) = generateZMXZChallenges(t.zmY, proof); - - return t; - } - - function generateEtaChallenge(Honk.Proof memory proof, bytes32[] calldata publicInputs) - internal - view - returns (Fr eta, Fr etaTwo, Fr etaThree) - { - bytes32[3 + NUMBER_OF_PUBLIC_INPUTS + 12] memory round0; - round0[0] = bytes32(proof.circuitSize); - round0[1] = bytes32(proof.publicInputsSize); - round0[2] = bytes32(proof.publicInputsOffset); - 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 + 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); - - eta = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); - etaTwo = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(eta)))); - etaThree = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(etaTwo)))); - } - - function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.Proof memory proof) - internal - view - returns (Fr beta, Fr gamma) - { - bytes32[13] memory round1; - round1[0] = FrLib.toBytes32(previousChallenge); - round1[1] = bytes32(proof.lookupReadCounts.x_0); - round1[2] = bytes32(proof.lookupReadCounts.x_1); - round1[3] = bytes32(proof.lookupReadCounts.y_0); - round1[4] = bytes32(proof.lookupReadCounts.y_1); - round1[5] = bytes32(proof.lookupReadTags.x_0); - round1[6] = bytes32(proof.lookupReadTags.x_1); - round1[7] = bytes32(proof.lookupReadTags.y_0); - round1[8] = bytes32(proof.lookupReadTags.y_1); - round1[9] = bytes32(proof.w4.x_0); - round1[10] = bytes32(proof.w4.x_1); - round1[11] = bytes32(proof.w4.y_0); - round1[12] = bytes32(proof.w4.y_1); - - beta = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); - gamma = FrLib.fromBytes32(keccak256(abi.encodePacked(beta))); - } - - // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges(Fr previousChallenge, Honk.Proof memory proof) - internal - view - returns (Fr[NUMBER_OF_ALPHAS] memory alphas) - { - // Generate the original sumcheck alpha 0 by hashing zPerm and zLookup - uint256[9] memory alpha0; - alpha0[0] = Fr.unwrap(previousChallenge); - alpha0[1] = proof.lookupInverses.x_0; - alpha0[2] = proof.lookupInverses.x_1; - alpha0[3] = proof.lookupInverses.y_0; - alpha0[4] = proof.lookupInverses.y_1; - alpha0[5] = proof.zPerm.x_0; - alpha0[6] = proof.zPerm.x_1; - alpha0[7] = proof.zPerm.y_0; - alpha0[8] = proof.zPerm.y_1; - - alphas[0] = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); - - Fr prevChallenge = alphas[0]; - for (uint256 i = 1; i < NUMBER_OF_ALPHAS; i++) { - prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(prevChallenge)))); - alphas[i] = prevChallenge; - } - } - - function generateGateChallenges(Fr previousChallenge) - internal - view - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges) - { - for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - gateChallenges[i] = previousChallenge; - } - } - - function generateSumcheckChallenges(Honk.Proof memory proof, Fr prevChallenge) - internal - view - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges) - { - for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { - Fr[BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; - univariateChal[0] = prevChallenge; - - // TODO(opt): memcpy - for (uint256 j = 0; j < BATCHED_RELATION_PARTIAL_LENGTH; j++) { - univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; - } +import {MODULUS as P, MINUS_ONE, Fr, FrLib} from "../Fr.sol"; - sumcheckChallenges[i] = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); - prevChallenge = sumcheckChallenges[i]; - } - } - - function generateRhoChallenge(Honk.Proof memory proof, Fr prevChallenge) internal view returns (Fr rho) { - Fr[NUMBER_OF_ENTITIES + 1] memory rhoChallengeElements; - rhoChallengeElements[0] = prevChallenge; +import {Transcript, TranscriptLib} from "../Transcript.sol"; - // TODO: memcpy - for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - rhoChallengeElements[i + 1] = proof.sumcheckEvaluations[i]; - } - - rho = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); - } - - function generateZMYChallenge(Fr previousChallenge, Honk.Proof memory proof) - internal - view - returns (Fr zeromorphY) - { - uint256[CONST_PROOF_SIZE_LOG_N * 4 + 1] memory zmY; - zmY[0] = Fr.unwrap(previousChallenge); - - for (uint256 i; i < CONST_PROOF_SIZE_LOG_N; ++i) { - zmY[1 + i * 4] = proof.zmCqs[i].x_0; - zmY[2 + i * 4] = proof.zmCqs[i].x_1; - zmY[3 + i * 4] = proof.zmCqs[i].y_0; - zmY[4 + i * 4] = proof.zmCqs[i].y_1; - } - - zeromorphY = FrLib.fromBytes32(keccak256(abi.encodePacked(zmY))); - } - - function generateZMXZChallenges(Fr previousChallenge, Honk.Proof memory proof) - internal - view - returns (Fr zeromorphX, Fr zeromorphZ) - { - uint256[4 + 1] memory buf; - buf[0] = Fr.unwrap(previousChallenge); - - buf[1] = proof.zmCq.x_0; - buf[2] = proof.zmCq.x_1; - buf[3] = proof.zmCq.y_0; - buf[4] = proof.zmCq.y_1; - - zeromorphX = FrLib.fromBytes32(keccak256(abi.encodePacked(buf))); - zeromorphZ = FrLib.fromBytes32(keccak256(abi.encodePacked(zeromorphX))); - } -} +import {RelationsLib} from "../Relations.sol"; // Errors error PublicInputsLengthWrong(); @@ -236,18 +32,16 @@ error ZeromorphFailed(); /// Smart contract verifier of honk proofs contract EcdsaHonkVerifier 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 = loadProof(proof); + 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, vk, publicInputs); + Transcript memory t = TranscriptLib.generateTranscript(p, publicInputs, vk.publicInputsSize); // Compute the public input delta t.publicInputsDelta = @@ -268,138 +62,6 @@ contract EcdsaHonkVerifier is IVerifier { return VK.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); - // Zero morph Commitments - for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { - // Explicitly stating the x0, x1, y0, y1 start and end boundaries to make the calldata slicing bearable - 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.zmCqs[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 * 0x80); - - p.zmCq = 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])) - }); - - p.zmPi = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[boundary + 0x80:boundary + 0xa0])), - x_1: uint256(bytes32(proof[boundary + 0xa0:boundary + 0xc0])), - y_0: uint256(bytes32(proof[boundary + 0xc0:boundary + 0xe0])), - y_1: uint256(bytes32(proof[boundary + 0xe0:boundary + 0x100])) - }); - - return p; - } - function computePublicInputDelta( bytes32[] memory publicInputs, Fr beta, @@ -449,7 +111,7 @@ contract EcdsaHonkVerifier is IVerifier { } // Last round - Fr grandHonkRelationSum = accumulateRelationEvaluations(proof, tp, powPartialEvaluation); + Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations(proof, tp, powPartialEvaluation); verified = (grandHonkRelationSum == roundTarget); } @@ -469,18 +131,27 @@ contract EcdsaHonkVerifier is IVerifier { returns (Fr targetSum) { // TODO: inline - Fr[7] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000002d0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff89), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000030), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffdd), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000030), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff89), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000002d0) + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff11), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000090), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffff71), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000000f0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000013b0) ]; - Fr[7] 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[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 @@ -513,590 +184,13 @@ contract EcdsaHonkVerifier is IVerifier { // 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 - view + pure returns (Fr newEvaluation) { Fr univariateEval = Fr.wrap(1) + (roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); newEvaluation = currentEvaluation * univariateEval; } - // Calculate the contributions of each relation to the expected value of the full honk relation - // - // For each relation, we use the purported values ( the ones provided by the prover ) of the multivariates to - // calculate a contribution to the purported value of the full Honk relation. - // These are stored in the evaluations part of the proof object. - // We add these together, with the appropiate scaling factor ( the alphas calculated in challenges ) - // This value is checked against the final value of the target total sum - et voila! - function accumulateRelationEvaluations(Honk.Proof memory proof, Transcript memory tp, Fr powPartialEval) - internal - view - returns (Fr accumulator) - { - Fr[NUMBER_OF_ENTITIES] memory purportedEvaluations = proof.sumcheckEvaluations; - Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; - - // Accumulate all 6 custom gates - each with varying number of subrelations - accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePermutationRelation(purportedEvaluations, tp, evaluations, powPartialEval); - accumulateLogDerivativeLookupRelation(purportedEvaluations, tp, evaluations, powPartialEval); - accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateAuxillaryRelation(purportedEvaluations, tp, evaluations, powPartialEval); - - // Apply alpha challenges to challenge evaluations - // Returns grand honk realtion evaluation - accumulator = scaleAndBatchSubrelations(evaluations, tp.alphas); - } - - /** - * 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) { - return p[uint256(_wire)]; - } - - /** - * Ultra Arithmetic Relation - * - */ - function accumulateArithmeticRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal view { - // Relation 0 - Fr q_arith = wire(p, WIRE.Q_ARITH); - { - 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 + (q_arith - Fr.wrap(1)) * wire(p, WIRE.W_4_SHIFT); - accum = accum * q_arith; - accum = accum * domainSep; - evals[0] = accum; - } - - // Relation 1 - { - Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); - accum = accum * (q_arith - Fr.wrap(2)); - accum = accum * (q_arith - Fr.wrap(1)); - accum = accum * q_arith; - accum = accum * domainSep; - evals[1] = accum; - } - } - - function accumulatePermutationRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Transcript memory tp, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal view { - Fr grand_product_numerator; - Fr grand_product_denominator; - - { - Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * tp.beta + tp.gamma; - num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * tp.beta + tp.gamma); - num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * tp.beta + tp.gamma); - num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * tp.beta + tp.gamma); - - grand_product_numerator = num; - } - { - Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * tp.beta + tp.gamma; - den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * tp.beta + tp.gamma); - den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * tp.beta + tp.gamma); - den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * tp.beta + tp.gamma); - - grand_product_denominator = den; - } - - // Contribution 2 - { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; - - acc = acc - - ( - (wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * tp.publicInputsDelta)) - * grand_product_denominator - ); - acc = acc * domainSep; - evals[2] = acc; - } - - // Contribution 3 - { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; - evals[3] = acc; - } - } - - function accumulateLogDerivativeLookupRelation( - 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; - - // Calculate the write term (the table accumulation) - { - write_term = wire(p, WIRE.TABLE_1) + tp.gamma + (wire(p, WIRE.TABLE_2) * tp.eta) - + (wire(p, WIRE.TABLE_3) * tp.etaTwo) + (wire(p, WIRE.TABLE_4) * tp.etaThree); - } - - // Calculate the write term - { - Fr derived_entry_1 = wire(p, WIRE.W_L) + tp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); - - read_term = derived_entry_1 + (derived_entry_2 * tp.eta) + (derived_entry_3 * tp.etaTwo) - + (wire(p, WIRE.Q_O) * tp.etaThree); - } - - Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; - Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - - Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); - - // Inverse calculated correctly relation - Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; - accumulatorNone = accumulatorNone * domainSep; - - // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; - - evals[4] = accumulatorNone; - evals[5] = accumulatorOne; - } - - function accumulateDeltaRangeRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) 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); - - // Compute wire differences - Fr delta_1 = wire(p, WIRE.W_R) - wire(p, WIRE.W_L); - Fr delta_2 = wire(p, WIRE.W_O) - wire(p, WIRE.W_R); - Fr delta_3 = wire(p, WIRE.W_4) - wire(p, WIRE.W_O); - Fr delta_4 = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_4); - - // Contribution 6 - { - Fr acc = delta_1; - acc = acc * (delta_1 + minus_one); - acc = acc * (delta_1 + minus_two); - acc = acc * (delta_1 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[6] = acc; - } - - // Contribution 7 - { - Fr acc = delta_2; - acc = acc * (delta_2 + minus_one); - acc = acc * (delta_2 + minus_two); - acc = acc * (delta_2 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[7] = acc; - } - - // Contribution 8 - { - Fr acc = delta_3; - acc = acc * (delta_3 + minus_one); - acc = acc * (delta_3 + minus_two); - acc = acc * (delta_3 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[8] = acc; - } - - // Contribution 9 - { - Fr acc = delta_4; - acc = acc * (delta_4 + minus_one); - acc = acc * (delta_4 + minus_two); - acc = acc * (delta_4 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[9] = acc; - } - } - - struct EllipticParams { - // Points - Fr x_1; - Fr y_1; - Fr x_2; - Fr y_2; - Fr y_3; - Fr x_3; - // push accumulators into memory - Fr x_double_identity; - } - - 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); - - ep.x_2 = wire(p, WIRE.W_L_SHIFT); - ep.y_2 = wire(p, WIRE.W_4_SHIFT); - ep.y_3 = wire(p, WIRE.W_O_SHIFT); - ep.x_3 = wire(p, WIRE.W_R_SHIFT); - - Fr q_sign = wire(p, WIRE.Q_L); - Fr q_is_double = wire(p, WIRE.Q_M); - - // Contribution 10 point addition, x-coordinate check - // q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0 - Fr x_diff = (ep.x_2 - ep.x_1); - Fr y1_sqr = (ep.y_1 * ep.y_1); - { - // Move to top - Fr partialEval = domainSep; - - Fr y2_sqr = (ep.y_2 * ep.y_2); - Fr y1y2 = ep.y_1 * ep.y_2 * q_sign; - Fr x_add_identity = (ep.x_3 + ep.x_2 + ep.x_1); - x_add_identity = x_add_identity * x_diff * x_diff; - x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - - evals[10] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (Fr.wrap(1) - q_is_double); - } - - // Contribution 11 point addition, x-coordinate check - // q_elliptic * (q_sign * y1 + y3)(x2 - x1) + (x3 - x1)(y2 - q_sign * y1) = 0 - { - Fr y1_plus_y3 = ep.y_1 + ep.y_3; - Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; - evals[11] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (Fr.wrap(1) - q_is_double); - } - - // Contribution 10 point doubling, x-coordinate check - // (x3 + x1 + x1) (4y1*y1) - 9 * x1 * x1 * x1 * x1 = 0 - // N.B. we're using the equivalence x1*x1*x1 === y1*y1 - curve_b to reduce degree by 1 - { - Fr x_pow_4 = (y1_sqr + GRUMPKIN_CURVE_B_PARAMETER_NEGATED) * ep.x_1; - Fr y1_sqr_mul_4 = y1_sqr + y1_sqr; - y1_sqr_mul_4 = y1_sqr_mul_4 + y1_sqr_mul_4; - Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); - - // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; - evals[10] = evals[10] + acc; - } - - // Contribution 11 point doubling, y-coordinate check - // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 - { - Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); - evals[11] = evals[11] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; - } - } - - // Constants for the auxiliary relation - Fr constant LIMB_SIZE = Fr.wrap(uint256(1) << 68); - Fr constant SUBLIMB_SHIFT = Fr.wrap(uint256(1) << 14); - Fr constant MINUS_ONE = Fr.wrap(P - 1); - - // Parameters used within the Auxiliary Relation - // A struct is used to work around stack too deep. This relation has alot of variables - struct AuxParams { - Fr limb_subproduct; - Fr non_native_field_gate_1; - Fr non_native_field_gate_2; - Fr non_native_field_gate_3; - Fr limb_accumulator_1; - Fr limb_accumulator_2; - Fr memory_record_check; - Fr partial_record_check; - Fr next_gate_access_type; - Fr record_delta; - Fr index_delta; - Fr adjacent_values_match_if_adjacent_indices_match; - Fr adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation; - Fr access_check; - Fr next_gate_access_type_is_boolean; - Fr ROM_consistency_check_identity; - Fr RAM_consistency_check_identity; - Fr timestamp_delta; - Fr RAM_timestamp_check_identity; - Fr memory_identity; - Fr index_is_monotonically_increasing; - Fr auxiliary_identity; - } - - function accumulateAuxillaryRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Transcript memory tp, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { - AuxParams memory ap; - - /** - * Contribution 12 - * Non native field arithmetic gate 2 - * deg 4 - * - * _ _ - * / _ _ _ 14 \ - * q_2 . q_4 | (w_1 . w_2) + (w_1 . w_2) + (w_1 . w_4 + w_2 . w_3 - w_3) . 2 - w_3 - w_4 | - * \_ _/ - * - * - */ - ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = - (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); - - ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); - ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); - - ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); - - Fr non_native_field_identity = - ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; - non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); - - // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm - // deg 2 - ap.limb_accumulator_1 = wire(p, WIRE.W_R_SHIFT) * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L_SHIFT); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_O); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_R); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L); - ap.limb_accumulator_1 = ap.limb_accumulator_1 - wire(p, WIRE.W_4); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * wire(p, WIRE.Q_4); - - // ((((w3' * 2^14 + w2') * 2^14 + w1') * 2^14 + w4) * 2^14 + w3 - w4') * qm - // deg 2 - ap.limb_accumulator_2 = wire(p, WIRE.W_O_SHIFT) * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_R_SHIFT); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_L_SHIFT); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_4); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_O); - ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - - Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; - limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 - - /** - * MEMORY - * - * A RAM memory record contains a tuple of the following fields: - * * i: `index` of memory cell being accessed - * * t: `timestamp` of memory cell being accessed (used for RAM, set to 0 for ROM) - * * v: `value` of memory cell being accessed - * * a: `access` type of record. read: 0 = read, 1 = write - * * r: `record` of memory cell. record = access + index * eta + timestamp * eta_two + value * eta_three - * - * A ROM memory record contains a tuple of the following fields: - * * i: `index` of memory cell being accessed - * * v: `value1` of memory cell being accessed (ROM tables can store up to 2 values per index) - * * v2:`value2` of memory cell being accessed (ROM tables can store up to 2 values per index) - * * r: `record` of memory cell. record = index * eta + value2 * eta_two + value1 * eta_three - * - * When performing a read/write access, the values of i, t, v, v2, a, r are stored in the following wires + - * selectors, depending on whether the gate is a RAM read/write or a ROM read - * - * | gate type | i | v2/t | v | a | r | - * | --------- | -- | ----- | -- | -- | -- | - * | ROM | w1 | w2 | w3 | -- | w4 | - * | RAM | w1 | w2 | w3 | qc | w4 | - * - * (for accesses where `index` is a circuit constant, it is assumed the circuit will apply a copy constraint on - * `w2` to fix its value) - * - * - */ - - /** - * Memory Record Check - * Partial degree: 1 - * Total degree: 4 - * - * A ROM/ROM access gate can be evaluated with the identity: - * - * qc + w1 \eta + w2 \eta_two + w3 \eta_three - w4 = 0 - * - * For ROM gates, qc = 0 - */ - ap.memory_record_check = wire(p, WIRE.W_O) * tp.etaThree; - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * tp.etaTwo); - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * tp.eta); - ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); - ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 - ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); - - /** - * Contribution 13 & 14 - * ROM Consistency Check - * Partial degree: 1 - * Total degree: 4 - * - * For every ROM read, a set equivalence check is applied between the record witnesses, and a second set of - * records that are sorted. - * - * We apply the following checks for the sorted records: - * - * 1. w1, w2, w3 correctly map to 'index', 'v1, 'v2' for a given record value at w4 - * 2. index values for adjacent records are monotonically increasing - * 3. if, at gate i, index_i == index_{i + 1}, then value1_i == value1_{i + 1} and value2_i == value2_{i + 1} - * - */ - ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); - ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - - 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 - - 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 - - /** - * Contributions 15,16,17 - * RAM Consistency Check - * - * The 'access' type of the record is extracted with the expression `w_4 - ap.partial_record_check` - * (i.e. for an honest Prover `w1 * eta + w2 * eta^2 + w3 * eta^3 - w4 = access`. - * This is validated by requiring `access` to be boolean - * - * For two adjacent entries in the sorted list if _both_ - * A) index values match - * B) adjacent access value is 0 (i.e. next gate is a READ) - * then - * C) both values must match. - * The gate boolean check is - * (A && B) => C === !(A && B) || C === !A || !B || C - * - * N.B. it is the responsibility of the circuit writer to ensure that every RAM cell is initialized - * 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 - - // TODO(https://github.com/AztecProtocol/barretenberg/issues/757): If we sorted in - // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta - // 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 - - // 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 - // do with an arithmetic gate because of the `eta` factors. We need to check that the *next* gate's access - // type is correct, to cover this edge case - // deg 2 or 4 - ap.next_gate_access_type_is_boolean = - 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 - - ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_ARITH)); // deg 3 or 9 - - /** - * RAM Timestamp Consistency Check - * - * | w1 | w2 | w3 | w4 | - * | index | timestamp | timestamp_check | -- | - * - * Let delta_index = index_{i + 1} - index_{i} - * - * Iff delta_index == 0, timestamp_check = timestamp_{i + 1} - timestamp_i - * Else timestamp_check = 0 - */ - ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = - (ap.index_delta * MINUS_ONE + Fr.wrap(1)) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 - - /** - * Complete Contribution 12 - * The complete RAM/ROM memory identity - * Partial degree: - */ - 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 - - // (deg 3 or 9) + (deg 4) + (deg 3) - ap.auxiliary_identity = ap.memory_identity + non_native_field_identity + limb_accumulator_identity; - ap.auxiliary_identity = ap.auxiliary_identity * (wire(p, WIRE.Q_AUX) * domainSep); // deg 4 or 10 - evals[12] = ap.auxiliary_identity; - } - - function scaleAndBatchSubrelations( - Fr[NUMBER_OF_SUBRELATIONS] memory evaluations, - Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges - ) internal view returns (Fr accumulator) { - accumulator = accumulator + evaluations[0]; - - for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; - } - } - function verifyZeroMorph(Honk.Proof memory proof, Honk.VerificationKey memory vk, Transcript memory tp) internal view @@ -1183,12 +277,12 @@ contract EcdsaHonkVerifier is IVerifier { // f commitments are accumulated at (zm_x * r) cp.rho_pow = Fr.wrap(1); - for (uint256 i = 1; i < 34; ++i) { + for (uint256 i = 1; i <= NUMBER_UNSHIFTED; ++i) { scalars[i] = tp.zmX * cp.rho_pow; cp.rho_pow = cp.rho_pow * tp.rho; } // g commitments are accumulated at r - for (uint256 i = 34; i < 43; ++i) { + for (uint256 i = NUMBER_UNSHIFTED + 1; i <= NUMBER_OF_ENTITIES; ++i) { scalars[i] = cp.rho_pow; cp.rho_pow = cp.rho_pow * tp.rho; } @@ -1205,41 +299,43 @@ contract EcdsaHonkVerifier is IVerifier { commitments[9] = vk.qElliptic; commitments[10] = vk.qAux; commitments[11] = vk.qLookup; - commitments[12] = vk.s1; - commitments[13] = vk.s2; - commitments[14] = vk.s3; - commitments[15] = vk.s4; - commitments[16] = vk.id1; - commitments[17] = vk.id2; - commitments[18] = vk.id3; - commitments[19] = vk.id4; - commitments[20] = vk.t1; - commitments[21] = vk.t2; - commitments[22] = vk.t3; - commitments[23] = vk.t4; - commitments[24] = vk.lagrangeFirst; - commitments[25] = vk.lagrangeLast; + 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[26] = convertProofPoint(proof.w1); - commitments[27] = convertProofPoint(proof.w2); - commitments[28] = convertProofPoint(proof.w3); - commitments[29] = convertProofPoint(proof.w4); - commitments[30] = convertProofPoint(proof.zPerm); - commitments[31] = convertProofPoint(proof.lookupInverses); - commitments[32] = convertProofPoint(proof.lookupReadCounts); - commitments[33] = convertProofPoint(proof.lookupReadTags); + 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[34] = vk.t1; - commitments[35] = vk.t2; - commitments[36] = vk.t3; - commitments[37] = vk.t4; - commitments[38] = convertProofPoint(proof.w1); - commitments[39] = convertProofPoint(proof.w2); - commitments[40] = convertProofPoint(proof.w3); - commitments[41] = convertProofPoint(proof.w4); - commitments[42] = convertProofPoint(proof.zPerm); + 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); // Add scalar contributions // Add contributions: scalar * [q_k], k = 0,...,log_N, where @@ -1264,8 +360,8 @@ contract EcdsaHonkVerifier is IVerifier { cp.x_pow_2kp1 = cp.x_pow_2kp1 * cp.x_pow_2kp1; } - scalars[43 + k] = scalar; - commitments[43 + k] = convertProofPoint(proof.zmCqs[k]); + scalars[NUMBER_OF_ENTITIES + 1 + k] = scalar; + commitments[NUMBER_OF_ENTITIES + 1 + k] = convertProofPoint(proof.zmCqs[k]); } return batchMul2(commitments, scalars); @@ -1284,7 +380,7 @@ contract EcdsaHonkVerifier is IVerifier { let free := mload(0x40) // Write the original into the accumulator - // Load into memory for ecMUL, leave offset for eccAdd result + // Load into memory forecMUL, leave offset foreccAdd 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)))) @@ -1324,7 +420,7 @@ contract EcdsaHonkVerifier is IVerifier { let free := mload(0x40) // Write the original into the accumulator - // Load into memory for ecMUL, leave offset for eccAdd result + // Load into memory forecMUL, leave offset foreccAdd 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)))) diff --git a/barretenberg/sol/src/honk/keys/Add2HonkVerificationKey.sol b/barretenberg/sol/src/honk/keys/Add2HonkVerificationKey.sol index 1b9ba31c154..2402e0a4069 100644 --- a/barretenberg/sol/src/honk/keys/Add2HonkVerificationKey.sol +++ b/barretenberg/sol/src/honk/keys/Add2HonkVerificationKey.sol @@ -58,21 +58,29 @@ library Add2HonkVerificationKey { x: uint256(0x19e2d786ebad24caf1bef735441e58525a2f9b5807b2102f295c58cde00f5c97), y: uint256(0x085713ce7bac807a084a66904ebc6e695840e8cf405a6fd0c325f8bfcf7c2dd8) }), + qPoseidon2External: Honk.G1Point({ + x: uint256(0x0ca0bc4b1cd9eadbbf49eae56a99a4502ef13d965226a634d0981555e4a4da56), + y: uint256(0x1a8a818e6c61f68cefa329f2fabc95c80ad56a538d852f75eda858ed1a616c74) + }), + qPoseidon2Internal: Honk.G1Point({ + x: uint256(0x09dfd2992ac1708f0dd1d28c2ad910d9cf21a1510948580f406bc9416113d620), + y: uint256(0x205f76eebda12f565c98c775c4e4f3534b5dcc29e57eed899b1a1a880534dcb9) + }), s1: Honk.G1Point({ - x: uint256(0x039e4b13c22e227df79cafc6a8a9e8cc6217791d738ccad75c88d4a150cf9324), - y: uint256(0x16b832f3a75a8dffdb969bb79918867eaff198957c687d20ebc726dcbd61f9e1) + x: uint256(0x19a07402ffcc103c3d8fbfbc7e9a660147d7380e65c34f64b75701b8d4868c11), + y: uint256(0x0b7ab8c749a4af75d6100dba9246d7f993748b326d23791a595e21a17653fe30) }), s2: Honk.G1Point({ - x: uint256(0x165d5d53619ae0694e61c26302abfe39bc20646fd04210519520980f3ffb91d2), - y: uint256(0x10f0c4a0b216e66fe2f88ae617b54347fa41fce598ec801dd289394890f3b66b) + x: uint256(0x027234cb39eacbf2ebe98907cf433e429a37933e429d4f24df14274b5c4d2549), + y: uint256(0x2c1ea0996e3fd6cfabcfc6bbd4c86c65fb19c3dda2ded5c4f973af397e8e5c8b) }), s3: Honk.G1Point({ - x: uint256(0x0afca124a27006abfd194b1ad23b404f2112d11fe95ec69cc705f02258adc913), - y: uint256(0x2c1acafafadacc271d5297ab45bc79a5a3c87916578d8309493c2e2507bbe508) + x: uint256(0x243daee8a40861aba1ef660929ee9e874e52cd8e8d75f8c0245852369a731491), + y: uint256(0x0a20f23c0697fb0698478f7a861dde5e18bf5aa34f4731178e74f7460df49a88) }), s4: Honk.G1Point({ - x: uint256(0x0f7cf2c8c0cd0fe37630e8f79ba6dffd59c0fd1f2f6ae5789efab86d4c1c0007), - y: uint256(0x25bde264a751e99cf36bb64034710e55b3b1e02234c75a35683c91274d445ed8) + x: uint256(0x18b8202abb615440b5544d88092245911d2b5ff3b5a4a80bb15dbabafdfb56a7), + y: uint256(0x096a6685f36b1ca09a62820ae3be7538128093440fa943ea7412617a6d927916) }), t1: Honk.G1Point({ x: uint256(0x2e0cddbc5712d79b59cb3b41ebbcdd494997477ab161763e46601d95844837ef), diff --git a/barretenberg/sol/src/honk/keys/BlakeHonkVerificationKey.sol b/barretenberg/sol/src/honk/keys/BlakeHonkVerificationKey.sol index 49f2ae375c0..0eaeba194f1 100644 --- a/barretenberg/sol/src/honk/keys/BlakeHonkVerificationKey.sol +++ b/barretenberg/sol/src/honk/keys/BlakeHonkVerificationKey.sol @@ -58,21 +58,29 @@ library BlakeHonkVerificationKey { x: uint256(0x1375bbfbf5ed31b38460f46a43ac14e2cda93a3bc5cfd6e8a93cca356694a346), y: uint256(0x204c5173892c19a97a04b5f8419898063df5136489809ddb9f7eabb58d6858ab) }), + qPoseidon2External: Honk.G1Point({ + x: uint256(0x1fa8529236d7eacdab8dcd8169af30d334be103357577353e9ef08dfda841785), + y: uint256(0x055251b013746385e921b4620e55ef4f08b4d8afc4dbca7e6c3ca0f1b52c5a2b) + }), + qPoseidon2Internal: Honk.G1Point({ + x: uint256(0x1515283648ab8622ac6447f1fcf201a598d8df325279bfac9a6564924df97ee5), + y: uint256(0x0335bb595984ad38686009bca08f5f420e3b4cf888fad5af4a99eca08190a315) + }), s1: Honk.G1Point({ - x: uint256(0x07205fa20c0a8ff2aab958f4dfc4fa14449aad08f66de2341412a70ca6cb972b), - y: uint256(0x1341c9e5d1300703ae7ab1689c2d83a843ec898e5120a2b52b4323e2acf94f30) + x: uint256(0x26cec5ff3eb1b803c52fa1fefaac7a2be5cd13c1a1cc20bb9f22049c7f8597d2), + y: uint256(0x07e80e74eb0e06d7c0c9a3fbbdea4e86e5934faa8142625f175320778bcba65f) }), s2: Honk.G1Point({ - x: uint256(0x09821cc06874047f1204cf5c13d5d24951757e727368690715de0e8160f50eeb), - y: uint256(0x01675621d2a4202874b596d732f983f06524dbcc855a08dadd9925e29c02c7e3) + x: uint256(0x140b2faaf30cb5fc528621f4a395943e7fab8198dc734ac13253dd249682dd2a), + y: uint256(0x12709c4a13428f4704d284c90a81cc83280680185ae6298187e86debcd3e00f7) }), s3: Honk.G1Point({ - x: uint256(0x2567d6126bd4dfa15eecbda777e9c86566fc5817b87bdd479e558919cd5aa96b), - y: uint256(0x2ac31be3d7a2f45625b98c022106a076c0c4730299c18d48c7739d423e53bd94) + x: uint256(0x0aca5621e9f49279969497b3da0eb8a74c68c3513f4cf98e8b1d6f88567557a8), + y: uint256(0x2664811311f75057a16267bc0479eaeea2424156417cc4d3f8bd286fac9aa5d2) }), s4: Honk.G1Point({ - x: uint256(0x15d68f126e0adc08580957fad3db16973c5c7f47a64d04646204c2b14ec14482), - y: uint256(0x3048e354794e4dbcbb670dc137409427a954eb899bdba8bbaeec5ba7f3a79170) + x: uint256(0x04417c606a41393e73113ec3f834883dbeb302889199b888c0f5ea58a008ff98), + y: uint256(0x0865670de7962d29b6a9012f28ea52113c4e2b55d7de44e829edec87dba1d5c2) }), t1: Honk.G1Point({ x: uint256(0x1ec1b607634e31421b5047dc99d7674d6505fed978df0f42a3504f9771a8a7fa), diff --git a/barretenberg/sol/src/honk/keys/EcdsaHonkVerificationKey.sol b/barretenberg/sol/src/honk/keys/EcdsaHonkVerificationKey.sol index 1ef78325263..b2d9abdf2a2 100644 --- a/barretenberg/sol/src/honk/keys/EcdsaHonkVerificationKey.sol +++ b/barretenberg/sol/src/honk/keys/EcdsaHonkVerificationKey.sol @@ -58,21 +58,29 @@ library EcdsaHonkVerificationKey { x: uint256(0x13a5f6d8f4de0f66dc7ea0d75efa7ae6632e6448c13bbbe5358412f7a36518d6), y: uint256(0x142fd8f3223785fbd36b380c6065215d16b821b3df4d86d5464f1bfff2a29544) }), + qPoseidon2External: Honk.G1Point({ + x: uint256(0x02c909437bb59751312ce2208a2b367d3c9eaa8721d7671306c41ebd9843b3ba), + y: uint256(0x1db8a23e0231ac4b008ccdb6f21aa37c59349a77b51d894217596f0ef543120c) + }), + qPoseidon2Internal: Honk.G1Point({ + x: uint256(0x19d898bac51355e0822e2aa6e6630494e47ea2476a0c4c15b6f03ce441f6c6d0), + y: uint256(0x2add808f3d5b3c608ce5937fcd3c9c968ba56dbe5855e2f6d3e4bdd9d118d19b) + }), s1: Honk.G1Point({ - x: uint256(0x12f95fba378f68a39b900e458349c0f778dca65e2627bb1c5d6284c2b1260ef1), - y: uint256(0x15965dc624e9a40d1ffe0343f8fd4c2afdb6ac86e0f487ab8c4fe2a85d1036f4) + x: uint256(0x0dd1eea7735fc4052df5a19e4859c59e50e3ab9cb3cc2accbd42ef8a1104449b), + y: uint256(0x1541af79ad21fe21642a50d97899451c868b6d5d608431e5de6b0a730abe130d) }), s2: Honk.G1Point({ - x: uint256(0x1c260f913ab6417444cf9512dc273201aa34946e41d75fa81c3626c81fe716ff), - y: uint256(0x176e7818b3dfff8b2744f5b5d2c0a2ebdafcd9286c795270092dfbe25a9c08af) + x: uint256(0x21d9072c3474c1cfe1c2d96c098c4d9af4bb5d222944aa6470063f4a8b9b9770), + y: uint256(0x137ad8c018449f48311b5394ac91a6b2f5c5e40c676216a299a3d501d69b1f7d) }), s3: Honk.G1Point({ - x: uint256(0x16945d3d1dc53de6aeaff189e40c72c9de0ac1dd796345064c0af5e092e8e3f1), - y: uint256(0x21f48c50caca803009a007bc66421e7135ece814e02767a43cb5a7ec60b4e7d8) + x: uint256(0x2c2fe61ccbf18af13d41950ef58f3a2a64d355657a4dfba8e9917e618ea8add4), + y: uint256(0x2e7edf4dae50db17925e431d3198a39cb4bdc6f4e6e7d8d6163c972f4750a606) }), s4: Honk.G1Point({ - x: uint256(0x187a3c8a8fa68d820cdcbdda2aec2fd6af5495bce3ec51a86a9043ed828cac12), - y: uint256(0x08d15b3ae7be0b34eac0495b771c58ec10b386e4e614301b4b00e2b0e360f8e0) + x: uint256(0x1825a30f42c7508e2ee2158d374dc626cf4149b745ba55d533181f418ac605aa), + y: uint256(0x15d9b33a9612c0c8a55a75a827c0230656054765c7b37ba77a798b71a4766d1b) }), t1: Honk.G1Point({ x: uint256(0x1ddc9ef86584375e5998d9f6fc16a4e646dc315ab86b477abc2f18a723dc24f6), diff --git a/barretenberg/sol/src/honk/utils.sol b/barretenberg/sol/src/honk/utils.sol index 340204c20bc..15d5e9b7b67 100644 --- a/barretenberg/sol/src/honk/utils.sol +++ b/barretenberg/sol/src/honk/utils.sol @@ -1,6 +1,10 @@ -import {Honk, P, Q} from "./HonkTypes.sol"; +pragma solidity >=0.8.21; + +import {Honk} from "./HonkTypes.sol"; import {Fr, FrLib} from "./Fr.sol"; +uint256 constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; // EC group order. F_q + import "forge-std/console.sol"; import "forge-std/console2.sol"; diff --git a/barretenberg/sol/test/base/TestBase.sol b/barretenberg/sol/test/base/TestBase.sol index ecf458e28a3..9b111bfd7bd 100644 --- a/barretenberg/sol/test/base/TestBase.sol +++ b/barretenberg/sol/test/base/TestBase.sol @@ -22,6 +22,7 @@ contract TestBase is Test { let rLoc := add(rawBytes, 0x24) let end := add(rLoc, length) + // TODO: Can be updated with new evm mcpy opcode for {} lt(rLoc, end) { wLoc := add(wLoc, 0x20) rLoc := add(rLoc, 0x20)