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