diff --git a/barretenberg/acir_tests/sol-test/src/index.js b/barretenberg/acir_tests/sol-test/src/index.js index ca402b06182..e8ceb1dc260 100644 --- a/barretenberg/acir_tests/sol-test/src/index.js +++ b/barretenberg/acir_tests/sol-test/src/index.js @@ -8,6 +8,10 @@ import solc from "solc"; const NUMBER_OF_FIELDS_IN_PLONK_PROOF = 93; const NUMBER_OF_FIELDS_IN_HONK_PROOF = 443; +const WRONG_PUBLIC_INPUTS_LENGTH = "0xfa066593"; +const SUMCHECK_FAILED = "0x9fc3a218"; +const SHPLEMINI_FAILED = "0xa5d82e8a"; + // 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 @@ -94,7 +98,7 @@ if (!testingHonk) { } var output = JSON.parse(solc.compile(JSON.stringify(compilationInput))); -if (output.errors.some((e) => e.type == "Error")) { +if (output.errors.some((e) => e.severity == "error")) { throw new Error(JSON.stringify(output.errors, null, 2)); } const contract = output.contracts["Test.sol"]["Test"]; @@ -236,8 +240,20 @@ try { const result = await contract.test(proofStr, publicInputs); if (!result) throw new Error("Test failed"); } catch (e) { - console.error(testName, " failed"); - console.log(e); + console.error(testName, "failed"); + if (testingHonk) { + var errorType = e.data; + switch (errorType) { + case WRONG_PUBLIC_INPUTS_LENGTH: + throw new Error("Number of inputs in the proof is wrong"); + case SUMCHECK_FAILED: + throw new Error("Sumcheck round failed"); + case SHPLEMINI_FAILED: + throw new Error("PCS round failed"); + default: + throw e; + } + } throw e; } finally { // Kill anvil at the end of running 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 98c352bcb32..45b72b21ba6 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp @@ -254,6 +254,18 @@ library Honk { G1Point lagrangeLast; } + struct RelationParameters { + // challenges + Fr eta; + Fr etaTwo; + Fr etaThree; + Fr beta; + Fr gamma; + // derived + Fr publicInputsDelta; + } + + struct Proof { uint256 circuitSize; uint256 publicInputsSize; @@ -280,24 +292,20 @@ library Honk { } } - // Transcript library to generate fiat shamir challenges struct Transcript { - Fr eta; - Fr etaTwo; - Fr etaThree; - Fr beta; - Fr gamma; + // Oink + Honk.RelationParameters relationParameters; Fr[NUMBER_OF_ALPHAS] alphas; Fr[CONST_PROOF_SIZE_LOG_N] gateChallenges; + // Sumcheck Fr[CONST_PROOF_SIZE_LOG_N] sumCheckUChallenges; - // Shplemini + // Gemini Fr rho; Fr geminiR; + // Shplonk Fr shplonkNu; Fr shplonkZ; - // Derived - Fr publicInputsDelta; } library TranscriptLib { @@ -307,9 +315,9 @@ library TranscriptLib { returns (Transcript memory t) { Fr previousChallenge; - (t.eta, t.etaTwo, t.etaThree, previousChallenge) = generateEtaChallenge(proof, publicInputs, publicInputsSize); + (t.relationParameters, previousChallenge) = + generateRelationParametersChallenges(proof, publicInputs, publicInputsSize, previousChallenge); - (t.beta, t.gamma, previousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); @@ -336,6 +344,19 @@ library TranscriptLib { second = FrLib.fromBytes32(bytes32(hi)); } + function generateRelationParametersChallenges( + Honk.Proof memory proof, + bytes32[] calldata publicInputs, + uint256 publicInputsSize, + Fr previousChallenge + ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { + (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = + generateEtaChallenge(proof, publicInputs, publicInputsSize); + + (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); + + } + function generateEtaChallenge(Honk.Proof memory proof, bytes32[] calldata publicInputs, uint256 publicInputsSize) internal pure @@ -531,152 +552,83 @@ library TranscriptLib { (shplonkZ, unused) = splitChallenge(nextPreviousChallenge); } - function loadProof(bytes calldata proof) internal pure returns (Honk.Proof memory) { - Honk.Proof memory p; - + function loadProof(bytes calldata proof) internal pure returns (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])) - }); + p.w1 = bytesToG1ProofPoint(proof[0x60:0xe0]); + + p.w2 = bytesToG1ProofPoint(proof[0xe0:0x160]); + p.w3 = bytesToG1ProofPoint(proof[0x160:0x1e0]); // Lookup / Permutation Helper Commitments - p.lookupReadCounts = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[0x1e0:0x200])), - x_1: uint256(bytes32(proof[0x200:0x220])), - y_0: uint256(bytes32(proof[0x220:0x240])), - y_1: uint256(bytes32(proof[0x240:0x260])) - }); - p.lookupReadTags = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[0x260:0x280])), - x_1: uint256(bytes32(proof[0x280:0x2a0])), - y_0: uint256(bytes32(proof[0x2a0:0x2c0])), - y_1: uint256(bytes32(proof[0x2c0:0x2e0])) - }); - p.w4 = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[0x2e0:0x300])), - x_1: uint256(bytes32(proof[0x300:0x320])), - y_0: uint256(bytes32(proof[0x320:0x340])), - y_1: uint256(bytes32(proof[0x340:0x360])) - }); - p.lookupInverses = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[0x360:0x380])), - x_1: uint256(bytes32(proof[0x380:0x3a0])), - y_0: uint256(bytes32(proof[0x3a0:0x3c0])), - y_1: uint256(bytes32(proof[0x3c0:0x3e0])) - }); - p.zPerm = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[0x3e0:0x400])), - x_1: uint256(bytes32(proof[0x400:0x420])), - y_0: uint256(bytes32(proof[0x420:0x440])), - y_1: uint256(bytes32(proof[0x440:0x460])) - }); - - // Boundary represents a pointer to the head of the unread part of the proof + p.lookupReadCounts = bytesToG1ProofPoint(proof[0x1e0:0x260]); + p.lookupReadTags = bytesToG1ProofPoint(proof[0x260:0x2e0]); + p.w4 = bytesToG1ProofPoint(proof[0x2e0:0x360]); + p.lookupInverses = bytesToG1ProofPoint(proof[0x360:0x3e0]); + p.zPerm = bytesToG1ProofPoint(proof[0x3e0:0x460]); 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])); + p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + 0x20]); + boundary += 0x20; } } - - 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])); + p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + 0x20]); + boundary += 0x20; } - boundary = boundary + (NUMBER_OF_ENTITIES * 0x20); - // Gemini // Read gemini fold univariates for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N - 1; i++) { - uint256 xStart = boundary + (i * 0x80); - uint256 xEnd = xStart + 0x20; - - uint256 x1Start = xEnd; - uint256 x1End = x1Start + 0x20; - - uint256 yStart = x1End; - uint256 yEnd = yStart + 0x20; - - uint256 y1Start = yEnd; - uint256 y1End = y1Start + 0x20; - p.geminiFoldComms[i] = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[xStart:xEnd])), - x_1: uint256(bytes32(proof[x1Start:x1End])), - y_0: uint256(bytes32(proof[yStart:yEnd])), - y_1: uint256(bytes32(proof[y1Start:y1End])) - }); + p.geminiFoldComms[i] = bytesToG1ProofPoint(proof[boundary:boundary + 0x80]); + boundary += 0x80; } - boundary = boundary + ((CONST_PROOF_SIZE_LOG_N - 1) * 0x80); - // Read gemini a evaluations for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { - uint256 start = boundary + (i * 0x20); - uint256 end = start + 0x20; - p.geminiAEvaluations[i] = FrLib.fromBytes32(bytes32(proof[start:end])); + p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + 0x20]); + boundary += 0x20; } - boundary = boundary + (CONST_PROOF_SIZE_LOG_N * 0x20); - // Shplonk - p.shplonkQ = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[boundary:boundary + 0x20])), - x_1: uint256(bytes32(proof[boundary + 0x20:boundary + 0x40])), - y_0: uint256(bytes32(proof[boundary + 0x40:boundary + 0x60])), - y_1: uint256(bytes32(proof[boundary + 0x60:boundary + 0x80])) - }); - + p.shplonkQ = bytesToG1ProofPoint(proof[boundary:boundary + 0x80]); boundary = boundary + 0x80; - // KZG - p.kzgQuotient = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[boundary:boundary + 0x20])), - x_1: uint256(bytes32(proof[boundary + 0x20:boundary + 0x40])), - y_0: uint256(bytes32(proof[boundary + 0x40:boundary + 0x60])), - y_1: uint256(bytes32(proof[boundary + 0x60:boundary + 0x80])) - }); - - return p; + p.kzgQuotient = bytesToG1ProofPoint(proof[boundary:boundary + 0x80]); } } + +// Fr utility + +function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { + require(proofSection.length == 0x20, "invalid bytes scalar"); + scalar = FrLib.fromBytes32(bytes32(proofSection)); +} + // EC Point utilities function convertProofPoint(Honk.G1ProofPoint memory input) pure returns (Honk.G1Point memory) { return Honk.G1Point({x: input.x_0 | (input.x_1 << 136), y: input.y_0 | (input.y_1 << 136)}); } +function bytesToG1ProofPoint(bytes calldata proofSection) pure returns (Honk.G1ProofPoint memory point) { + require(proofSection.length == 0x80, "invalid bytes point"); + point = Honk.G1ProofPoint({ + x_0: uint256(bytes32(proofSection[0x00:0x20])), + x_1: uint256(bytes32(proofSection[0x20:0x40])), + y_0: uint256(bytes32(proofSection[0x40:0x60])), + y_1: uint256(bytes32(proofSection[0x60:0x80])) + }); +} + function ecMul(Honk.G1Point memory point, Fr scalar) view returns (Honk.G1Point memory) { bytes memory input = abi.encodePacked(point.x, point.y, Fr.unwrap(scalar)); (bool success, bytes memory result) = address(0x07).staticcall(input); @@ -715,25 +667,26 @@ function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point mem 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 - pure - returns (Fr accumulator) - { + function accumulateRelationEvaluations( + Honk.Proof memory proof, + Honk.RelationParameters memory rp, + Fr[NUMBER_OF_ALPHAS] memory alphas, + Fr powPartialEval + ) internal pure 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); + accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateAuxillaryRelation(purportedEvaluations, tp, evaluations, powPartialEval); + accumulateAuxillaryRelation(purportedEvaluations, rp, evaluations, powPartialEval); accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); // batch the subrelations with the alpha challenges to obtain the full honk relation - accumulator = scaleAndBatchSubrelations(evaluations, tp.alphas); + accumulator = scaleAndBatchSubrelations(evaluations, alphas); } /** @@ -782,7 +735,7 @@ library RelationsLib { function accumulatePermutationRelation( Fr[NUMBER_OF_ENTITIES] memory p, - Transcript memory tp, + Honk.RelationParameters memory rp, Fr[NUMBER_OF_SUBRELATIONS] memory evals, Fr domainSep ) internal pure { @@ -790,18 +743,18 @@ library RelationsLib { 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); + Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; + num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.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); + Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; + den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); grand_product_denominator = den; } @@ -812,7 +765,7 @@ library RelationsLib { acc = acc - ( - (wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * tp.publicInputsDelta)) + (wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * grand_product_denominator ); acc = acc * domainSep; @@ -828,7 +781,7 @@ library RelationsLib { function accumulateLogDerivativeLookupRelation( Fr[NUMBER_OF_ENTITIES] memory p, - Transcript memory tp, + Honk.RelationParameters memory rp, Fr[NUMBER_OF_SUBRELATIONS] memory evals, Fr domainSep ) internal pure { @@ -837,18 +790,18 @@ library RelationsLib { // 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) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.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_1 = wire(p, WIRE.W_L) + rp.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); + read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; @@ -1041,7 +994,7 @@ library RelationsLib { function accumulateAuxillaryRelation( Fr[NUMBER_OF_ENTITIES] memory p, - Transcript memory tp, + Honk.RelationParameters memory rp, Fr[NUMBER_OF_SUBRELATIONS] memory evals, Fr domainSep ) internal pure { @@ -1152,9 +1105,9 @@ library RelationsLib { * * 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 = wire(p, WIRE.W_O) * rp.etaThree; + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.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); @@ -1211,9 +1164,9 @@ library RelationsLib { Fr access_type = (wire(p, WIRE.W_4) - ap.partial_record_check); // will be 0 or 1 for honest Prover; deg 1 or 4 ap.access_check = access_type * access_type - access_type; // check value is 0 or 1; deg 2 or 8 - ap.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_O_SHIFT) * rp.etaThree; + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.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); @@ -1434,9 +1387,10 @@ abstract contract BaseHonkVerifier is IVerifier { // Generate the fiat shamir challenges for the whole protocol Transcript memory t = TranscriptLib.generateTranscript(p, publicInputs, vk.publicInputsSize); - // Compute the public input delta - t.publicInputsDelta = - computePublicInputDelta(publicInputs, t.beta, t.gamma, vk.circuitSize, p.publicInputsOffset); + // Derive public input delta + t.relationParameters.publicInputsDelta = computePublicInputDelta( + publicInputs, t.relationParameters.beta, t.relationParameters.gamma, p.publicInputsOffset + ); // Sumcheck bool sumcheckVerified = verifySumcheck(p, t); @@ -1448,17 +1402,15 @@ abstract contract BaseHonkVerifier is IVerifier { return sumcheckVerified && shpleminiVerified; // Boolean condition not required - nice for vanity :) } - function computePublicInputDelta( - bytes32[] memory publicInputs, - Fr beta, - Fr gamma, - uint256 domainSize, - uint256 offset - ) internal view returns (Fr publicInputDelta) { + function computePublicInputDelta(bytes32[] memory publicInputs, Fr beta, Fr gamma, 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 numeratorAcc = gamma + (beta * FrLib.from(N + offset)); Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); { @@ -1497,7 +1449,8 @@ abstract contract BaseHonkVerifier is IVerifier { } // Last round - Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations(proof, tp, powPartialEvaluation); + Fr grandHonkRelationSum = + RelationsLib.accumulateRelationEvaluations(proof, tp.relationParameters, tp.alphas, powPartialEvaluation); verified = (grandHonkRelationSum == roundTarget); } diff --git a/barretenberg/sol/src/honk/BaseHonkVerifier.sol b/barretenberg/sol/src/honk/BaseHonkVerifier.sol index 5c38218a041..8d63c71aa8d 100644 --- a/barretenberg/sol/src/honk/BaseHonkVerifier.sol +++ b/barretenberg/sol/src/honk/BaseHonkVerifier.sol @@ -54,8 +54,10 @@ abstract contract BaseHonkVerifier is IVerifier { // Generate the fiat shamir challenges for the whole protocol Transcript memory t = TranscriptLib.generateTranscript(p, publicInputs, numPublicInputs); - // Compute the public input delta - t.publicInputsDelta = computePublicInputDelta(publicInputs, t.beta, t.gamma, p.publicInputsOffset); + // Derive public input delta + t.relationParameters.publicInputsDelta = computePublicInputDelta( + publicInputs, t.relationParameters.beta, t.relationParameters.gamma, p.publicInputsOffset + ); // Sumcheck bool sumcheckVerified = verifySumcheck(p, t); @@ -114,7 +116,8 @@ abstract contract BaseHonkVerifier is IVerifier { } // Last round - Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations(proof, tp, powPartialEvaluation); + Fr grandHonkRelationSum = + RelationsLib.accumulateRelationEvaluations(proof, tp.relationParameters, tp.alphas, powPartialEvaluation); verified = (grandHonkRelationSum == roundTarget); } diff --git a/barretenberg/sol/src/honk/HonkTypes.sol b/barretenberg/sol/src/honk/HonkTypes.sol index bedb2a28a44..42a0ea36ccf 100644 --- a/barretenberg/sol/src/honk/HonkTypes.sol +++ b/barretenberg/sol/src/honk/HonkTypes.sol @@ -111,6 +111,17 @@ library Honk { G1Point lagrangeLast; } + struct RelationParameters { + // challenges + Fr eta; + Fr etaTwo; + Fr etaThree; + Fr beta; + Fr gamma; + // derived + Fr publicInputsDelta; + } + struct Proof { uint256 circuitSize; uint256 publicInputsSize; diff --git a/barretenberg/sol/src/honk/Relations.sol b/barretenberg/sol/src/honk/Relations.sol index 251dbe8245a..23eab4ba5e2 100644 --- a/barretenberg/sol/src/honk/Relations.sol +++ b/barretenberg/sol/src/honk/Relations.sol @@ -17,30 +17,30 @@ 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 - pure - returns (Fr accumulator) - { + function accumulateRelationEvaluations( + Honk.Proof memory proof, + Honk.RelationParameters memory rp, + Fr[NUMBER_OF_ALPHAS] memory alphas, + Fr powPartialEval + ) internal pure 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); + accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateAuxillaryRelation(purportedEvaluations, tp, evaluations, powPartialEval); + accumulateAuxillaryRelation(purportedEvaluations, rp, evaluations, powPartialEval); accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); // batch the subrelations with the alpha challenges to obtain the full honk relation - accumulator = scaleAndBatchSubrelations(evaluations, tp.alphas); + accumulator = scaleAndBatchSubrelations(evaluations, alphas); } /** @@ -90,7 +90,7 @@ library RelationsLib { function accumulatePermutationRelation( Fr[NUMBER_OF_ENTITIES] memory p, - Transcript memory tp, + Honk.RelationParameters memory rp, Fr[NUMBER_OF_SUBRELATIONS] memory evals, Fr domainSep ) internal pure { @@ -98,18 +98,18 @@ library RelationsLib { 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); + Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; + num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.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); + Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; + den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); grand_product_denominator = den; } @@ -120,7 +120,7 @@ library RelationsLib { acc = acc - ( - (wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * tp.publicInputsDelta)) + (wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * grand_product_denominator ); acc = acc * domainSep; @@ -136,7 +136,7 @@ library RelationsLib { function accumulateLogDerivativeLookupRelation( Fr[NUMBER_OF_ENTITIES] memory p, - Transcript memory tp, + Honk.RelationParameters memory rp, Fr[NUMBER_OF_SUBRELATIONS] memory evals, Fr domainSep ) internal pure { @@ -145,18 +145,18 @@ library RelationsLib { // 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) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.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_1 = wire(p, WIRE.W_L) + rp.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); + read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; @@ -349,7 +349,7 @@ library RelationsLib { function accumulateAuxillaryRelation( Fr[NUMBER_OF_ENTITIES] memory p, - Transcript memory tp, // sooo we take the relation parameters, if needed, from tramscript + Honk.RelationParameters memory rp, // 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 { @@ -460,9 +460,9 @@ library RelationsLib { * * 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 = wire(p, WIRE.W_O) * rp.etaThree; + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.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); @@ -522,9 +522,9 @@ library RelationsLib { // 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_O_SHIFT) * rp.etaThree; + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.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); diff --git a/barretenberg/sol/src/honk/Transcript.sol b/barretenberg/sol/src/honk/Transcript.sol index ca4e1449716..7cd47a60bd9 100644 --- a/barretenberg/sol/src/honk/Transcript.sol +++ b/barretenberg/sol/src/honk/Transcript.sol @@ -8,24 +8,22 @@ import { CONST_PROOF_SIZE_LOG_N } from "./HonkTypes.sol"; import {Fr, FrLib} from "./Fr.sol"; +import {bytesToG1ProofPoint, bytesToFr} from "./utils.sol"; // Transcript library to generate fiat shamir challenges struct Transcript { - Fr eta; - Fr etaTwo; - Fr etaThree; - Fr beta; - Fr gamma; + // Oink + Honk.RelationParameters relationParameters; Fr[NUMBER_OF_ALPHAS] alphas; Fr[CONST_PROOF_SIZE_LOG_N] gateChallenges; + // Sumcheck Fr[CONST_PROOF_SIZE_LOG_N] sumCheckUChallenges; // Gemini Fr rho; Fr geminiR; + // Shplonk Fr shplonkNu; Fr shplonkZ; - // Derived - Fr publicInputsDelta; } library TranscriptLib { @@ -35,9 +33,8 @@ library TranscriptLib { returns (Transcript memory t) { Fr previousChallenge; - (t.eta, t.etaTwo, t.etaThree, previousChallenge) = generateEtaChallenge(proof, publicInputs, publicInputsSize); - - (t.beta, t.gamma, previousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); + (t.relationParameters, previousChallenge) = + generateRelationParametersChallenges(proof, publicInputs, publicInputsSize, previousChallenge); (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); @@ -64,6 +61,18 @@ library TranscriptLib { second = FrLib.fromBytes32(bytes32(hi)); } + function generateRelationParametersChallenges( + Honk.Proof memory proof, + bytes32[] calldata publicInputs, + uint256 publicInputsSize, + Fr previousChallenge + ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { + (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = + generateEtaChallenge(proof, publicInputs, publicInputsSize); + + (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); + } + function generateEtaChallenge(Honk.Proof memory proof, bytes32[] calldata publicInputs, uint256 publicInputsSize) internal pure @@ -265,145 +274,57 @@ library TranscriptLib { // 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 pure returns (Honk.Proof memory) { - Honk.Proof memory p; - + function loadProof(bytes calldata proof) internal pure returns (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])) - }); + p.w1 = bytesToG1ProofPoint(proof[0x60:0xe0]); - // 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])) - }); + p.w2 = bytesToG1ProofPoint(proof[0xe0:0x160]); + p.w3 = bytesToG1ProofPoint(proof[0x160:0x1e0]); + // Lookup / Permutation Helper Commitments + p.lookupReadCounts = bytesToG1ProofPoint(proof[0x1e0:0x260]); + p.lookupReadTags = bytesToG1ProofPoint(proof[0x260:0x2e0]); + p.w4 = bytesToG1ProofPoint(proof[0x2e0:0x360]); + p.lookupInverses = bytesToG1ProofPoint(proof[0x360:0x3e0]); + p.zPerm = bytesToG1ProofPoint(proof[0x3e0: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])); + p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + 0x20]); + boundary += 0x20; } } - - 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])); + p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + 0x20]); + boundary += 0x20; } - boundary = boundary + (NUMBER_OF_ENTITIES * 0x20); - // Gemini // Read gemini fold univariates for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N - 1; i++) { - uint256 xStart = boundary + (i * 0x80); - uint256 xEnd = xStart + 0x20; - - uint256 x1Start = xEnd; - uint256 x1End = x1Start + 0x20; - - uint256 yStart = x1End; - uint256 yEnd = yStart + 0x20; - - uint256 y1Start = yEnd; - uint256 y1End = y1Start + 0x20; - p.geminiFoldComms[i] = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[xStart:xEnd])), - x_1: uint256(bytes32(proof[x1Start:x1End])), - y_0: uint256(bytes32(proof[yStart:yEnd])), - y_1: uint256(bytes32(proof[y1Start:y1End])) - }); + p.geminiFoldComms[i] = bytesToG1ProofPoint(proof[boundary:boundary + 0x80]); + boundary += 0x80; } - boundary = boundary + ((CONST_PROOF_SIZE_LOG_N - 1) * 0x80); - // Read gemini a evaluations for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { - uint256 start = boundary + (i * 0x20); - uint256 end = start + 0x20; - p.geminiAEvaluations[i] = FrLib.fromBytes32(bytes32(proof[start:end])); + p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + 0x20]); + boundary += 0x20; } - boundary = boundary + (CONST_PROOF_SIZE_LOG_N * 0x20); - // Shplonk - p.shplonkQ = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[boundary:boundary + 0x20])), - x_1: uint256(bytes32(proof[boundary + 0x20:boundary + 0x40])), - y_0: uint256(bytes32(proof[boundary + 0x40:boundary + 0x60])), - y_1: uint256(bytes32(proof[boundary + 0x60:boundary + 0x80])) - }); - + p.shplonkQ = bytesToG1ProofPoint(proof[boundary:boundary + 0x80]); boundary = boundary + 0x80; - // KZG - p.kzgQuotient = Honk.G1ProofPoint({ - x_0: uint256(bytes32(proof[boundary:boundary + 0x20])), - x_1: uint256(bytes32(proof[boundary + 0x20:boundary + 0x40])), - y_0: uint256(bytes32(proof[boundary + 0x40:boundary + 0x60])), - y_1: uint256(bytes32(proof[boundary + 0x60:boundary + 0x80])) - }); - - return p; + p.kzgQuotient = bytesToG1ProofPoint(proof[boundary:boundary + 0x80]); } } diff --git a/barretenberg/sol/src/honk/utils.sol b/barretenberg/sol/src/honk/utils.sol index 55c0e73806e..33710be7e15 100644 --- a/barretenberg/sol/src/honk/utils.sol +++ b/barretenberg/sol/src/honk/utils.sol @@ -70,12 +70,29 @@ function logFr(string memory name, uint256 i, Fr value) pure { console2.log(name, i, as_hex); } +// Fr utility + +function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { + require(proofSection.length == 0x20, "invalid number of bytes to construct Fr scalar"); + scalar = FrLib.fromBytes32(bytes32(proofSection)); +} + // EC Point utilities function convertProofPoint(Honk.G1ProofPoint memory input) pure returns (Honk.G1Point memory) { return Honk.G1Point({x: input.x_0 | (input.x_1 << 136), y: input.y_0 | (input.y_1 << 136)}); } +function bytesToG1ProofPoint(bytes calldata proofSection) pure returns (Honk.G1ProofPoint memory point) { + require(proofSection.length == 0x80, "invalid number of bytes to construct a G1 point"); + point = Honk.G1ProofPoint({ + x_0: uint256(bytes32(proofSection[0x00:0x20])), + x_1: uint256(bytes32(proofSection[0x20:0x40])), + y_0: uint256(bytes32(proofSection[0x40:0x60])), + y_1: uint256(bytes32(proofSection[0x60:0x80])) + }); +} + function ecMul(Honk.G1Point memory point, Fr scalar) view returns (Honk.G1Point memory) { bytes memory input = abi.encodePacked(point.x, point.y, Fr.unwrap(scalar)); (bool success, bytes memory result) = address(0x07).staticcall(input);